Java中synchronize与ReentrantLock两种锁特点介绍

目录

一、两种锁特性总览:

二、锁特点介绍:

2.1 隐式和显式

2.2  重入锁

2.3  公平锁和非公平锁


一、两种锁特性总览:

synchronize:隐式锁(内置锁),重量级锁,重入锁,非公平锁;

ReentrantLock:显式锁,轻量级锁,重入锁,具有公平锁和非公平锁两种方式;

二、锁特点介绍:

2.1 隐式和显式

隐式锁:程序能够自动获取锁和释放锁,无需手动创建和删除,在非逻辑问题的情况下,是不会出现死锁问题的。

synchronize修饰普通方法:

public synchronized void method() { }

synchronize修饰静态方法:

public synchronized static void method() {}

synchronize修饰代码块:

synchronized(this) {}

synchronize修饰一个类:

synchronized(ClassName.class){}

显式锁:手动创建或释放锁,如果没释放锁,可能导致死锁问题。

// 创建显式锁
ReentrantLock lock = new ReentrantLock();

// 上锁
lock.lock();

// 释放锁
lock.unlock();

2.2  重入锁

概念:重入锁也可称为递归锁,意思是当同一线程的外部函数获得了某个锁,其内部函数也能使用该锁,锁是可以传递的。

在Java中,我们都知道ReentrantLock是重入锁,因为光看Reentrant翻译就是可重入的意思。于是可能有人会问synchronize是重入锁么?答案当然是yes;

下面我们用代码演示synchronize可重入特点:

methodA方法先使用了this锁,然后去调用methodB方法,也使用了this锁;因为是可重入锁,当方法A调用方法B的时候,B方法应该也能被执行,不会发生死锁的情况。

package com.demo.spring.test.baseThread;

import com.demo.spring.annotation.ThreadSafe;

/**
 * 可重入锁:基本锁都有可重入性,否则很容易导致死锁
 * 死锁产生原因:在同步方法中嵌套同步方法
 */
public class SynchronizedReentrantLock implements Runnable{

    private volatile Integer num = 0;

    @Override
    public void run() {
        while (num < 100){
            this.methodA();
        }
    }

    // methodA和methodB可以共用this锁
    private synchronized void methodA(){
        // 此处需要再次校验num,否则如果按照多线程执行,会导致起初等待中的线程在拿到锁后继续执行,num值会等于100;
        if(num < 100){
            System.out.println("我是方法A,开始执行,即将调用方法B,num="+num+":"+Thread.currentThread().getName());
            this.methodB();
        }
    }

    // 也是使用this锁
    private synchronized void methodB(){
        num++;
        System.out.println("我是方法B,获取到A方法的所,可以进行模拟发送邮件");
    }

    public static void main(String[] args) {
        SynchronizedReentrantLock reentryLock= new SynchronizedReentrantLock();
        Thread thread = new Thread(reentryLock,"窗口1");
        Thread thread1 = new Thread(reentryLock,"窗口2");
        Thread thread2 = new Thread(reentryLock,"窗口3");
        thread.start();
        thread1.start();
        thread2.start();
    }

}

结果展示:

我是方法A,开始执行,即将调用方法B,num=0:窗口1
我是方法B,获取到A方法的所,可以进行模拟发送邮件
我是方法A,开始执行,即将调用方法B,num=1:窗口1
我是方法B,获取到A方法的所,可以进行模拟发送邮件
我是方法A,开始执行,即将调用方法B,num=2:窗口3
我是方法B,获取到A方法的所,可以进行模拟发送邮件
我是方法A,开始执行,即将调用方法B,num=3:窗口3
我是方法B,获取到A方法的所,可以进行模拟发送邮件
我是方法A,开始执行,即将调用方法B,num=4:窗口3
我是方法B,获取到A方法的所,可以进行模拟发送邮件
我是方法A,开始执行,即将调用方法B,num=5:窗口3
我是方法B,获取到A方法的所,可以进行模拟发送邮件
我是方法A,开始执行,即将调用方法B,num=6:窗口3
我是方法B,获取到A方法的所,可以进行模拟发送邮件
我是方法A,开始执行,即将调用方法B,num=7:窗口3
我是方法B,获取到A方法的所,可以进行模拟发送邮件
我是方法A,开始执行,即将调用方法B,num=8:窗口3
我是方法B,获取到A方法的所,可以进行模拟发送邮件
...

下面我们再演示ReentrantLock可重入特点,这个就简单粗暴点的,结果也能正常执行:

2.3  公平锁和非公平锁

用一个面向对象的图片说明:邪恶一点,大家都能理解的...直白了吧

下面用代码演示以下ReentrantLock的非公平锁和公共锁效果:

2.3.1 非公平锁:ReentrantLock lock = new ReentrantLock();

package com.demo.spring.test.baseThread;

import java.util.concurrent.locks.ReentrantLock;

/** 
 * @Description: 重入锁:公平和非公平锁
 * @Author: yangshilei
 */
public class FairLockDemo implements Runnable{

    // 入参为空,默认为非公平锁,线程调用随机选择
    public static ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true){
            try {
                lock.lock();
                lock.lock();
                Thread.sleep(1000);
                System.out.println("线程名称:"+Thread.currentThread().getName());
            }catch (Exception e){
                System.out.println("异常");
            }
            finally {
                lock.unlock();
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        FairLockDemo demo = new FairLockDemo();

        Thread thread1 = new Thread(demo,"线程1");
        Thread thread2 = new Thread(demo,"线程2");
        Thread thread3 = new Thread(demo,"线程3");
        Thread thread4 = new Thread(demo,"线程4");
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}

执行效果:

2.3.2 公平锁:ReentrantLock lock = new ReentrantLock(true);

package com.demo.spring.test.baseThread;

import java.util.concurrent.locks.ReentrantLock;

/** 
 * @Description: 重入锁:公平和非公平锁
 * @Author: yangshilei
 */
public class FairLockDemo implements Runnable{

    // 公平锁,按照顺序去执行
    public static ReentrantLock lock = new ReentrantLock(true);

    @Override
    public void run() {
        while (true){
            try {
                lock.lock();
                lock.lock();
                Thread.sleep(1000);
                System.out.println("线程名称:"+Thread.currentThread().getName());
            }catch (Exception e){
                System.out.println("异常");
            }
            finally {
                lock.unlock();
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        FairLockDemo demo = new FairLockDemo();

        Thread thread1 = new Thread(demo,"线程1");
        Thread thread2 = new Thread(demo,"线程2");
        Thread thread3 = new Thread(demo,"线程3");
        Thread thread4 = new Thread(demo,"线程4");
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}

执行结果:

演示结束,后期再继续补充关于锁的内容

猜你喜欢

转载自blog.csdn.net/qq_37488998/article/details/112303335