目录
一、两种锁特性总览:
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();
}
}
执行结果:
演示结束,后期再继续补充关于锁的内容