程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> java 線程安全 Lock,java線程lock

java 線程安全 Lock,java線程lock

編輯:JAVA綜合教程

java 線程安全 Lock,java線程lock


 java.util.concurrent.locks

  對於線程安全我們前面使用了synchronized關鍵字,對於線程的協作我們使用Object.wait()和Object.notify()。在JDK1.5中java為我們提供了Lock來實現與它們相同的功能,並且性能優於它們,在JDK1.6時,JDK對synchronized做了優化,在性能上兩種方式差距不大了。

一、為什麼出現lock

  synchronized修飾的代碼塊,當一個線程獲取了對應的鎖,並執行該代碼塊時,其他線程便只能一直等待,等待獲取鎖的線程釋放鎖,如果沒有釋放則需要無限的等待下去。獲取鎖的線程釋放鎖只會有兩種情況:

  1、獲取鎖的線程執行完了該代碼塊,然後線程釋放對鎖的占有。

  2、線程執行發生異常,此時JVM會讓線程自動釋放鎖。

Lock與synchronized對比:

  1、Lock不是Java語言內置的,synchronized是Java語言的關鍵字,因此是內置特性。Lock是一個類,通過這個類可以實現同步訪問。

  2、synchronized不需要手動釋放鎖,當synchronized方法或者synchronized代碼塊執行完之後,系統會自動讓線程釋放對鎖的占用;而Lock則必須要用戶去手動釋放鎖,如果沒有主動釋放鎖,就有可能導致出現死鎖現象。

二、java.util.concurrent.locks包中常用的類和接口。

public interface Lock {
    //用來獲取鎖。如果鎖已被其他線程獲取,則進行等待。
    void lock();
   // 當通過這個方法去獲取鎖時,如果線程正在等待獲取鎖,則這個線程能夠響應中斷,即中斷線程的等待狀態
    void lockInterruptibly() throws InterruptedException;
    //它表示用來嘗試獲取鎖,如果獲取成功,則返回true,如果獲取失敗(即鎖已被其他線程獲取),則返回false
    boolean tryLock();
    //與tryLock()方法是類似的,只不過區別在於這個方法在拿不到鎖時會等待一定的時間,在時間期限之內如果還拿不到鎖,就返回false。如果如果一開始拿到鎖或者在等待期間內拿到了鎖,則返回true。
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    //釋放鎖
    void unlock();
    Condition newCondition();
}

1、Lock與unlock
  Lock用於獲取鎖,但它不會主動釋放鎖所以需要與unlock()配合使用。一般在使用Lock時必須在try{}catch{}塊中進行,並且將釋放鎖的操作放在finally塊中進行,以保證鎖一定被被釋放,防止死鎖的發生。

package com.jalja.base.threadTest;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest implements Runnable{
    public static ReentrantLock lock=new ReentrantLock();
    public static int c=0;
    public void run() {
        for(int i=0;i<1000;i++){
            lock.lock();//獲取鎖
            try {
                System.out.println(Thread.currentThread().getName()+"獲得鎖");
                System.out.println(Thread.currentThread().getName()+"====>"+c);
                c++;
            } catch (Exception e) {
                e.printStackTrace();
            }finally{
                System.out.println(Thread.currentThread().getName()+"釋放鎖");
                lock.unlock();//釋放鎖
            }
        }
    }
    public static void main(String[] args) {
        LockTest lt=new LockTest();
        Thread thread1=new Thread(lt);
        Thread thread2=new Thread(lt);
        thread1.start();
        thread2.start();
        try {
            thread1.join();
            thread2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(c);
    }
}

注意:同一個線程可以連續獲得同一把鎖,但也必須釋放相同次數的鎖。允許下面的寫法

          lock.lock();//獲取鎖
            lock.lock();
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName()+"獲得鎖");
                System.out.println(Thread.currentThread().getName()+"====>"+c);
                c++;
            } catch (Exception e) {
                e.printStackTrace();
            }finally{
                System.out.println(Thread.currentThread().getName()+"釋放鎖");
                lock.unlock();//釋放鎖
                lock.unlock();//釋放鎖
                lock.unlock();//釋放鎖
            }

2、獲取鎖等待時間tryLock(long time, TimeUnit unit)
  如果你約朋友打籃球,約定時間到了你朋友還沒有出現,你等1小時後還是沒到,我想你肯定會掃興的離去。對於線程來說也應該時這樣的,因為通常我們是無法判斷一個線程為什麼會無法獲得鎖,但我們可以給該線程一個獲取鎖的時間限制,如果到時間還沒有獲取到鎖,則放棄獲取鎖。

package com.jalja.base.threadTest;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class TryLockTest implements Runnable{
    public static ReentrantLock lock=new ReentrantLock();
    private static int m=0;
    public void run() {
        try {
            if(lock.tryLock(1, TimeUnit.SECONDS)){//設置獲取鎖的等待時長1秒
                System.out.println(Thread.currentThread().getName()+"獲得鎖");
                m++;
                //Thread.sleep(2000);//設置休眠2秒
            }else{
                System.out.println(Thread.currentThread().getName()+"未獲得鎖");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            if(lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }
    }
    public static void main(String[] args) {
        TryLockTest thread1=new TryLockTest();
        TryLockTest thread2=new TryLockTest();
        Thread th1=new Thread(thread1);
        Thread th2=new Thread(thread2);
        th1.start();
        th2.start();
        try {
            //讓main線程等待th1、th2線程執行完畢後,再繼續執行
            th1.join();
            th2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(m);
    }
}

執行結果:

Thread-0獲得鎖
Thread-1獲得鎖
2

  該代碼就是讓線程在鎖請求中,最多等待1秒,如果超過一秒沒有獲得鎖就返回false,如果獲得了鎖就返回true,根據執行結果可以看出Thread-1線程在1秒內獲得了鎖。

我們開啟注釋 //Thread.sleep(2000);就會發現Thread-1或Thread-0一定會有一個是未獲得鎖,這是因為占用鎖的線程時間是2秒,而等待鎖的線程等待時間是1秒,所以在1秒後的瞬間它就放棄了請求鎖操作。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved