1. 程式人生 > >Java多線程學習篇(三)Lock

Java多線程學習篇(三)Lock

參考 簡單 which sys lin int some call row

Lock 是Java多線程的一個同步機制,用來控制線程對共享資源的訪問。線程在執行同步方法或者代碼塊之前必須先獲得一個鎖。

Lock 的 lock() 和 unlock() 方法;

  lock():獲得一個鎖,如果鎖不可用,則當前線程將因線程調度目的而被禁用,並在獲得鎖之前處於休眠狀態。

  unlock():釋放掉獲得的鎖。

Lock的作用範圍:

  1. 若 Lock 是靜態的,則作用範圍是整個類。

    技術分享圖片
    public class Test {
        public static void main(String[] args) {
            Thread thread_one = new
    Thread(new Account(), "Thread_ONE"); Thread thread_two = new Thread(new Account(), "Thread_Tow"); thread_one.start(); thread_two.start(); } } class Account implements Runnable{ static Lock lock = new ReentrantLock(); // 靜態的Lock static int Count = 0; @Override
    public void run(){ runTest(); } public void runTest() { lock.lock(); try{ for(int i = 0; i < 5; ++i) { Count++; System.out.println(Thread.currentThread().getName() + " " + Count); } }
    finally { lock.unlock(); } } } //輸出: //Thread_ONE 1 //Thread_ONE 2 //Thread_ONE 3 //Thread_ONE 4 //Thread_ONE 5 //Thread_Tow 6 //Thread_Tow 7 //Thread_Tow 8 //Thread_Tow 9 //Thread_Tow 10
    靜態的Lock
  2. 若 Lock 是非靜態的,則範圍是整個對象。

    技術分享圖片
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Test1 {
        public static void main(String[] args) {
            Thread thread_one = new Thread(new Account(), "Thread_ONE");
            Thread thread_two = new Thread(new Account(), "Thread_Tow");
            thread_one.start();
            thread_two.start();
        }
    }
    class Account implements Runnable{
        Lock lock = new ReentrantLock();  // 非靜態的Lock
        static int Count = 0;
        @Override
        public void run(){
            runTest();
        }
        public void runTest() {
            lock.lock();
            try{
                for(int i = 0; i < 5; ++i)
                {
                    Count++;
                    System.out.println(Thread.currentThread().getName() + " " + Count);
                }
            }
            finally {
                lock.unlock();
            }
        }
    }
    //輸出:
    //Thread_Tow 2
    //Thread_ONE 2
    //Thread_Tow 3
    //Thread_ONE 4
    //Thread_Tow 5
    //Thread_ONE 6
    //Thread_Tow 7
    //Thread_ONE 8
    //Thread_Tow 9
    //Thread_ONE 10
    非靜態的Lock

Lock的一個簡單的實現

public class Lock{
    
    private boolean isLocked = false;

    public synchronized void lock()throws InterruptedException{
        while(isLocked){ // 使用whlie而不是if
            wait(); // 終止線程並且釋放對象的鎖,當線程被通知後重新啟動,鎖就會自動被重新獲取
        }
        isLocked = true;
    }
    public synchronized void unlock(){
        isLocked = false;
        notify();//通知一個等待的線程重新獲取鎖並且恢復執行
    }
}

這裏的方法lock(),若當前的鎖被其它線程獲取而鎖住,這調用wait()方法,讓線程等待,直到其他線程調用unlock()中的notify()方法而重新啟動。這裏用的是while而不是if,防止wait()退出後鎖還是被其它線程鎖住,(比如鎖又被其它線程搶走),(lock() 和 notify()、notifyAll() 是Object對象中的方法)


ReentrantLock 是 Lock接口 的一種實現。
public class ReentrantLock implements Lock, java.io.Serializable{ }

ReentrantLock 具有可重入性。
什麽是可可重入性呢?先舉個例子。
public class Reentrant2{

    Lock lock = new Lock(); // 這裏的Lock用的是上面寫的 public class Lock { }

    public outer(){
        lock.lock();
        inner();
        lock.unlock();
    }

    public synchronized inner(){
        lock.lock();
        //do something
        lock.unlock();
    }
}
假設某個線程調用了outer()方法,這時,該線程調用了lock(),獲取了鎖,然後再調用inner(),但是inner()內再次調用 lock() 獲取鎖,可是這鎖已經被第一次調用而鎖上,所以會進入while調用wait()從而導致死鎖。
可重入性可以讓線程調用自身持有的鎖(如果該鎖是可重入的)。

改寫一下Lock的代碼
public class Lock{

    boolean isLocked = false;
    Thread  lockedBy = null;
    int     lockedCount = 0;

    public synchronized void lock()
            throws InterruptedException{
        Thread callingThread = Thread.currentThread();
        while(isLocked && lockedBy != callingThread){  // 若鎖被不是自己持有的鎖鎖住
            wait();
        }
        isLocked = true;
        lockedCount++;
        lockedBy = callingThread;
    }
    
    public synchronized void unlock(){
        if(Thread.curentThread() == this.lockedBy){
            lockedCount--;

            if(lockedCount == 0){
                isLocked = false;
                notify();
            }
        }
    }
  ...
}
while 循環中加了一個判斷 lockedBy != callingThread,這樣就不會被自己的鎖鎖住,並且用 lockedCount 計數一下,只有當 lockedCount 為 0 時,才會調用notify()方法。

最後說一點,使用鎖的時候最好使用try,以防一些想不到的問題導致鎖沒有釋放。
lock.lock();
try{
  //do critical section code, which may throw exception
} finally {
  lock.unlock();
}

參考:http://tutorials.jenkov.com/java-concurrency/locks.html




























Java多線程學習篇(三)Lock