首先举一个例子
现在有5个用户同时访问业务需要生成5个唯一订单ID并存入数据库
这是一个公共的生成ID的类,生成的规则是【当前时间+用于自增的全局变量】(不要在意这个方式的弊端,只是用来举个栗子~)
我们跑一下试试
可以看到竟然有两个一模一样的ID,这是万万不能允许发生的情况
为什么会发生这种情况呢,是因为多个线程在同一时间访问了这个方法,然后修改了这个int变量,上一个线程还没来得及做完所有操作,int值就被另个线程给修改了
下面讲解如何解决问题
基于JVM解决:
1.使用Synchronized解决问题
public class Test{
//创建锁对象
static synchronizedTest instance=new synchronizedTest();
public void test() {
//省略其他的耗时操作。。。
//使用同步代码块对方法内的代码进行同步操作,锁对象为instance
synchronized(instance){
//需要执行的代码。。。
}
}
}
1234567891011
给生成ID的代码加上同步代码块,成功解决问题
使用同步代码块的作用是:同一时刻,只有一个线程可以执行该代码块
除了第一种方法外,还可以使用第二种方法解决问题
2.使用Lock锁解决问题
public class LockTest {
private Lock lock = new ReentrantLock();
//需要参与同步的方法
private void test(Thread thread){
//获取锁,如果锁被暂用则一直等待
lock.lock();
try {
System.out.println("获得了锁");
}catch(Exception e){
//打印异常
e.printStackTrace();
} finally {
System.out.println("释放了锁");
//释放锁
lock.unlock();
}
}
12345678910111213141516171819
给生成ID的代码加上Lock锁,成功解决问题
这两种方法都是为了解决同步问题,那么他们的区别是什么呢?
这里提供一个表来方便做对比(来源:https://blog.csdn.net/qq_39521554/article/details/81130442)
类别
synchronized
Lock
存在层次
Java的关键字,在jvm层面上
是一个类
锁的释放
1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁
在finally中必须释放锁,不然容易造成线程死锁
锁的获取
假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待
分情况而定,Lock有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待
锁状态
无法判断
可以判断
锁类型
可重入 不可中断 非公平
可重入 可判断 可公平(两者皆可)
性能
少量同步
大量同步
谈谈我的看法:
两者有一个很大的区别就是Synchronized是自动释放锁的,而ReentrantLock需要手动通过unlock()释放,如果没有处理好就很容易死锁
其次,ReentrantLock锁机制要比Synchronized更好一些,比如Synchronized因IO等问题被阻塞了,但是又没有释放锁,其他线程便只能干巴巴的等着,ReentrantLock有机制可以使线程不无限期等待而Synchronized不可以
两种同步方式的使用建议:
Synchronized和ReentrantLock在一般情况下没有什么区别,但是在非常复杂的同步应用中,请考虑使用ReentrantLock,特别是遇到下面3种需求
1.某个线程在等待一个锁的控制权的这段时间需要中断
2.需要分开处理一些wait-notify,ReentrantLock里面的Condition应用,能够控制notify哪个线程
3.具有公平锁功能,每个到来的线程都将排队等候
————————————————
版权声明:本文为CSDN博主「_MoLi_」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/z806899669/article/details/99067176