A. java中實現同步的兩種方式syschronized和lock的區別和聯系
Lock是java.util.concurrent.locks包下的介面,Lock實現提供了比使用synchronized方法和語句可獲得的更廣泛的鎖定操作,它能以更優雅的方式處理線程同步問題,我們拿Java線程(二)中的一個例子簡單的實現一下和sychronized一樣的效果,代碼如下:
[java]view plain
Thread-4准備讀取數據
Thread-3准備讀取數據
Thread-5准備讀取數據
Thread-5讀取18
Thread-4讀取18
Thread-3讀取18
Thread-2准備寫入數據
Thread-2寫入6
Thread-2准備寫入數據
Thread-2寫入10
Thread-1准備寫入數據
Thread-1寫入22
Thread-5准備讀取數據
從結果可以看出實現了我們的需求,這只是鎖的基本用法,鎖的機制還需要繼續深入學習。
本文來自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/7461369,轉載請註明。
在java中有兩種方式實現原子性操作(即同步操作):
1)使用同步關鍵字synchronized
2)使用lock鎖機制其中也包括相應的讀寫鎖
package com.xiaohao.test;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Test {
public static void main(String[] args) {
final LockTest lock=new LockTest();
//輸出張三
new Thread(){
public void run(){
lock.test("張三張三張三張三張三張三張三張三張三張三");
}
}.start();
//輸出李四
new Thread(){
public void run(){
lock.test("李四李四李四李四李四李四李四李四李四李四");System.out.println
("
---------------------------------------------------------------");
}
}.start();
//---------------------------------------------------------------
//模擬寫入數據的
for (int i = 0; i < 3; i++) {
new Thread(){
public void run() {
for (int j = 0; j < 5; j++) {
// lock.set(new Random().nextInt(30));
lock.set2(new Random().nextInt(30));
}
}
}.start();
}
//模擬讀取數據的
for (int i = 0; i < 3; i++) {
new Thread(){
public void run() {
for (int j = 0; j < 5; j++) {
// lock.get();
lock.get2();
}
}
}.start();
}
}
}
class LockTest{
private Lock lock=new ReentrantLock(); //創建普通的鎖
private ReadWriteLock readWriteLock=new ReentrantReadWriteLock();//創建讀寫鎖
private int data;// 共享數據
//實現同步的方法一 使用同步關鍵字 synchronized
public synchronized void test(String name){
//下面的相關操作是一個原子性的操作
// lock.lock();// 得到鎖
try {
for(int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
} finally {
// lock.unlock();// 釋放鎖
}
}
//實現同步的方法二 使用lock鎖機制
public void test2(String name){
//下面的相關操作是一個原子性的操作
lock.lock();// 得到鎖
try {
for(int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
} finally {
lock.unlock();// 釋放鎖
}
}
//使用set方法模擬寫入數據
//使用 synchronized 實現了讀讀,寫寫,讀寫之間的互斥 ,但讀讀之間的互斥是沒有什麼必要的
public synchronized void set(int data){
System.out.println(Thread.currentThread().getName() + "准備寫入數據");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.data = data;
System.out.println(Thread.currentThread().getName() + "寫入" + this.data);
}
//使用get方法模擬讀取數據
//使用 synchronized 實現了讀讀,寫寫,讀寫之間的互斥 ,但讀讀之間的互斥是沒有什麼必要的
public synchronized void get() {
System.out.println(Thread.currentThread().getName() + "准備讀取數據");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "讀取" + this.data);
}
//使用set方法模擬寫入數據
//使用 讀寫鎖實現了寫寫,讀寫之間的互斥 ,但讀讀之間的互斥是沒有什麼必要的
public void set2(int data){
readWriteLock.writeLock().lock();//獲取寫入鎖
try{
System.out.println(Thread.currentThread().getName() + "准備寫入數據");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.data = data;
System.out.println(Thread.currentThread().getName() + "寫入" + this.data);
}
finally{
readWriteLock.writeLock().unlock();
}
}
//使用get方法模擬讀取數據
//使用 讀寫鎖實現了寫寫,讀寫之間的互斥 ,但讀讀之間的互斥是沒有什麼必要的
public void get2() {
//獲取相應的讀鎖
readWriteLock.readLock().lock();
try{
System.out.println(Thread.currentThread().getName() + "准備讀取數據");
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "讀取" + this.data);
}
finally{
// 釋放相應的寫鎖
readWriteLock.readLock().unlock();
}
}
}
線程同步經典版:
package com.xiaohao.test;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Test2{
public static void main(String[] args){
final LockTest2 lockTest=new LockTest2();
for(int i=0;i<3;i++) {
new Thread(){
public void run(){
try {
for (int j = 0; j < 3; j++) {
lockTest.setValue();
} } catch (InterruptedException e) {
// TODO Auto-generated catch block e.printStackTrace();
}
}
}.start();
}
for(int i=0;i<3;i++) {
new Thread(){
public void run(){
try {
for (int j = 0; j < 3; j++) {
lockTest.getValue();
}
} catch (InterruptedException e)
{ // TODO Auto-generated catch block e.printStackTrace(); }
}
}.start();
}
}
}
class LockTest2 {
int data=0;
ReentrantReadWriteLock lock= new ReentrantReadWriteLock();// 鎖對象
public void setValue() throws InterruptedException{
lock.writeLock().lock();
System.out.println("正在使用寫鎖......");
data=(int) (Math.random()*10);
System.out.println("正在寫入:"+data);
Thread.sleep(500);
System.out.println("寫鎖調用完畢---------------------------");
lock.writeLock().unlock(); }
public void getValue() throws InterruptedException{
lock.readLock().lock();
System.out.println("正在使用讀鎖...........................................");
System.out.println("正在讀入:"+data); Thread.sleep(500);
System.out.println("讀鎖調用完畢......");
lock.readLock().unlock();
}
}
**** 當一個線程進入了一個對象是的synchronized方法,那麼其它線程還能掉否調用此對象的其它方法?
這個問題需要分幾種情況進行討論。
1)查看其它方法是否使用了同步關鍵字(synchronized)修飾,如果沒有的話就可以調用相關的方法。
2)在當前synchronized方法中是否調用了wait方法,如果調用了,則對應的鎖已經釋放,可以訪問了。
3)如果其它方法也使用synchronized修飾,並且當前同步方法中沒有調用wait方法的話,這樣是不允許訪問的。
4)如果其它方法是靜態方法的話,由於靜態方法和對象是扯不上什麼關系,對於靜態同步方法而言,其對應的同步監視器為當前類的位元組碼
所以肯定可以訪問的了。
B. java 總結幾種線程非同步轉同步的方法
以Java語言為例:
用synchronized關鍵字修飾同步方法。
同步有幾種實現方法分別是synchronized,wait與notify
wait():使一個線程處於等待狀態,並且釋放所持有的對象的lock。
sleep():使一個正在運行的線程處於睡眠狀態,是一個靜態方法,調用此方法要捕捉InterruptedException異常。
notify():喚醒一個處於等待狀態的線程,注意的是在調用此方法的時候,並不能確切的喚醒某一個等待狀態的線程,而是由JVM確定喚醒哪個線程,而且不是按優先順序。
Allnotity():喚醒所有處入等待狀態的線程,注意並不是給所有喚醒線程一個對象的鎖,而是讓它們競爭。
同步是多線程中的重要概念。同步的使用可以保證在多線程運行的環境中,程序不會產生設計之外的錯誤結果。同步的實現方式有兩種,同步方法和同步塊,這兩種方式都要用到synchronized關鍵字。
給一個方法增加synchronized修飾符之後就可以使它成為同步方法,這個方法可以是靜態方法和非靜態方法,但是不能是抽象類的抽象方法,也不能是介面中的介面方法。下面代碼是一個同步方法的示例:
public synchronized void aMethod() {
// do something
}
public static synchronized void anotherMethod() {
// do something
}
線程在執行同步方法時是具有排它性的。當任意一個線程進入到一個對象的任意一個同步方法時,這個對象的所有同步方法都被鎖定了,在此期間,其他任何線程都不能訪問這個對象的任意一個同步方法,直到這個線程執行完它所調用的同步方法並從中退出,從而導致它釋放了該對象的同步鎖之後。在一個對象被某個線程鎖定之後,其他線程是可以訪問這個對象的所有非同步方法的。
同步塊是通過鎖定一個指定的對象,來對同步塊中包含的代碼進行同步;而同步方法是對這個方法塊里的代碼進行同步,而這種情況下鎖定的對象就是同步方法所屬的主體對象自身。如果這個方法是靜態同步方法呢?那麼線程鎖定的就不是這個類的對象了,也不是這個類自身,而是這個類對應的java.lang.Class類型的對象。同步方法和同步塊之間的相互制約只限於同一個對象之間,所以靜態同步方法只受它所屬類的其它靜態同步方法的制約,而跟這個類的實例(對象)沒有關系。
C. java中同步有幾種方式啊
1。同步代碼塊:
synchronized(同一個數據){} 同一個數據:就是N條線程同時訪問一個數據。
2。
同步方法:
public synchronized 數據返回類型 方法名(){}
就
是使用 synchronized 來修飾某個方法,則該方法稱為同步方法。對於同步方法而言,無需顯示指定同步監視器,同步方法的同步監視器是
this
也就是該對象的本身(這里指的對象本身有點含糊,其實就是調用該同步方法的對象)通過使用同步方法,可非常方便的將某類變成線程安全的類,具有如下特徵:
1,該類的對象可以被多個線程安全的訪問。
2,每個線程調用該對象的任意方法之後,都將得到正確的結果。
3,每個線程調用該對象的任意方法之後,該對象狀態依然保持合理狀態。
註:synchronized關鍵字可以修飾方法,也可以修飾代碼塊,但不能修飾構造器,屬性等。
實現同步機制注意以下幾點: 安全性高,性能低,在多線程用。性能高,安全性低,在單線程用。
1,不要對線程安全類的所有方法都進行同步,只對那些會改變共享資源方法的進行同步。
2,如果可變類有兩種運行環境,當線程環境和多線程環境則應該為該可變類提供兩種版本:線程安全版本和線程不安全版本(沒有同步方法和同步塊)。在單線程中環境中,使用線程不安全版本以保證性能,在多線程中使用線程安全版本.
線程通訊:
為什麼要使用線程通訊?
當
使用synchronized
來修飾某個共享資源時(分同步代碼塊和同步方法兩種情況),當某個線程獲得共享資源的鎖後就可以執行相應的代碼段,直到該線程運行完該代碼段後才釋放對該
共享資源的鎖,讓其他線程有機會執行對該共享資源的修改。當某個線程佔有某個共享資源的鎖時,如果另外一個線程也想獲得這把鎖運行就需要使用wait()
和notify()/notifyAll()方法來進行線程通訊了。
Java.lang.object 里的三個方法wait() notify() notifyAll()
wait方法導致當前線程等待,直到其他線程調用同步監視器的notify方法或notifyAll方法來喚醒該線程。
wait(mills)方法
都是等待指定時間後自動蘇醒,調用wait方法的當前線程會釋放該同步監視器的鎖定,可以不用notify或notifyAll方法把它喚醒。
notify()
喚醒在同步監視器上等待的單個線程,如果所有線程都在同步監視器上等待,則會選擇喚醒其中一個線程,選擇是任意性的,只有當前線程放棄對該同步監視器的鎖定後,也就是使用wait方法後,才可以執行被喚醒的線程。
notifyAll()方法
喚醒在同步監視器上等待的所有的線程。只用當前線程放棄對該同步監視器的鎖定後,才可以執行被喚醒的線程
D. java synchronized同步靜態方法和同步非靜態方法的區別與舉例
同步靜態方法是對對應類的Class類加鎖,這樣的話,所有被synchronized修飾的靜態方法同一時刻只能有一個被調用。同步非靜態方法的話,它鎖的是對應的示例,並不影響其他示例。
E. java同步代碼塊和同步方法的區別
1. 語法不同。
2. 同步塊需要註明鎖定對象,同步方法默認鎖定this。
3. 在靜態方法中,都是默認鎖定類對象。
4. 在考慮性能方面,最好使用同步塊來減少鎖定范圍提高並發效率。
F. java中同步的幾種方法
Java的同步可以用synchronized關鍵字來實現。
sychronized可以同步代碼,需要綁定一個對象,如synchronized(obj){}
也可以同步一個方法,是對方法進行線程同步。如public void synchronized methodA(){}
G. java synchronized同步靜態方法和同步非靜態方法的異同
所有的非靜態同步方法用的都是同一把鎖——實例對象本身,也就是說如果一個實例對象的非靜態同步方法獲取鎖後,該實例對象的其他非靜態同步方法必須等待獲取鎖的方法釋放鎖後才能獲取鎖,可是別的實例對象的非靜態同步方法因為跟該實例對象的非靜態同步方法用的是不同的鎖,所以毋須等待該實例對象已獲取鎖的非靜態同步方法釋放鎖就可以獲取他們自己的鎖。
而所有的靜態同步方法用的也是同一把鎖——類對象本身,這兩把鎖是兩個不同的對象,所以靜態同步方法與非靜態同步方法之間是不會有競態條件的。但是一旦一個靜態同步方法獲取鎖後,其他的靜態同步方法都必須等待該方法釋放鎖後才能獲取鎖,而不管是同一個實例對象的靜態同步方法之間,還是不同的實例對象的靜態同步方法之間,只要它們同一個類的實例對象!
H. java語言同步問題,為什麼這樣寫就鎖不住了 謝謝各路大神解答!
你寫的方法的鎖對象是當前對象.也就是說TR1線程的鎖是TR1這個對象,TR2線程的鎖是TR2這個對象,2個線程互不幹擾
你可以參數如下信息:
詳細解說一下同步方法的鎖,同步方法分為靜態同步方法與非靜態同步方法。
所有的非靜態同步方法用的都是同一把鎖——實例對象本身,也
就是說如果一個實例對象的非靜態同步方法獲取鎖後,該實例對象的其他非靜態同步方法必須等待獲取鎖的方法釋放鎖後才能獲取鎖,可是別的實例對象的非靜態同
步方法因為跟該實例對象的非靜態同步方法用的是不同的鎖,所以毋須等待該實例對象已獲取鎖的非靜態同步方法釋放鎖就可以獲取他們自己的鎖。
而所有的靜態同步方法用的也是同一把鎖——類對象本身,這兩把鎖是兩個不同的對象,所以靜態同步方法與非靜態同步方法之間是不會有競態條件的。但是一旦一個靜態同步方法獲取鎖後,其他的靜態同步方法都必須等待該方法釋放鎖後才能獲取鎖,而不管是同一個實例對象的靜態同步方法之間,還是不同的實例對象的靜態同步方法之間,只要它們同一個類的實例對象!
而對於同步塊,由於其鎖是可以選擇的,所以只有使用同一把鎖的同步塊之間才有著競態條件,這就得具體情況具體分析了,但這里有個需要注意的地方,同步塊的鎖是可以選擇的,但是不是可以任意選擇的!!!!這里必須要注意一個物理對象和一個引用對象的實例變數之間的區別!使用一個引用對象的實例變數作為鎖並不是一個好的選擇,因為同步塊在執行過程中可能會改變它的值,其中就包括將其設置為null,而對一個null對象加鎖會產生異常,並且對不同的對象加鎖也違背了同步的初衷!這
看起來是很清楚的,但是一個經常發生的錯誤就是選用了錯誤的鎖對象,因此必須注意:同步是基於實際對象而不是對象引用的!多個變數可以引用同一個對象,變
量也可以改變其值從而指向其他的對象,因此,當選擇一個對象鎖時,我們要根據實際對象而不是其引用來考慮!作為一個原則,不要選擇一個可能會在鎖的作用域
中改變值的實例變數作為鎖對象!!!!
I. java 方法同步
設置三個同步變數不就完了,然後synchronized的時候就同步這三個變數區分就行了
J. Java實現同步的幾種方式
應該是同步方法和同步代碼塊。
synchronized,wait與notify 這幾個是實現同步的進一步細節操作,如果不是為了實現什麼多線程明細,就簡單的同步方法和同步代碼塊即可解決同步問題。