java基礎之線程 Lock接口

把java基礎擼一邊,從簡單的開始。html

線程部分:java

特別說明來源:

抄了的網站:java併發編程-Lock
編程

Lock的須要

在java中,已經有了synchronize來當鎖,爲何還要Lock呢?bash

被synchronize修飾的方法,當一個線程得到了對應的鎖以後,並執行該代碼塊時,其餘線程只能一直等待,其餘線程獲取鎖的狀況會有三種。併發

1:執行完該代碼塊,而後線程釋放對鎖的佔有jvm

2:線程執行發生異常,此時jvm會讓線程自動釋放鎖ide

3:這個主要是在等待喚醒機制裏面的wait()方法工具

那麼若是這個獲取鎖的線程要等待IO或其餘被阻塞了,但沒有釋放,其餘線程只能乾巴巴地等。這樣會影響效率,所以咱們須要不論程序的代碼塊執行的如何最終都將鎖對象進行釋放,方便其餘線程的執行性能

這個時候就須要用到lock優化

1:Lock不是java的語音內置的,synchronize是java的關鍵字,所以是內置特性。Lock是一個類,經過這個類能夠實現同步方法

2:synchronize是在JVM層面上實現的,不但能夠經過一些監控工具監控synchronize的鎖定,並且在代碼執行出現異常,jvm會自動釋放鎖定,但lock則不行,lock是經過代碼執行的,要保證鎖必定會被釋放,就必須將nuLock放到finally中

3:在資源競爭不是很激烈的狀況下,synchronize的性能要優於ReetranLock,但在資源競爭很激烈的狀況下,Synchronize的性能會降低幾十倍

Lock的使用

Lock是一個抽象類,接口Lock的類有,ReentrantLock 可重入鎖,ReadWriteLock讀寫鎖,

ReentantReadWriteLock可重入讀寫鎖,StampedLock

Lock的方法:

//獲取鎖
void lock();
//獲取鎖的過程能響應中斷
void lockInterruptibly() throws InterruptedException;
//獲取鎖返回true,不然返回false
boolean tryLock();
//超時獲取鎖,在規定時間未獲取到鎖返回false
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
釋放鎖
void unlock();
//獲取與lock綁定的等待通知組件,在前現場必須先得到了鎖才能等待,等待會釋放鎖,
//再次獲取到鎖才能從等待中返回
Condition newCondition();

複製代碼

ReentrantLock使用

在實現類中,用的比較多的是ReentrantLock。下面對這個類的使用作個介紹

public class Demo51  {

    Lock lock = new ReentrantLock(); //建立這個類的失戀,保證惟一
    public void insert(Thread thread) {
        if (lock.tryLock()){ //獲取鎖
            try {
                System.out.println(thread.getName()+"獲得了鎖");
            } catch (Exception e) {
                // TODO: handle exception
            }finally {
                System.out.println(thread.getName()+"釋放了鎖");
                lock.unlock(); //記得釋放鎖
            }
        }else {
            System.out.println(thread.getName()+"獲取鎖失敗");
        }
    }

    public static void main(String[] age){
        final Demo51 test = new Demo51();

        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();

        new Thread(){
            public void run() {
                test.insert(Thread.currentThread());
            };
        }.start();
    }

}複製代碼

打印

Thread-0獲得了鎖
Thread-0釋放了鎖
Thread-1獲得了鎖
Thread-1釋放了鎖
複製代碼

這裏使用了兩個方法

tryLock和unlock 一個獲取,一個釋放,有獲取就必定要有釋放。這個和synchronize不同,當執行完方法的時候會自動釋放,讓下個線程獲取鎖,但這個必須代碼

public class Demo52 {

    private Lock lock = new ReentrantLock();
    public static void main(String[] args)  {
        Demo52 test = new Demo52();
        MyThread thread1 = new MyThread(test);
        MyThread thread2 = new MyThread(test);
        thread1.start();
        thread2.start();

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread2.interrupt();
    }

    public void insert(Thread thread) throws InterruptedException{
        lock.lockInterruptibly();   //注意,若是須要正確中斷等待鎖的線程,必須將獲取鎖放在外面,而後將InterruptedException拋出
        try {
            System.out.println(thread.getName()+"獲得了鎖");
            long startTime = System.currentTimeMillis();
            for(int i = 0 ;i < 20 ; i ++     ) {
                Thread.sleep(200);
                if(System.currentTimeMillis() - startTime >= Integer.MAX_VALUE)
                    break;
                //插入數據
            }
        }
        finally {
            System.out.println(Thread.currentThread().getName()+"執行finally");
            lock.unlock();
            System.out.println(thread.getName()+"釋放了鎖");
        }
    }

    static class MyThread extends Thread{
        private Demo52 demo52 = null;
        public MyThread(Demo52 demo52){
            this.demo52 = demo52;
        }
        @Override
        public void run() {
            try {
                demo52.insert(Thread.currentThread());
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName()+"被中斷");
            }
        }
    }
}複製代碼

打印

Thread-0獲得了鎖
Thread-1被中斷
Thread-0執行finally
Thread-0釋放了鎖
複製代碼

在Thread-0拿到鎖以後,Thread-1判斷,若是鎖被佔用,會中斷

(還有幾個java原生實現了Lock的接口。後續再補吧!給本身留的做業)

手寫實現Lock接口類

目標:

1:實現獲取鎖lock()和釋放unlock()方法

2:實現重入鎖

初步實現

實現Lock抽象類

public class MyLock5 implements Lock {

    private boolean isLock = false ;

    @Override
    public synchronized void lock() {
        while (isLock){
            try {
                wait(); //等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        isLock = true ;
    }

    @Override
    public synchronized void unlock() {
                notify(); //喚醒
                isLock = false ;
    }
}複製代碼

在lock()方法裏面對進入的線程進行判斷,若是已經有線程進入會是isLock會是true,進入wait等待狀態中。執行unlock方法後isLock 爲true 這樣就能夠不須要等待去執行方法

public class Demo54 {

    private Lock lock = new MyLock5();

    int value = 0 ;

    public int getNext(){
        lock.lock();
        value++ ;
        lock.unlock();
        return value ;

    }

    public static void main(String[] age){
        Demo54 demo54 = new Demo54();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (;;)
                System.out.println(""+demo54.getNext());
            }
        }).start();
    }

}複製代碼

打印:

235842
235843
235844
235845
235846
235847
235848
235849
235850
235851
235852
複製代碼

能夠順序執行,沒有出現問題

可重入鎖

若是重入鎖呢

public void a(){
    lock.lock();
    System.out.println("a");
    b();
    lock.unlock();
}

public void b(){
    lock.lock();
    System.out.println("b");
    lock.unlock();
}複製代碼

在線程裏執行a()方法

執行結果。打印完a以後就進入了等待。這個時候須要對MyLock進行優化

public class MyLock5 implements Lock {

    private boolean isLock = false ;

    private Thread nowThread = null ;

    private int threadNumber = 0 ;

    @Override
    public synchronized void lock() {
        Thread thread = Thread.currentThread();
        while (isLock && thread != nowThread ){
            try {
                wait(); //等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        isLock = true ;
        nowThread = thread ;
        threadNumber++ ;
    }
    @Override
    public synchronized void unlock() {
        if (nowThread == Thread.currentThread()){
            threadNumber-- ;
            if (threadNumber == 0){
                notify(); //喚醒
                isLock = false ;
            }
        }
    }
}
複製代碼

1:判斷是不是同一個線程,若是是同一個線程的話進入等待狀態,若是不是,繼續執行

2:對線程進行計數

執行結果。a 方法執行完以後 b也執行

相關文章
相關標籤/搜索