深入淺出Java並發包—鎖(Lock)VS同步(synchronized)


今天我們來探討一下Java中的鎖機制。前面我們提到,在JDK1.5之前只能通過synchronized關鍵字來實現同步,這個前面我們已經提到是屬於獨占鎖,性能並不高,因此JDK1.5之后開始借助JNI實現了一套高效的鎖實現!

JDK5以后鎖的接口是JUC中的Lock,我們來先看一下他的相關API文檔。

 

方法摘要

 void

lock() 
 
獲取鎖。 如果鎖不可用,出於線程調度目的,將禁用當前線程,並且在獲得鎖之前,該線程將一直處於休眠狀態。

 void

lockInterruptibly()

         如果當前線程未被中斷,則獲取鎖。

如果鎖可用,則獲取鎖,並立即返回。

如果鎖不可用,出於線程調度目的,將禁用當前線程,並且在發生以下兩種情況之一以前,該線程將一直處於休眠狀態:

 鎖由當前線程獲得;或者

 其他某個線程中斷當前線程,並且支持對鎖獲取的中斷。

如果當前線程:

 在進入此方法時已經設置了該線程的中斷狀態;或者

 在獲取鎖時被中斷,並且支持對鎖獲取的中斷,

則將拋出 InterruptedException,並清除當前線程的已中斷狀態。

 Condition

newCondition()
         
返回綁定到此 Lock 實例的新 Condition 實例。

在等待條件前,鎖必須由當前線程保持。調用 Condition.await() 將在等待前以原子方式釋放鎖,並在等待返回前重新獲取鎖。

 boolean

tryLock() 
 
僅在調用時鎖為空閑狀態才獲取該鎖。

如果鎖可用,則獲取鎖,並立即返回值 true。如果鎖不可用,則此方法將立即返回值 false

 

此方法的典型使用語句如下:

 

      Lock lock = ...;

      if (lock.tryLock()) {

          try {

              // manipulate protected state

          } finally {

              lock.unlock();

          }

      } else {

          // perform alternative actions

      }

 此用法可確保如果獲取了鎖,則會釋放鎖,如果未獲取鎖,則不會試圖將其釋放。

 boolean

tryLock(long time, TimeUnit unit)
         
如果鎖在給定的等待時間內空閑,並且當前線程未被中斷,則獲取鎖。

如果鎖可用,則此方法將立即返回值 true。如果鎖不可用,出於線程調度目的,將禁用當前線程,並且在發生以下三種情況之一前,該線程將一直處於休眠狀態:

l鎖由當前線程獲得;或者

l其他某個線程中斷當前線程,並且支持對鎖獲取的中斷;或者

l已超過指定的等待時間

 

如果獲得了鎖,則返回值 true

 

如果當前線程:

l在進入此方法時已經設置了該線程的中斷狀態;或者

l在獲取鎖時被中斷,並且支持對鎖獲取的中斷,

則將拋出 InterruptedException,並會清除當前線程的已中斷狀態。

如果超過了指定的等待時間,則將返回值 false。如果 time 小於等於0,該方法將完全不等待。

void

unlock()
          
釋放鎖。

相對於API來說,我們並不能看出他到底的優點在哪里?我們來看一個實例

package com.yhj.lock;
/**
 * @Described:原子int類型操作測試用例
 * @author YHJ create at 2013-4-26 下午05:58:32
 * @ClassNmae com.yhj.lock.AtomicIntegerTestCase
 */
public interface AtomicIntegerTestCase {
 
    /**
     * ++並返回
     * @return
     * @Author YHJ create at 2013-4-26 下午05:39:47
     */
    int incrementAndGet();
    /**
     * 取值
     * @return
     * @Author YHJ create at 2013-4-26 下午05:39:56
     */
    int get();
}
package com.yhj.lock;
 
 
/**
 * @Described:帶同步的測試
 * @author YHJ create at 2013-4-26 下午05:35:35
 * @ClassNmae com.yhj.lock.AtomicIntegerWithLock
 */
public class AtomicIntegerWithSynchronized implements AtomicIntegerTestCase {
   
    private int value;
    private Object lock  = new Object();//為保證兩個方法都在同步內 采用對象同步方法
   
    @Override
    public int incrementAndGet(){
       synchronized(lock){
           return ++value;
       }
    }
   
    @Override
    public int get(){
       synchronized(lock){
           return value;
       }
    }
 
}
package com.yhj.lock;
 
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
/**
 * @Described:帶鎖的測試
 * @author YHJ create at 2013-4-26 下午05:35:35
 * @ClassNmae com.yhj.lock.AtomicIntegerWithLock
 */
public class AtomicIntegerWithLock implements AtomicIntegerTestCase{
   
    private int value;
    private Lock lock  = new ReentrantLock();
   
    @Override
    public int incrementAndGet(){
       try {
           lock.lock();
           return ++value;
       }finally{
           lock.unlock();
       }
      
    }
 
    @Override
    public int get(){
       try {
           lock.lock();
           return value;
       }finally{
           lock.unlock();
       }
    }
 
}
package com.yhj.lock;
 
public class Client {
 
    /**
     * 啟動並等待線程結束
     * @param threads
     * @throws InterruptedException
     * @Author YHJ create at 2013-4-26 下午05:57:16
     */
    private static void startAndWait(Thread [] threads) throws InterruptedException{
       for(Thread thread:threads)
           thread.start();
       for(Thread thread:threads)
           thread.join();
    }
    /**
     * 准備線程數據
     * @param threads
     * @param testCase
     * @param threadCount
     * @param loopCount
     * @Author YHJ create at 2013-4-26 下午06:01:25
     */
    private static void prepare(Thread [] threads,final AtomicIntegerTestCase testCase,int threadCount,final int loopCount){
       for (int i = 0; i < threadCount; i++) {
           threads[i] = new Thread(){
              @Override
              public void run() {
                  for(int i=0;i<loopCount;++i){
                     testCase.incrementAndGet();
                  }
              }
           };
       }
    }
 
    public static void main(String[] args) throws InterruptedException {
       //前期數據准備
       final int threadCount = 200;//線程數目
       final int loopCount = 100000;//循環次數
       final AtomicIntegerWithLock lockTestCase = new AtomicIntegerWithLock();//測試對象
       final AtomicIntegerWithSynchronized synchronizedtestCase = new AtomicIntegerWithSynchronized();
       //第一波數據准備
       Thread [] threads= new Thread[threadCount];
       prepare(threads, lockTestCase, threadCount, loopCount);
       //第一波啟動
       long costTime = 0;
       long start = System.nanoTime();
       startAndWait(threads);
       long end = System.nanoTime();
       costTime = (end-start);
       //第一波輸出
       System.out.println("AtomicIntegerWithLock result:"+lockTestCase.get()+" costTime:"+costTime);
       System.out.println("=======我是分割線=======");
       //第二波數據准備
       threads= new Thread[threadCount];
       prepare(threads, synchronizedtestCase, threadCount, loopCount);
       //第二波啟動
       costTime = 0;
       start = System.nanoTime();
       startAndWait(threads);
       end = System.nanoTime();
       costTime = (end-start);
       //第二波輸出
       System.out.println("AtomicIntegerWithSynchronized result:"+lockTestCase.get()+" costTime:"+costTime);
    }
 
}

運行結果:

AtomicIntegerWithLock result:20000000 costTime:1192257757
=======我是分割線=======
AtomicIntegerWithSynchronized result:20000000 costTime:3955951264

這個例子很簡單,200個線程,每次執行10w++操作,很清楚的大家看到,最好的執行結果都是20000000,都能正常的保證數據的一致性,另外我們還能看到一點,就是Lock消耗的時間要比synchronized少,也證明了Lock的性能是要筆synchronized好的!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM