juc之Lock的學習

Lock接口的主要方法

//嘗試獲取鎖,超時就放棄當前鎖
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

//相當於把超時時間設置爲無限,在等待的過程中,線程可以被打斷
void lockInterruptibly() throws InterruptedException;

//注意不遵守公平原則,會獲取到鎖不管是否會有其它線程等待
boolean tryLock();



//最佳實踐
if (lock.tryLock()) {
        try {
          // manipulate protected state
        } finally {
          lock.unlock();
        }
     } 

可見性保證

happens-befor原則:在一個線程解鎖以後,另一個線程加鎖時可以看見之前解鎖線程的操作。

鎖的分類

公平鎖和非公平鎖

定義:公平指的是按照線程的請求順序,來分配鎖。非公平指的是,不完全按照線程的請求順序,在一定情況下可以插隊。

爲什麼有非公平鎖?

避免喚醒的空閒時間。

代碼示例:

package lock.reentrantlock;

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

/**
 * 描述:     演示公平和不公平兩種情況
 */
public class FairLock {

    public static void main(String[] args) {
        PrintQueue printQueue = new PrintQueue();
        Thread thread[] = new Thread[10];
        for (int i = 0; i < 10; i++) {
            thread[i] = new Thread(new Job(printQueue));
        }
        for (int i = 0; i < 10; i++) {
            thread[i].start();
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Job implements Runnable {

    PrintQueue printQueue;

    public Job(PrintQueue printQueue) {
        this.printQueue = printQueue;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "開始打印");
        printQueue.printJob(new Object());
        System.out.println(Thread.currentThread().getName() + "打印完畢");
    }
}

class PrintQueue {

    private Lock queueLock = new ReentrantLock(true);

    public void printJob(Object document) {
        queueLock.lock();
        try {
            int duration = new Random().nextInt(10) + 1;
            System.out.println(Thread.currentThread().getName() + "正在打印,需要" + duration);
            Thread.sleep(duration * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            queueLock.unlock();
        }

        queueLock.lock();
        try {
            int duration = new Random().nextInt(10) + 1;
            System.out.println(Thread.currentThread().getName() + "正在打印,需要" + duration+"秒");
            Thread.sleep(duration * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            queueLock.unlock();
        }
    }
}

共享鎖(讀鎖)和排它鎖 

讀寫鎖的規則:

要麼是多個或者一個線程持有讀鎖,要麼是一個線程有寫鎖,二者不會同時出現。

代碼示例:

package lock.readwrite;

import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 描述:     TODO
 */
public class CinemaReadWrite {

    private static ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
    private static ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
    private static ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();

    private static void read() {
        readLock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "得到了讀鎖,正在讀取");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + "釋放讀鎖");
            readLock.unlock();
        }
    }

    private static void write() {
        writeLock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "得到了寫鎖,正在寫入");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + "釋放寫鎖");
            writeLock.unlock();
        }
    }

    public static void main(String[] args) {
        new Thread(()->read(),"Thread1").start();
        new Thread(()->read(),"Thread2").start();
        new Thread(()->write(),"Thread3").start();
        new Thread(()->write(),"Thread4").start();
    }
}

讀寫鎖插隊策略

  • 公平鎖:不允許插隊
  • 非公平鎖:寫鎖可以隨時插隊,讀鎖僅在等待隊列的頭節點,不是想獲取寫鎖的線程時可以插隊

鎖的升降級:ReentrantReadWriteLock支持鎖的降級,獲取寫鎖的同時可以獲取讀鎖。

自旋鎖和阻塞鎖

自旋鎖的定義:如果物理機有多個cpu,能夠讓兩個或兩個以上的線程並行執行代碼,我們就可以讓後面那個請求的線程不放棄cpu的執行時間,看看持有鎖的線程是否很快釋放鎖。而爲了讓線程稍等一下,我們需要讓當前線程進行自旋。如果在自旋完成後前面鎖定的同步資源線程已經釋放了鎖,那麼當前線程就可以不必阻塞而是直接獲得同步資源。從而避免切換線程的開銷。

阻塞鎖的定義:如果線程沒有拿到鎖,會直接把線程阻塞,知道被喚醒

自旋鎖的缺點:自旋時間長消耗cpu。

鎖優化

jvm對鎖的優化:自旋鎖和自適應,鎖銷除

代碼層面優化:縮小同步代碼塊,不要鎖住方法,減少鎖的次數

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章