java中實現同步的兩種方式:syschronized和lock的區別和聯繫

 Lock是java.util.concurrent.locks包下的接口,Lock 實現提供了比使用synchronized 方法和語句可獲得的更廣泛的鎖定操作,它能以更優雅的方式處理線程同步問題,我們拿Java線程(二)中的一個例子簡單的實現一下和sychronized一樣的效果,代碼如下:

 

  1. public class LockTest {  
  2.     public static void main(String[] args) {  
  3.         final Outputter1 output = new Outputter1();  
  4.         new Thread() {  
  5.             public void run() {  
  6.                 output.output("zhangsan");  
  7.             };  
  8.         }.start();        
  9.         new Thread() {  
  10.             public void run() {  
  11.                 output.output("lisi");  
  12.             };  
  13.         }.start();  
  14.     }  
  15. }  
  16. class Outputter1 {  
  17.     private Lock lock = new ReentrantLock();// 鎖對象  
  18.     public void output(String name) {  
  19.         // TODO 線程輸出方法  
  20.         lock.lock();// 得到鎖  
  21.         try {  
  22.             for(int i = 0; i < name.length(); i++) {  
  23.                 System.out.print(name.charAt(i));  
  24.             }  
  25.         } finally {  
  26.             lock.unlock();// 釋放鎖  
  27.         }  
  28.     }  
  29. }  

        這樣就實現了和sychronized一樣的同步效果,需要注意的是,用sychronized修飾的方法或者語句塊在代碼執行完之後鎖自動釋放,而用Lock需要我們手動釋放鎖,所以爲了保證鎖最終被釋放(發生異常情況),要把互斥區放在try內,釋放鎖放在finally內。

 

        如果說這就是Lock,那麼它不能成爲同步問題更完美的處理方式,下面要介紹的是讀寫鎖(ReadWriteLock),我們會有一種需求,在對數據進行讀寫的時候,爲了保證數據的一致性和完整性,需要讀和寫是互斥的,寫和寫是互斥的,但是讀和讀是不需要互斥的,這樣讀和讀不互斥性能更高些,來看一下不考慮互斥情況的代碼原型:

 

  1. public class ReadWriteLockTest {  
  2.     public static void main(String[] args) {  
  3.         final Data data = new Data();  
  4.         for (int i = 0; i < 3; i++) {  
  5.             new Thread(new Runnable() {  
  6.                 public void run() {  
  7.                     for (int j = 0; j < 5; j++) {  
  8.                         data.set(new Random().nextInt(30));  
  9.                     }  
  10.                 }  
  11.             }).start();  
  12.         }         
  13.         for (int i = 0; i < 3; i++) {  
  14.             new Thread(new Runnable() {  
  15.                 public void run() {  
  16.                     for (int j = 0; j < 5; j++) {  
  17.                         data.get();  
  18.                     }  
  19.                 }  
  20.             }).start();  
  21.         }  
  22.     }  
  23. }  
  24. class Data {      
  25.     private int data;// 共享數據      
  26.     public void set(int data) {  
  27.         System.out.println(Thread.currentThread().getName() + "準備寫入數據");  
  28.         try {  
  29.             Thread.sleep(20);  
  30.         } catch (InterruptedException e) {  
  31.             e.printStackTrace();  
  32.         }  
  33.         this.data = data;  
  34.         System.out.println(Thread.currentThread().getName() + "寫入" + this.data);  
  35.     }     
  36.     public void get() {  
  37.         System.out.println(Thread.currentThread().getName() + "準備讀取數據");  
  38.         try {  
  39.             Thread.sleep(20);  
  40.         } catch (InterruptedException e) {  
  41.             e.printStackTrace();  
  42.         }  
  43.         System.out.println(Thread.currentThread().getName() + "讀取" + this.data);  
  44.     }  
  45. }  

        部分輸出結果:

 

 

  1. Thread-1準備寫入數據  
  2. Thread-3準備讀取數據  
  3. Thread-2準備寫入數據  
  4. Thread-0準備寫入數據  
  5. Thread-4準備讀取數據  
  6. Thread-5準備讀取數據  
  7. Thread-2寫入12  
  8. Thread-4讀取12  
  9. Thread-5讀取5  
  10. Thread-1寫入12  

        我們要實現寫入和寫入互斥,讀取和寫入互斥,讀取和讀取互斥,在set和get方法加入sychronized修飾符:

 

 

  1. public synchronized void set(int data) {...}      
  2. public synchronized void get() {...}  

        部分輸出結果:

  1. Thread-0準備寫入數據  
  2. Thread-0寫入9  
  3. Thread-5準備讀取數據  
  4. Thread-5讀取9  
  5. Thread-5準備讀取數據  
  6. Thread-5讀取9  
  7. Thread-5準備讀取數據  
  8. Thread-5讀取9  
  9. Thread-5準備讀取數據  
  10. Thread-5讀取9  

        我們發現,雖然寫入和寫入互斥了,讀取和寫入也互斥了,但是讀取和讀取之間也互斥了,不能併發執行,效率較低,用讀寫鎖實現代碼如下:

 

 

  1. class Data {      
  2.     private int data;// 共享數據  
  3.     private ReadWriteLock rwl = new ReentrantReadWriteLock();     
  4.     public void set(int data) {  
  5.         rwl.writeLock().lock();// 取到寫鎖  
  6.         try {  
  7.             System.out.println(Thread.currentThread().getName() + "準備寫入數據");  
  8.             try {  
  9.                 Thread.sleep(20);  
  10.             } catch (InterruptedException e) {  
  11.                 e.printStackTrace();  
  12.             }  
  13.             this.data = data;  
  14.             System.out.println(Thread.currentThread().getName() + "寫入" + this.data);  
  15.         } finally {  
  16.             rwl.writeLock().unlock();// 釋放寫鎖  
  17.         }  
  18.     }     
  19.     public void get() {  
  20.         rwl.readLock().lock();// 取到讀鎖  
  21.         try {  
  22.             System.out.println(Thread.currentThread().getName() + "準備讀取數據");  
  23.             try {  
  24.                 Thread.sleep(20);  
  25.             } catch (InterruptedException e) {  
  26.                 e.printStackTrace();  
  27.             }  
  28.             System.out.println(Thread.currentThread().getName() + "讀取" + this.data);  
  29.         } finally {  
  30.             rwl.readLock().unlock();// 釋放讀鎖  
  31.         }  
  32.     }  
  33. }  

 

        部分輸出結果:

 

  1. Thread-4準備讀取數據  
  2. Thread-3準備讀取數據  
  3. Thread-5準備讀取數據  
  4. Thread-5讀取18  
  5. Thread-4讀取18  
  6. Thread-3讀取18  
  7. Thread-2準備寫入數據  
  8. Thread-2寫入6  
  9. Thread-2準備寫入數據  
  10. Thread-2寫入10  
  11. Thread-1準備寫入數據  
  12. Thread-1寫入22  
  13. 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 
("\n---------------------------------------------------------------");
}
}.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)如果其它方法是靜態方法的話,由於靜態方法和對象是扯不上什麼關係,對於靜態同步方法而言,其對應的同步監視器爲當前類的字節碼

           所以肯定可以訪問的了。

發佈了55 篇原創文章 · 獲贊 61 · 訪問量 60萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章