第四章-java多線程核心技術-Lock鎖-第一篇

lock鎖的使用

ReentrantLock鎖的使用

在JDK5.0版本以前,重入鎖的性能遠遠好於synchronized關鍵字,JDK6.0版本以後synchronized 獲得了大量的優化,兩者性能也不分伯仲,可是重入鎖是能夠徹底替代synchronized關鍵字的。除此以外,重入鎖又自帶一系列其餘功能:可中斷響應、鎖申請等待限時、公平鎖。另外能夠結合Condition來使用,使其更是逼格滿滿。java

請看以下使用代碼小程序

public class ReenTrantLock {
    private Lock lock = new ReentrantLock();

    public void testLock() {
        lock.lock();
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "---" + i);
        }
        lock.unlock();
    }

} 

/**
 * @program: demo
 * @description: demo
 * @author: lee
 * @create: 2018-09-03
 **/
public class ThreadA extends Thread {
    private  ReenTrantLock reenTrantLock ;

    public ThreadA(ReenTrantLock reenTrantLock) {
        this.reenTrantLock = reenTrantLock;
    }

    @Override
    public void run() {
        super.run();
        this.reenTrantLock.testLock();
    }
}

public class Run {
    public static void main(String[] args) {
        ReenTrantLock reenTrantLock = new ReenTrantLock();
        ThreadA threadA = new ThreadA(reenTrantLock);
        threadA.start();
        ThreadA threadB = new ThreadA(reenTrantLock);
        threadB.start();
        ThreadA threadC = new ThreadA(reenTrantLock);
        threadC.start();
        ThreadA threadD = new ThreadA(reenTrantLock);
        threadD.start();
    }
}

輸出結果ide

Thread-0---0
Thread-0---1
Thread-0---2
Thread-0---3
Thread-0---4
Thread-0---5
Thread-0---6
Thread-0---7
Thread-0---8
Thread-0---9
Thread-1---0
Thread-1---1
Thread-1---2
Thread-1---3
Thread-1---4
Thread-1---5
Thread-1---6
Thread-1---7
Thread-1---8
Thread-1---9
...

ReentrantLock同步實現

請看以下代碼性能

/**
 * @program: demo
 * @description: demo
 * @author: lee
 * @create: 2018-09-03
 **/
public class ReenTrantLock {
    private Lock lock = new ReentrantLock();

    public void testLock() {
        lock.lock();
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "---" + i);
        }
        lock.unlock();
    }

    public void testLockB() {
        lock.lock();
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "---" + i);
        }
        lock.unlock();
    }

}  

/**
 * @program: demo
 * @description: demo
 * @author: lee
 * @create: 2018-09-03
 **/
public class ThreadA extends Thread {
    private  ReenTrantLock reenTrantLock ;

    public ThreadA(ReenTrantLock reenTrantLock) {
        this.reenTrantLock = reenTrantLock;
    }

    @Override
    public void run() {
        super.run();
        this.reenTrantLock.testLock();
    }
}

public class ThreadB extends Thread {
    private  ReenTrantLock reenTrantLock ;

    public ThreadB(ReenTrantLock reenTrantLock) {
        this.reenTrantLock = reenTrantLock;
    }

    @Override
    public void run() {
        super.run();
        this.reenTrantLock.testLockB();
    }
}

public class Run {
    public static void main(String[] args) {
        ReenTrantLock reenTrantLock = new ReenTrantLock();
        ThreadA threadA = new ThreadA(reenTrantLock);
        threadA.start();
        ThreadB threadB = new ThreadB(reenTrantLock);
        threadB.start();
    }
}

輸出結果學習

Thread-0---0
Thread-0---1
Thread-0---2
Thread-0---3
Thread-0---4
Thread-0---5
Thread-0---6
Thread-0---7
Thread-0---8
Thread-0---9
Thread-1---0
Thread-1---1
Thread-1---2
Thread-1---3
Thread-1---4
Thread-1---5
Thread-1---6
Thread-1---7
Thread-1---8
Thread-1---9

由輸出結果可知,線程之間呈現互斥訪問,即lock鎖持有的是對象監視器,同一個鎖修飾的線程之間的代碼是互斥的。優化

lock鎖實現等待通知

在前面咱們學習與synchronized鎖配合的線程等待(Object.wait)與線程通知(Object.notify),那麼對於JDK1.5 的 java.util.concurrent.locks.ReentrantLock 鎖,JDK也爲咱們提供了與此功能相應的類java.util.concurrent.locks.Condition。Condition與重入鎖是經過lock.newCondition()方法產生一個與當前重入鎖綁定的Condtion實例,咱們通知該實例來控制線程的等待與通知。該接口的全部方法:this

public interface Condition {
     //使當前線程加入 await() 等待隊列中,並釋放當鎖,當其餘線程調用signal()會從新請求鎖。與Object.wait()相似。
    void await() throws InterruptedException;

    //調用該方法的前提是,當前線程已經成功得到與該條件對象綁定的重入鎖,不然調用該方法時會拋出IllegalMonitorStateException。
    //調用該方法後,結束等待的惟一方法是其它線程調用該條件對象的signal()或signalALL()方法。等待過程當中若是當前線程被中斷,該方法仍然會繼續等待,同時保留該線程的中斷狀態。 
    void awaitUninterruptibly();

    // 調用該方法的前提是,當前線程已經成功得到與該條件對象綁定的重入鎖,不然調用該方法時會拋出IllegalMonitorStateException。
    //nanosTimeout指定該方法等待信號的的最大時間(單位爲納秒)。若指定時間內收到signal()或signalALL()則返回nanosTimeout減去已經等待的時間;
    //若指定時間內有其它線程中斷該線程,則拋出InterruptedException並清除當前線程的打斷狀態;若指定時間內未收到通知,則返回0或負數。 
    long awaitNanos(long nanosTimeout) throws InterruptedException;

    //與await()基本一致,惟一不一樣點在於,指定時間以內沒有收到signal()或signalALL()信號或者線程中斷時該方法會返回false;其它狀況返回true。
    boolean await(long time, TimeUnit unit) throws InterruptedException;

   //適用條件與行爲與awaitNanos(long nanosTimeout)徹底同樣,惟一不一樣點在於它不是等待指定時間,而是等待由參數指定的某一時刻。
    boolean awaitUntil(Date deadline) throws InterruptedException;
    
    //喚醒一個在 await()等待隊列中的線程。與Object.notify()類似
    void signal();

   //喚醒 await()等待隊列中全部的線程。與object.notifyAll()類似
    void signalAll();
}

請看實際小程序線程

public class MyService {
    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    public void testLock() {
        try {
            lock.lock();
            System.out.println("即將進入wait喚醒");
            condition.await();
            System.out.println("wait喚醒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void testLockB() {
        try {
            lock.lock();
            System.out.println("即將喚醒線程");
            condition.signal();
        }finally {
            lock.unlock();
        }

    }

}    

public class ThreadA extends Thread {
    private MyService reenTrantLock ;

    public ThreadA(MyService reenTrantLock) {
        this.reenTrantLock = reenTrantLock;
    }

    @Override
    public void run() {
        super.run();
        this.reenTrantLock.testLock();
    }
}

public class ThreadB extends Thread {
    private MyService reenTrantLock ;

    public ThreadB(MyService reenTrantLock) {
        this.reenTrantLock = reenTrantLock;
    }

    @Override
    public void run() {
        super.run();
        this.reenTrantLock.testLockB();
    }
}

public class Run {
    public static void main(String[] args) {

        try {
            MyService service = new MyService();
            ThreadA threadA = new ThreadA(service);
            threadA.start();
            Thread.sleep(1000);
            ThreadB threadB = new ThreadB(service);
            threadB.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

輸出結果code

即將進入wait喚醒
即將喚醒線程
wait喚醒

通知部分線程

請看以下代碼對象

public class MyService {
    private Lock lock = new ReentrantLock();

    private Condition condition = lock.newCondition();

    private Condition condition2 = lock.newCondition();

    public void testLock() {
        try {
            lock.lock();
            System.out.println("condition即將進入wait喚醒");
            condition.await();
            System.out.println("condition wait已經喚醒喚醒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void testLockB() {
        try {
            lock.lock();
            System.out.println("condition2即將進入wait喚醒");
            condition2.await();
            System.out.println("condition2 wait已經喚醒喚醒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    public void callCondition() {
        try {
            lock.lock();
            condition.signal();
            System.out.println("即將喚醒Condition");
        }finally {
            lock.unlock();
        }
    }

    public void callCondition2() {
        try {
            lock.lock();
            condition2.signal();
            System.out.println("即將喚醒Condition2");
        }finally {
            condition2.signal();
        }
    }

}   

public class ThreadA extends Thread {
    private MyService reenTrantLock ;

    public ThreadA(MyService reenTrantLock) {
        this.reenTrantLock = reenTrantLock;
    }

    @Override
    public void run() {
        super.run();
        this.reenTrantLock.testLock();
    }
}

public class ThreadB extends Thread {
    private MyService reenTrantLock ;

    public ThreadB(MyService reenTrantLock) {
        this.reenTrantLock = reenTrantLock;
    }

    @Override
    public void run() {
        super.run();
        this.reenTrantLock.testLockB();
    }
}

public class Run {
    public static void main(String[] args) {

        try {
            MyService service = new MyService();
            ThreadA threadA = new ThreadA(service);
            threadA.start();
            ThreadB threadB = new ThreadB(service);
            threadB.start();
            Thread.sleep(1000);
            service.callCondition();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

輸出結果

condition即將進入wait喚醒
condition2即將進入wait喚醒
即將喚醒Condition
condition wait已經喚醒喚醒

此時程序仍未結束,還在處於運行狀態,由於其中一個線程還在處於等待狀態。

lock實現單生產者單消費着模式

請看以下代碼

public class Product {
    public static boolean hasValue =false;
}    

public class MyService {
    private Lock lock = new ReentrantLock();
    private boolean hasValue = false;
    private Condition condition = lock.newCondition();

    public void product() {
        try {
            lock.lock();
            while (true) {
                if (Product.hasValue) {
                    condition.await();
                }
                System.out.println("生產了一個物品");
                Product.hasValue = true;
                condition.signal();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void consume() {
        try {
            lock.lock();
            while (true) {
                if (!Product.hasValue) {
                    condition.await();
                }
                System.out.println("消費了一個物品");
                Product.hasValue = false;
                condition.signal();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}  

public class ThreadA extends Thread {
    private MyService reenTrantLock ;

    public ThreadA(MyService reenTrantLock) {
        this.reenTrantLock = reenTrantLock;
    }

    @Override
    public void run() {
        super.run();
        this.reenTrantLock.product();
    }
}

public class ThreadB extends Thread {
    private MyService reenTrantLock ;

    public ThreadB(MyService reenTrantLock) {
        this.reenTrantLock = reenTrantLock;
    }

    @Override
    public void run() {
        super.run();
        this.reenTrantLock.consume();
    }
}

輸出結果

生產了一個物品
消費了一個物品
生產了一個物品
消費了一個物品
生產了一個物品
消費了一個物品
生產了一個物品
消費了一個物品
生產了一個物品
消費了一個物品
....

公平鎖和非公平鎖

公平鎖和非公平鎖

公平鎖的意思就是線程獲取鎖的順序是按照線程加鎖的順序來分配的,即先來先得的FIFO先進先出的方式,並不必定所有是。非公平鎖就是指線程隨機獲取到鎖,先加鎖的不必定先得到鎖,這種方式可能形成一些線程老是拿不到鎖,結果也就是不公平的。

請看以下代碼

//final MyService service = new MyService(true); 非公平鎖
public class MyService {
    private ReentrantLock lock ;
    private boolean isFair;

    public MyService(boolean isFair) {
        this.isFair = isFair;
        lock =  new ReentrantLock(this.isFair);
    }
    public void serviceMethod() {
        try {
            lock.lock();
            System.out.println("得到鎖的線程名稱是" + Thread.currentThread().getName());
        } finally {
            lock.unlock();
        }
    }
}


public class Run {
    public static void main(String[] args) {

        final MyService service = new MyService(true);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("當前線程是" + Thread.currentThread().getName());
                service.serviceMethod();
            }
        };

        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            threads[i] = new Thread(runnable);

        }

        for (int i=0;i<10;i++){
            threads[i].start();
        }

    }
}

輸出結果

當前線程是Thread-0
得到鎖的線程名稱是Thread-0
當前線程是Thread-1
得到鎖的線程名稱是Thread-1
當前線程是Thread-2
得到鎖的線程名稱是Thread-2
當前線程是Thread-3
得到鎖的線程名稱是Thread-3
當前線程是Thread-4
得到鎖的線程名稱是Thread-4
當前線程是Thread-5
得到鎖的線程名稱是Thread-5
當前線程是Thread-6
得到鎖的線程名稱是Thread-6
當前線程是Thread-7
當前線程是Thread-8
得到鎖的線程名稱是Thread-7
得到鎖的線程名稱是Thread-8
當前線程是Thread-9
得到鎖的線程名稱是Thread-9

得到的線程基本呈現有序狀態

相關文章
相關標籤/搜索