Java公平锁非公平锁以及可重入锁和自旋锁解析举例

125 阅读3分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

按公平程度区分 公平锁和非公平锁

公平锁

是指多个线程按照申请锁的顺序来获取锁,类似排队打饭,先来后到。

非公平锁

是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁在高并发的情况下,有可能会造成优先级反转或者饥饿现象 两者之前区别

公平锁/非公平锁 并发包中ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或非公平锁,默认是非公平锁

关于两者区别:

公平锁:

Threads acquire a fair lock in the order in which they requested it

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

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

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

synchronized也是非公平锁

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

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

可重入锁(递归锁)

可重入锁(也叫做递归锁)

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

case one

synchronized就是一个典型的可重入锁

t1 invoked s sencdSMS() t1线程在外层方法获取锁的时候

t1 ####invoked sendEmail() t1 在进入内层方法会自动获取锁

t2 invoked s sendSMS()

t2 ######invoked sendEmail()

case two Reentrantlock就是一个典型的可重入锁

lock锁必须成对出现,有闭必有开,几个无所谓

    package com.wsx.reentrantLock;

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

    class ThreadDemo implements Runnable{
        public synchronized void method1(){
            System.out.println(Thread.currentThread().getName()+"!!!!!!!!method1");
            method2();
        }
        public synchronized void method2(){
            System.out.println(Thread.currentThread().getName()+"!!!!!!!!method2");
        }

        //实现runnaable接口,然后new里面先找run()方法
        @Override
        public void run() {
            get();
        }

        Lock lock = new ReentrantLock();

        public void get(){
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName()+"!!!!!!get()");
                set();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        public void set(){
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName()+"!!!!!!!set()");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public class ReenterLockDemo {
        public static void main(String[] args) {
            ThreadDemo threadDemo = new ThreadDemo();
            new Thread(()->{
                threadDemo.method1();
            },"t1").start();

            new Thread(()->{
                threadDemo.method1();
            },"t2").start();

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //实现runnaable接口,然后new里面先找run()方法
            Thread t3 = new Thread(threadDemo);
            Thread t4 = new Thread(threadDemo);

            t3.start();
            t4.start();
        }

    }

自旋锁

循环调用,循环访问,循环探查

自旋锁(spinlock)

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

好处:不用阻塞

缺点:长时间不能获得,循环会变重,性能会下降

    package com.wsx.spinLock;

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

    class resources{
        //原子引用线程
        AtomicReference<Thread> atomicReference = new AtomicReference<>();

        public void mylock(){
            //获取当前线程
            Thread thread = Thread.currentThread();
            System.out.println(Thread.currentThread().getName()+"!!!!!come in");
            while (!atomicReference.compareAndSet(null,thread)){
                System.out.println(Thread.currentThread().getName()+"waiting");
            }
        }

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

    public class SpinLockDemo {
        public static void main(String[] args) {
            resources resources = new resources();

            new Thread(()->{
                resources.mylock();
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                resources.unmylock();
            },"AA").start();

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            new Thread(()->{
                resources.mylock();
                resources.unmylock();
            },"BB").start();

        }
    }