公平锁和非公平锁&可重入锁&自旋锁&独占锁/共享锁

2019-12-30  本文已影响0人  霓裳梦竹

文章同步更新在个人公众号“梓莘”,欢迎大家关注,相互交流。

公平锁和非公平锁

公平锁:是指多个线程按照申请锁的顺序来获取锁,也就是遵循先来后到

非公平锁:是指多个线程获取锁的顺序并不是安装申请锁的顺序,有可能后申请锁的线程优先获得锁,在高并发环境下,有可能造成优先级反转或者饥饿现象。非公平就是允许加塞

在并发包ReentrantLock的创建可以执行构造函数的boolean类型来得到公平锁和非公平锁,默认是非公平锁。

区别:

公平锁:Threads acquire a fair lock in the order in which they required it

公平锁,就是很公平,在并发环境下,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个,就占有锁,否则就会加入到等待队列中,以后会安装FIFO的规则从队列充取到自己。

非公平锁:a nonfair lock permits barging:threads requesting a lock can jump ahead of the qyeye of waiting threads if the lock happend to be available when it is requested.

非公平锁比较野蛮,上来就直接尝试占有锁,如果尝试失败,就再采用类似公平锁那种方式

对于Java ReentrantLock而言:

通过构造函数指定该锁是否是公平锁,默认是非公平锁,非公平锁的优点在于吞吐量比公平锁大。

对于synchronized而言,也是一种非公平锁。

   /**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
  public ReentrantLock() {
        sync = new NonfairSync();
    }

/**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
 public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

可重入锁

指的是同一线程外层函数获得锁之后,内层递归函数仍然能获得该锁的代码,在同一个线程在外层方法获得锁的时候,在进入内层方法会自动获取锁。也就是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块。

package com.zixin;

class Photo{
    public synchronized void sendSms() throws Exception{
        System.out.println(Thread.currentThread().getId()+"  invoked sendSMS");
        sendEmail();
    }

    public synchronized void sendEmail() throws Exception{
        System.out.println(Thread.currentThread().getId()+"  invoked sendEmail");

    }
}
/**
 * @ClassName ReenterLockDemo
 * @Description 指的是同一线程外层函数获得锁之后,内层递归函数仍然能获得该锁的代码,在同一个线程在外层方法获得锁的时候,在进入内层方法会自动获取锁。也就是锁,线程可以进入任何一个它已经拥有的锁所同步着的代码块。
 * @Author zishen
 * @Date 2019/12/30 9:36
 * @Version 1.0
 **/
public class ReenterLockDemo {

    /**
     * 11  invoked sendSMS
     * 11  invoked sendEmail
     * 10  invoked sendSMS
     * 10  invoked sendEmail
     * @param args
     */
    public static void main(String[] args) {
        Photo p = new Photo();
        new Thread(()->{
            try {
                p.sendSms();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"t1").start();
        new Thread(()->{
            try {
                p.sendSms();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"t2").start();
    }

}

自旋锁

尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。

package com.zixin;

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

/**
 * @ClassName SpinLockDemo
 * @Description 手写一个自旋锁
 * @Author zixin
 * @Date 2019/12/30 10:34
 * @Version 1.0
 **/
public class SpinLockDemo {
    //原子引用线程
    AtomicReference<Thread> atomicReference = new AtomicReference<Thread>();
    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+" come in");
        while(!atomicReference.compareAndSet(null,thread)){

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

    /**
     * AA come in
     * BB come in
     * AA invoked myUnLock
     * BB invoked myUnLock
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {

        SpinLockDemo spinLockDemo = new SpinLockDemo();
        new Thread(()->{
            spinLockDemo.myLock();
            try {
                TimeUnit.SECONDS.sleep(5);
                spinLockDemo.myUnLock();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"AA").start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{
            spinLockDemo.myLock();
            spinLockDemo.myUnLock();
        },"BB").start();
    }
}

独占锁/共享锁

独占锁:指该锁一次只能被一个线程所持有,对ReentrantLock和Synchronized而言都是独占锁、

共享锁:指该锁可被多个线程多持有。

对ReentrantReadWriteLock其读锁是共享锁,其写锁是独占锁。

读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程是互斥的。

package com.zixin;

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

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

    private ReentrantReadWriteLock rwlock = new ReentrantReadWriteLock();
    public void put(String key,Object value){
        rwlock.writeLock().lock();

        try {
            System.out.println(Thread.currentThread().getName()+" 正在写入:"+key);
            TimeUnit.MILLISECONDS.sleep(500);
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+" 写入完成:"+key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            rwlock.writeLock().unlock();
        }

    }
    public void get(String key){
        rwlock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName()+" 正在读取:"+key);
            TimeUnit.MILLISECONDS.sleep(500);
            Object result = map.get(key);
            System.out.println(Thread.currentThread().getName()+" 读取完成:"+result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            rwlock.readLock().unlock();
        }

    }
    public void clearMap(){
        map.clear();
    }
}
/**
 * @ClassName ReadWriteLockDemo
 * @Description 读写锁
 * @Author zixin
 * @Date 2019/12/30 11:38
 * @Version 1.0
 **/
public class ReadWriteLockDemo {

    /**
     * 0 正在写入:0
     * 0 写入完成:0
     * 1 正在写入:1
     * 1 写入完成:1
     * 2 正在写入:2
     * 2 写入完成:2
     * 3 正在写入:3
     * 3 写入完成:3
     * 4 正在写入:4
     * 4 写入完成:4
     * 0 正在读取:0
     * 1 正在读取:1
     * 2 正在读取:2
     * 3 正在读取:3
     * 4 正在读取:4
     * 1 读取完成:1
     * 2 读取完成:2
     * 0 读取完成:0
     * 3 读取完成:3
     * 4 读取完成:4
     * @param args
     */
    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+"");
            },String.valueOf(i)).start();
        }

        for (int i = 0; i <5 ; i++) {
            final int tempInt = i;
            new Thread(()->{
                myCache.get(tempInt+"");
            },String.valueOf(i)).start();
        }
    }
}

上一篇下一篇

猜你喜欢

热点阅读