java多線程總結-同步之ReentrantLock

1 ReentrantLock與synchronized對比

ReentrantLock與synchronized都是爲了同步加鎖,但ReentrantLock相對效率比synchronized高,量級較輕。
synchronized在JDK1.5版本開始,嘗試優化。到JDK1.7版本後,優化效率已經很是好了。在絕對效率上,不比reentrantLock差多少。使用ReentrantLock,必須手工釋放鎖標記。通常都是在finally代碼塊中定義釋放鎖標記的unlock方法。ide

2.示例用法

2.1 基本用法

lock()與unlock()就像synchronized同步代碼塊的開始與結束,使用ReentrantLocky必定要記得unlock()解鎖優化

package com.bernardlowe.concurrent.t03;

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

public class Test_01 {
    Lock lock = new ReentrantLock();
    
    void m1(){
        try{
            lock.lock(); // 加鎖
            for(int i = 0; i < 10; i++){
                TimeUnit.SECONDS.sleep(1);
                System.out.println("m1() method " + i);
            }
        }catch(InterruptedException e){
            e.printStackTrace();
        }finally{
            lock.unlock(); // 解鎖
        }
    }
    
    void m2(){
        lock.lock();
        System.out.println("m2() method");
        lock.unlock();
    }
    
    public static void main(String[] args) {
        final Test_01 t = new Test_01();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t.m1();
            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                t.m2();
            }
        }).start();
    }
}

2.2 嘗試鎖

嘗試鎖,顧名思義是嘗試獲取鎖標記trylock(),有兩種方式this

  • 無參嘗試鎖:會根據是否能獲取當前鎖標記返回對應值操作系統

    boolean tryLock();線程

  • 有參阻塞嘗試鎖, 阻塞嘗試鎖,阻塞參數表明等待超時時間。3d

    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;code

/**
 * 嘗試鎖
 */
package com.bernardlowe.concurrent.t03;

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

public class Test_02 {
    Lock lock = new ReentrantLock();
    
    void m1(){
        try{
            lock.lock();
            for(int i = 0; i < 10; i++){
                TimeUnit.SECONDS.sleep(1);
                System.out.println("m1() method " + i);
            }
        }catch(InterruptedException e){
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    }
    
    void m2(){
        boolean isLocked = false;
        try{
            // 嘗試鎖, 若是有鎖,沒法獲取鎖標記,返回false。
            // 若是獲取鎖標記,返回true
            // isLocked = lock.tryLock();
            
            // 阻塞嘗試鎖,阻塞參數表明的時長,嘗試獲取鎖標記。
            // 若是超時,不等待。直接返回。
            isLocked = lock.tryLock(5, TimeUnit.SECONDS); 
            
            if(isLocked){
                System.out.println("m2() method synchronized");
            }else{
                System.out.println("m2() method unsynchronized");
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            if(isLocked){
                // 嘗試鎖在解除鎖標記的時候,必定要判斷是否獲取到鎖標記。
                // 若是當前線程沒有獲取到鎖標記,會拋出異常。
                lock.unlock();
            }
        }
    }
    
    public static void main(String[] args) {
        final Test_02 t = new Test_02();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t.m1();
            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                t.m2();
            }
        }).start();
    }
}

2.3 可打斷

先解釋下線程的幾種狀態:
阻塞狀態: 包括普通阻塞,等待隊列,鎖池隊列。
普通阻塞: sleep(10000), 能夠被打斷。調用thread.interrupt()方法,能夠打斷阻塞狀態,拋出異常。
等待隊列: wait()方法被調用,也是一種阻塞狀態,只能由notify喚醒。沒法打斷
鎖池隊列: 沒法獲取鎖標記。不是全部的鎖池隊列均可被打斷blog

  • 使用ReentrantLock的lock方法,獲取鎖標記的時候,若是須要阻塞等待鎖標記,沒法被打斷。
  • 使用ReentrantLock的lockInterruptibly方法,獲取鎖標記的時候,若是須要阻塞等待,能夠被打斷。

示例代碼
主線程啓動了兩個線程t1,t2,t1調用m1(),t2調用m2()
當主線程調用interrupt()方法,能夠打斷t2線程的阻塞等待鎖,拋出異常隊列

package com.bernardlowe.concurrent.t03;

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

public class Test_03 {
    Lock lock = new ReentrantLock();
    
    void m1(){
        try{
            lock.lock();
            for(int i = 0; i < 5; i++){
                TimeUnit.SECONDS.sleep(1);
                System.out.println("m1() method " + i);
            }
        }catch(InterruptedException e){
            e.printStackTrace();
        }finally{
            lock.unlock();
        }
    }
    
    void m2(){
        try{
            lock.lockInterruptibly(); // 可嘗試打斷,阻塞等待鎖。能夠被其餘的線程打斷阻塞狀態
            System.out.println("m2() method");
        }catch(InterruptedException e){
            System.out.println("m2() method interrupted");
        }finally{
            try{
                lock.unlock();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        final Test_03 t = new Test_03();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t.m1();
            }
        }).start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                t.m2();
            }
        });
        t2.start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        t2.interrupt();// 打斷線程休眠。非正常結束阻塞狀態的線程,都會拋出異常。
    }
}

結果如圖

2.4 公平鎖

操做系統cpu,爲了保證效率,線程的執行機制是競爭機制,或者說是隨機機制,是不公平的,使用ReentrantLock實現公平鎖,是很是簡單的,只須要在建立ReentrantLock的時候傳一個參數ReentrantLock lock = new ReentrantLock(true);
示例代碼:
TestReentrantlock是公平鎖
TestSync是非公平鎖

/**
 * 公平鎖
 */
package com.bernardlowe.concurrent.t03;

import java.util.concurrent.locks.ReentrantLock;

public class Test_04 {
    
    public static void main(String[] args) {
        TestReentrantlock t = new TestReentrantlock();
        //TestSync t = new TestSync();
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        t1.start();
        t2.start();
    }
}

class TestReentrantlock extends Thread{
    // 定義一個公平鎖
    private static ReentrantLock lock = new ReentrantLock(true);
    public void run(){
        for(int i = 0; i < 5; i++){
            lock.lock();
            try{
                System.out.println(Thread.currentThread().getName() + " get lock");
            }finally{
                lock.unlock();
            }
        }
    }
    
}

class TestSync extends Thread{
    public void run(){
        for(int i = 0; i < 5; i++){
            synchronized (this) {
                System.out.println(Thread.currentThread().getName() + " get lock in TestSync");
            }
        }
    }
}

公平鎖結果:

非公平鎖結果:

相關文章
相關標籤/搜索