Java在多线程高并发下,该如何做到安全的修改同一个数据(含实际操作)

首先举一个例子

现在有5个用户同时访问业务需要生成5个唯一订单ID并存入数据库

这是一个公共的生成ID的类,生成的规则是【当前时间+用于自增的全局变量】(不要在意这个方式的弊端,只是用来举个栗子~)

Java在多线程高并发下,该如何做到安全的修改同一个数据(含实际操作)_第1张图片

我们跑一下试试

![在这里插入图片描述](https://img-blog.csdnimg.cn/20190810125807427.png![在这里插入图片描述](https://img-blog.csdnimg.cn/2019081012590697.png

可以看到竟然有两个一模一样的ID,这是万万不能允许发生的情况
为什么会发生这种情况呢,是因为多个线程在同一时间访问了这个方法,然后修改了这个int变量,上一个线程还没来得及做完所有操作,int值就被另个线程给修改了

下面讲解如何解决问题


基于JVM解决:

1.使用Synchronized解决问题

 public class Test{
 	//创建锁对象
    static synchronizedTest instance=new synchronizedTest();
    public void test() {
        //省略其他的耗时操作。。。
        //使用同步代码块对方法内的代码进行同步操作,锁对象为instance
        synchronized(instance){
           //需要执行的代码。。。
        }
    }
}
给生成ID的代码加上同步代码块,成功解决问题

Java在多线程高并发下,该如何做到安全的修改同一个数据(含实际操作)_第2张图片

使用同步代码块的作用是:同一时刻,只有一个线程可以执行该代码块

除了第一种方法外,还可以使用第二种方法解决问题

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();
        }
    }
 
给生成ID的代码加上Lock锁,成功解决问题

Java在多线程高并发下,该如何做到安全的修改同一个数据(含实际操作)_第3张图片


这两种方法都是为了解决同步问题,那么他们的区别是什么呢?


这里提供一个表来方便做对比(来源: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.具有公平锁功能,每个到来的线程都将排队等候

下一次说说分布式的解决方案


这是我通过学习对知识的整理及备忘,本博客的所有内容,仅是自己的一些学习笔记,如有错误,欢迎指正。如有侵权,请告知修改。

你可能感兴趣的:(Java)