Java自學-多線程 Lock對象

多線程 Lock對象

與synchronized相似的,lock也可以達到同步的效果html

步驟 1 : 回憶 synchronized 同步的方式java

首先回憶一下 synchronized 同步對象的方式多線程

當一個線程佔用 synchronized 同步對象,其餘線程就不能佔用了,直到釋放這個同步對象爲止this

回憶 synchronized 同步的方式

package multiplethread;
   
import java.text.SimpleDateFormat;
import java.util.Date;
    
public class TestThread {
      
    public static String now(){
        return new SimpleDateFormat("HH:mm:ss").format(new Date());
    }
      
    public static void main(String[] args) {
        final Object someObject = new Object();
           
        Thread t1 = new Thread(){
            public void run(){
                try {
                    System.out.println( now()+" t1 線程已經運行");
                    System.out.println( now()+this.getName()+ " 試圖佔有對象:someObject");
                    synchronized (someObject) {
                           
                        System.out.println( now()+this.getName()+ " 佔有對象:someObject");
                        Thread.sleep(5000);
                        System.out.println( now()+this.getName()+ " 釋放對象:someObject");
                    }
                    System.out.println(now()+" t1 線程結束");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        };
        t1.setName(" t1");
        t1.start();
        Thread t2 = new Thread(){
   
            public void run(){
                try {
                    System.out.println( now()+" t2 線程已經運行");
                    System.out.println( now()+this.getName()+ " 試圖佔有對象:someObject");
                    synchronized (someObject) {
                        System.out.println( now()+this.getName()+ " 佔有對象:someObject");
                        Thread.sleep(5000);
                        System.out.println( now()+this.getName()+ " 釋放對象:someObject");
                    }
                    System.out.println(now()+" t2 線程結束");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        };
        t2.setName(" t2");
        t2.start();
    }
        
}

步驟 2 : 使用Lock對象實現同步效果spa

Lock是一個接口,爲了使用一個Lock對象,須要用到線程

Lock lock = new ReentrantLock();

synchronized (someObject) 相似的,lock()方法,表示當前線程佔用lock對象,一旦佔用,其餘線程就不能佔用了。 與 synchronized 不一樣的是,一旦synchronized 塊結束,就會自動釋放對someObject的佔用。 lock卻必須調用unlock方法進行手動釋放,爲了保證釋放的執行,每每會把unlock() 放在finally中進行。設計

使用Lock對象實現同步效果

package multiplethread;
 
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
public class TestThread {
 
    public static String now() {
        return new SimpleDateFormat("HH:mm:ss").format(new Date());
    }
 
    public static void log(String msg) {
        System.out.printf("%s %s %s %n", now() , Thread.currentThread().getName() , msg);
    }
 
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
 
        Thread t1 = new Thread() {
            public void run() {
                try {
                    log("線程啓動");
                    log("試圖佔有對象:lock");
 
                    lock.lock();
 
                    log("佔有對象:lock");
                    log("進行5秒的業務操做");
                    Thread.sleep(5000);
 
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    log("釋放對象:lock");
                    lock.unlock();
                }
                log("線程結束");
            }
        };
        t1.setName("t1");
        t1.start();
        try {
            //先讓t1飛2秒
            Thread.sleep(2000);
        } catch (InterruptedException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        Thread t2 = new Thread() {
 
            public void run() {
                try {
                    log("線程啓動");
                    log("試圖佔有對象:lock");
 
                    lock.lock();
 
                    log("佔有對象:lock");
                    log("進行5秒的業務操做");
                    Thread.sleep(5000);
 
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    log("釋放對象:lock");
                    lock.unlock();
                }
                log("線程結束");
            }
        };
        t2.setName("t2");
        t2.start();
    }
 
}

步驟 3 : trylock方法code

synchronized 是不佔用到手不罷休的,會一直試圖佔用下去。 與 synchronized 的鑽牛角尖不同,Lock接口還提供了一個trylock方法。 trylock會在指定時間範圍內試圖佔用,佔成功了,就啪啪啪。 若是時間到了,還佔用不成功,扭頭就走~orm

注意: 由於使用trylock有可能成功,有可能失敗,因此後面unlock釋放鎖的時候,須要判斷是否佔用成功了,若是沒佔用成功也unlock,就會拋出異常htm

trylock方法

package multiplethread;
 
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
public class TestThread {
 
    public static String now() {
        return new SimpleDateFormat("HH:mm:ss").format(new Date());
    }
 
    public static void log(String msg) {
        System.out.printf("%s %s %s %n", now() , Thread.currentThread().getName() , msg);
    }
 
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
 
        Thread t1 = new Thread() {
            public void run() {
                boolean locked = false;
                try {
                    log("線程啓動");
                    log("試圖佔有對象:lock");
 
                    locked = lock.tryLock(1,TimeUnit.SECONDS);
                    if(locked){
                        log("佔有對象:lock");
                        log("進行5秒的業務操做");
                        Thread.sleep(5000);
                    }
                    else{
                        log("通過1秒鐘的努力,尚未佔有對象,放棄佔有");
                    }
 
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                     
                    if(locked){
                        log("釋放對象:lock");
                        lock.unlock();
                    }
                }
                log("線程結束");
            }
        };
        t1.setName("t1");
        t1.start();
        try {
            //先讓t1飛2秒
            Thread.sleep(2000);
        } catch (InterruptedException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        Thread t2 = new Thread() {
 
            public void run() {
                boolean locked = false;
                try {
                    log("線程啓動");
                    log("試圖佔有對象:lock");
 
                    locked = lock.tryLock(1,TimeUnit.SECONDS);
                    if(locked){
                        log("佔有對象:lock");
                        log("進行5秒的業務操做");
                        Thread.sleep(5000);
                    }
                    else{
                        log("通過1秒鐘的努力,尚未佔有對象,放棄佔有");
                    }
 
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                     
                    if(locked){
                        log("釋放對象:lock");
                        lock.unlock();
                    }
                }
                log("線程結束");
            }
        };
        t2.setName("t2");
        t2.start();
    }
 
}

步驟 4 : 線程交互

使用synchronized方式進行線程交互,用到的是同步對象的wait,notify和notifyAll方法

Lock也提供了相似的解決辦法,首先經過lock對象獲得一個Condition對象,而後分別調用這個Condition對象的:await, signal,signalAll 方法

注意: 不是Condition對象的wait,nofity,notifyAll方法,是await,signal,signalAll

線程交互

package multiplethread;
  
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
  
public class TestThread {
  
    public static String now() {
        return new SimpleDateFormat("HH:mm:ss").format(new Date());
    }
  
    public static void log(String msg) {
        System.out.printf("%s %s %s %n", now() , Thread.currentThread().getName() , msg);
    }
  
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
         
        Thread t1 = new Thread() {
            public void run() {
                try {
                    log("線程啓動");
                    log("試圖佔有對象:lock");
  
                    lock.lock();
  
                    log("佔有對象:lock");
                    log("進行5秒的業務操做");
                    Thread.sleep(5000);
                    log("臨時釋放對象 lock, 並等待");
                    condition.await();
                    log("從新佔有對象 lock,並進行5秒的業務操做");
                    Thread.sleep(5000);
  
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    log("釋放對象:lock");
                    lock.unlock();
                }
                log("線程結束");
            }
        };
        t1.setName("t1");
        t1.start();
        try {
            //先讓t1飛2秒
            Thread.sleep(2000);
        } catch (InterruptedException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        Thread t2 = new Thread() {
  
            public void run() {
                try {
                    log("線程啓動");
                    log("試圖佔有對象:lock");
  
                    lock.lock();
  
                    log("佔有對象:lock");
                    log("進行5秒的業務操做");
                    Thread.sleep(5000);
                    log("喚醒等待中的線程");
                    condition.signal();
  
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    log("釋放對象:lock");
                    lock.unlock();
                }
                log("線程結束");
            }
        };
        t2.setName("t2");
        t2.start();
    }
  
}

步驟 5 : 總結Lock和synchronized的區別

  1. Lock是一個接口,而synchronized是Java中的關鍵字,synchronized是內置的語言實現,Lock是代碼層面的實現。

  2. Lock能夠選擇性的獲取鎖,若是一段時間獲取不到,能夠放棄。synchronized不行,會一根筋一直獲取下去。 藉助Lock的這個特性,就可以規避死鎖,synchronized必須經過謹慎和良好的設計,才能減小死鎖的發生。

  3. synchronized在發生異常和同步塊結束的時候,會自動釋放鎖。而Lock必須手動釋放, 因此若是忘記了釋放鎖,同樣會形成死鎖。

練習多線程 Lock對象

當多個線程按照不一樣順序佔用多個同步對象的時候,就有可能產生死鎖現象。

死鎖之因此會發生,就是由於synchronized 若是佔用不到同步對象,就會苦苦的一直等待下去,藉助tryLock的有限等待時間,解決死鎖問題

答案 : 在這裏插入圖片描述

package multiplethread;
  
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
  
public class TestThread {
  
    public static void main(String[] args) throws InterruptedException {
        Lock lock_ahri = new ReentrantLock();
        Lock lock_annie = new ReentrantLock();
  
        Thread t1 = new Thread() {
            public void run() {
                // 佔有九尾妖狐
                boolean ahriLocked = false;
                boolean annieLocked = false;
                  
                try {
                    ahriLocked = lock_ahri.tryLock(10, TimeUnit.SECONDS);
                    if (ahriLocked) {
                        System.out.println("t1 已佔有九尾妖狐");
                        // 停頓1000秒,另外一個線程有足夠的時間佔有安妮
                        Thread.sleep(1000);
                        System.out.println("t1 試圖在10秒內佔有安妮");
                        try {
                            annieLocked = lock_annie.tryLock(10, TimeUnit.SECONDS);
                            if (annieLocked)
                                System.out.println("t1 成功佔有安妮,開始啪啪啪");
                            else{
                                System.out.println("t1 總是佔用不了安妮,放棄");
                            }
  
                        } finally {
                            if (annieLocked){
                                System.out.println("t1 釋放安妮");
                                lock_annie.unlock();
                            }
                        }
  
                    }
                } catch (InterruptedException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                } finally {
                    if (ahriLocked){
                        System.out.println("t1 釋放九尾狐");
                        lock_ahri.unlock();
                    }
                }
  
            }
        };
        t1.start();
          
        Thread.sleep(100);
          
        Thread t2 = new Thread() {
            public void run() {
                boolean annieLocked = false;
                boolean ahriLocked = false;
                                  
                try {annieLocked = lock_annie.tryLock(10, TimeUnit.SECONDS);
                  
                if (annieLocked){
                      
                        System.out.println("t2 已佔有安妮");
                        // 停頓1000秒,另外一個線程有足夠的時間佔有安妮
                        Thread.sleep(1000);
                        System.out.println("t2 試圖在10秒內佔有九尾妖狐");
                        try {
                            ahriLocked = lock_ahri.tryLock(10, TimeUnit.SECONDS);
                            if (ahriLocked)
                                System.out.println("t2 成功佔有九尾妖狐,開始啪啪啪");
                            else{
                                System.out.println("t2 總是佔用不了九尾妖狐,放棄");
                            }
                        }
                        finally {
                            if (ahriLocked){
                                System.out.println("t2 釋放九尾狐");
                                lock_ahri.unlock();
                            }
                                  
                        }
  
                    }
                } catch (InterruptedException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                } finally {
                    if (annieLocked){
                        System.out.println("t2 釋放安妮");
                        lock_annie.unlock();
                    }
                          
                }
            }
        };
        t2.start();
      
    }
}
相關文章
相關標籤/搜索