我是靠谱客的博主 优雅皮带,最近开发中收集的这篇文章主要介绍公平锁/非公平锁/可重入锁/递归锁/自旋锁,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

公平和非公平锁

  • 公平锁:是指多个线程按照申请锁的顺序获取锁。先来后到。
  • 非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁,在高并发下,有可能会造成优先级反转或者饥饿现象。

非公平锁的优点在于吞吐量比公平锁大。
ReentrantLock默认是非公平锁

Lock lock = new ReentrantLock();
public ReentrantLock() {
        sync = new NonfairSync();
    }

其有两个构造器。无参构造会创建一个非公平锁。
有参构造的参数是一个boolean值,true是公平锁,false是非公平锁(和无参构造一样)。

public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
}

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

可重入锁(递归锁)

可重入锁(也叫递归锁)。
指的是线程获取锁之后,可以再次获取锁而不会发生死锁。

也就是获取锁之后进入method1方法,调用的method2方法(此时method2也有锁),只不过method1和method2是一把锁。

举个现实生活中的例子:到家时用钥匙打开门后,就可以直接进入厨房或者卫生间。而不需要在用其他钥匙。

public sync method1(){
	method2();
}
public sync method2(){}

可重入锁最大的作用就是解决死锁问题。

ReentrantLock和synchronized都是可重入锁。

代码演示

此代码借用了博主https://blog.csdn.net/w8y56f/article/details/89554060的代码。
synchronized

package locks;

/*
*   可重入锁(也叫递归锁)
*   是指同一线程中,外层函数获取锁之后,在内层递归函数仍然能获取该锁的代码。
*   在同一线程在外层获取锁的时候,在进入内层方法会自动获取锁。
*
*   也就是说,线程可以进入任何一个它拥有锁所同步的代码块。
*
* */
public class ReentrantLockDemo {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (this){
                    System.out.println("第1次获取锁,这个锁是:"+this);
                    int index = 1;
                    while (true){
                        synchronized (this){
                            System.out.println("第"+(++index)+"次获取锁,这个锁是:"+this);
                        }
                        if(index == 10){
                            break;
                        }
                    }
                }
            }
        }).start();
    }
}

可以看到,同一个线程可以多次获取锁,而且没有发生死锁。
在这里插入图片描述

ReentrantLock

package locks;

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

public class ReentrantLockDemo2 {

    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();

        new Thread(new Runnable() {
            @Override
            public void run() {
                try{
                    lock.lock();
                    System.out.println("第1次获取锁,这个锁是:"+lock);

                    int index = 1;
                    while (true){

                        try{
                            lock.lock();
                            System.out.println("第"+(++index)+"次获取锁,这个锁是:"+lock);

                            try{
                                Thread.sleep(new Random().nextInt(200));
                            }catch (Exception e){
                                e.printStackTrace();
                            }

                            if(index == 10){
                                break;
                            }

                        }finally {
                            lock.unlock();
                        }

                    }
                }finally {
                    lock.unlock();
                }
            }
        }).start();
    }

}

在这里插入图片描述
注意:使用ReentrantLock,加锁和解锁必须是成对出现的。

正常情况

package locks;

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

public class T1 implements Runnable{
    Lock lock = new ReentrantLock();

    public void get(){
        lock.lock();
        lock.lock();
        try{
            System.out.println(Thread.currentThread().getName()+"t invoked get()");
            set();
        }finally {
            lock.unlock();
            lock.unlock();
        }
    }

    public void set(){

        lock.lock();

        try{
            System.out.println(Thread.currentThread().getName()+"t invoked set()");
        }finally {
            lock.unlock();
        }

    }

    @Override
    public void run() {
        get();
    }

    public static void main(String[] args) {

        T1 t1 = new T1();

        Thread t3 = new Thread(t1);
        Thread t4 = new Thread(t1);
        t3.start();
        t4.start();
    }

}

在这里插入图片描述

加锁比解锁多,会导致t4线程卡死,也就是说多出的lock,还没有释放锁,自然而然t4会一直等待。

package locks;

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

public class T1 implements Runnable{
    Lock lock = new ReentrantLock();

    public void get(){
        lock.lock();
        lock.lock();
        try{
            System.out.println(Thread.currentThread().getName()+"t invoked get()");
            set();
        }finally {
            //lock.unlock();
            lock.unlock();
        }
    }

    public void set(){

        lock.lock();

        try{
            System.out.println(Thread.currentThread().getName()+"t invoked set()");
        }finally {
            lock.unlock();
        }

    }

    @Override
    public void run() {
        get();
    }

    public static void main(String[] args) {

        T1 t1 = new T1();

        Thread t3 = new Thread(t1);
        Thread t4 = new Thread(t1);
        t3.start();
        t4.start();
    }

}

在这里插入图片描述
解锁比加锁多:会直接运行出错。

package locks;

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

public class T1 implements Runnable{
    Lock lock = new ReentrantLock();

    public void get(){
        lock.lock();
       // lock.lock();
        try{
            System.out.println(Thread.currentThread().getName()+"t invoked get()");
            set();
        }finally {
            lock.unlock();
            lock.unlock();
        }
    }

    public void set(){

        lock.lock();

        try{
            System.out.println(Thread.currentThread().getName()+"t invoked set()");
        }finally {
            lock.unlock();
        }

    }

    @Override
    public void run() {
        get();
    }

    public static void main(String[] args) {

        T1 t1 = new T1();

        Thread t3 = new Thread(t1);
        Thread t4 = new Thread(t1);
        t3.start();
        t4.start();
    }

}

在这里插入图片描述

自旋锁

自旋锁指的是尝试获取锁的线程不会立即阻塞,而是会通过循环不断的去尝试获取锁。这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。

代码验证

package locks;

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

public class SpinDemo {
    //原子引用线程  初始值null
    AtomicReference<Thread> reference = new AtomicReference<>();


    public void mylock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"t come in ^_^");

        //如果当前没有线程占用锁,则自己占用。 atomicInteger中cas思想
        while (!reference.compareAndSet(null,thread)){

            try {
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+"t 自旋");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


        }

    }

    public void myunlock(){

        Thread thread = Thread.currentThread();
        //解锁操作,如果自己用完锁,则替换为null
        reference.compareAndSet(thread,null);
        System.out.println(Thread.currentThread().getName()+"t invoked myunlock");

    }

    public static void main(String[] args) {

        SpinDemo spinDemo = new SpinDemo();

        new Thread(()->{

            spinDemo.mylock();

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

            spinDemo.myunlock();

        },"t1").start();

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

        new Thread(()->{
            spinDemo.mylock();
            spinDemo.myunlock();
        },"t2").start();

    }




}

在这里插入图片描述
t1在占用锁的时间里,t2一直会尝试获取锁。

最后

以上就是优雅皮带为你收集整理的公平锁/非公平锁/可重入锁/递归锁/自旋锁的全部内容,希望文章能够帮你解决公平锁/非公平锁/可重入锁/递归锁/自旋锁所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(52)

评论列表共有 0 条评论

立即
投稿
返回
顶部