1. 程式人生 > 實用技巧 >java基礎----各種型別的鎖

java基礎----各種型別的鎖

公平鎖:公平鎖就是執行緒遵循先來後到的順序執行。

非公平鎖:非公平鎖就是執行緒會首先嚐試搶奪鎖,如果能搶到,則執行,如果搶不到,則按照公平鎖的規則進行等待。(注意,reentrantLock如果初始化的時候不傳參的話,預設是false,也就是非公平鎖模式,而synchronize也是一個非公平鎖

可重入鎖:外層函式獲取到鎖後,可以在內層函式中自動獲取對應的鎖,例如:

import java.util.concurrent.locks.ReentrantLock;

public class ReentryDemo {
    private ReentrantLock lock = new ReentrantLock();

    public void print1(){
        lock.lock();
        System.out.println("this is method1");
        print2();
    }

    public void print2(){
        lock.lock();
        System.out.println("this is method2");
    }

    public static void main(String[] args) {
        ReentryDemo reentryDemo = new ReentryDemo();
        reentryDemo.print1();
    }
}

自旋鎖:是指嘗試獲取鎖的執行緒不會立即阻塞,而是採用迴圈的方式嘗試去獲取鎖,這樣的好處是減少執行緒上下文切換的消耗,缺點是迴圈會消耗CPU。下面提供一個程式碼

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

public class SpinDemo {

    AtomicReference<Thread> atomicReference = new AtomicReference<Thread>();

    public void myLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "\tcome into");
        while (!atomicReference.compareAndSet(null, thread))
        {
            try {
                System.out.println(Thread.currentThread().getName() + "\tspinning");
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + "\tget lock");
    }

    public void myUnLock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "\trelease lock");
        atomicReference.compareAndSet(thread,null);
    }

    public static void main(String[] args) throws InterruptedException {
        SpinDemo spinDemo = new SpinDemo();

        new Thread(()->{
            spinDemo.myLock();
            try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); }
            spinDemo.myUnLock();
        },"a").start();

        TimeUnit.SECONDS.sleep(1);

        new Thread(()->{
            spinDemo.myLock();
            spinDemo.myUnLock();
        },"b").start();
    }
}

  執行結果:  

    a come into
    a get lock
    b come into
    b spinning
    b spinning
    b spinning
    b spinning
    a release lock
    b get lock
    b release lock

  可以看到,這裡面b執行緒嘗試去獲取鎖的時候,會判斷鎖資源是否有別的執行緒在佔用,如果有別的執行緒在佔用,則會在一個迴圈裡面,反覆地嘗試去獲取鎖。這樣做,執行緒b不會被掛起,減少了上下文切換的開銷,但是假如執行緒b長時間內都獲取不到鎖,那麼他就會不斷地在那裡自旋,這樣反而會增加cpu的消耗,因此在是用自旋鎖的時候,往往需要設定一個自旋時間,當執行緒自旋超過了限定的時間,則需要自動退出來,而這個時間的閾值該怎麼設定,其實就是根據執行緒上下文切換的時間來定。

獨佔鎖:指該鎖只能被一個執行緒鎖持有,對reentrantLock和Synchronized而言都是獨佔鎖。

共享鎖:指該鎖可以被多個執行緒鎖持有,reentrantReadWriteLock的讀是共享鎖,寫鎖是獨佔鎖。

關於讀寫鎖的一個應用場景,下面給出一個例子

import sun.awt.windows.ThemeReader;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

class MyCache {
    private volatile Map<String,Object> map = new HashMap<String, Object>();
    private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();

    public void put(String key, Object value)
    {
        rwLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t 正在寫入:" + key);
            try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
            map.put(key,value);
            System.out.println(Thread.currentThread().getName() + "\t 寫入完成:" + key);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rwLock.writeLock().unlock();
        }
    }

    public void read(String key){
        rwLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t 正在讀取:" + key);
            try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); }
            map.get(key);
            System.out.println(Thread.currentThread().getName() + "\t 讀取完成:" + key);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rwLock.readLock().unlock();
        }
    }
}


public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();
        for(int i = 0; i < 5; i++){
            final int tempInt = i;
            new Thread(()->{
                myCache.put(tempInt+"",tempInt+"");
            },"write"+i).start();
        }
        for(int i = 0; i < 5; i++){
            final int tempInt = i;
            new Thread(()->{
                myCache.read(tempInt+"");
            },"read"+i).start();
        }
    }
}