在多线程环境中,经常发生一些线程安全问题。为此,Java提供了一些线程同步机制来解决安全问题,例如:同步锁和锁定锁可以解决线程安全问题。
我们可以将锁分为两类:
顾名思义,始终假定悲观情绪是最坏的情况是最坏的情况。每次获取数据时,都会修改其他线程,因此每次获得数据时,都会锁定。块直到获得锁定为止。。在MySQL数据库中,在Java中同步和重新进入。
总是认为乐观的锁是最好的情况。每当我获取数据时,我认为其他线程不会被修改,因此不会被锁定,但是在修改数据时,您需要判断在此期间是否还有其他线程。如果您已经修改了数据,则需要如果您尚未修改,可以正常修改它。Common乐观锁包括版本编号控制,CAS算法等。
案例如下:
该程序中总共打开了50个线程,并且在线程中使用了共享变量的共享变量,因此,如果没有发生线程安全问题,则应最终结果,但是必须有线程安全性该程序中的问题。
如果要解决线程安全问题,则可以使用同步关键字:
包装使用同步关键字修改计数变量的操作,以便当线程执行++操作时,不能同时执行其他线程,并且您只能等待先前的线程执行1000次,然后才能执行。继续执行可以确保最终结果是。
使用重新输入也可以解决线程安全问题:
这两个锁定机制是悲观主义的特定实施。无论是否同时修改其他线程以确保原子操作,它都是直接锁定的。
由于该线程的调度被操作系统资源极为消耗,因此我们应该尝试避免线程不断阻止和唤醒开关,从而一个乐观的锁。
在数据库表中,我们经常设置一个版本字段,这是一个乐观锁的实施例。假设数据表的数据内容如下:
如何避免线程安全性?
假设目前有两个线程A和B,他们想修改此数据,他们将执行以下SQL语句:
首先,两个线程都查询ZS用户的版本编号为1,然后首先执行“更新操作”。目前,用户的密码已修改为管理,并且版本号为1版本号为2,因此更新必须失败。结果,线程B失败。它只能再次获取版本号,然后更新。这是一个乐观的锁。锁操作,但仍然可以确保线程安全性。
我仍然以初始添加的过程为例。在Java,我们还可以使用一种特殊的方法来实施它:
为什么可以使用AtomicInteger类解决线程安全问题?
让我们检查源代码:
当计数调用regrementAndGet()方法时,实际上调用了不安全类的getAndddint()方法:
getAndDint()方法中有一个周期。关键代码在这里。我们假设线程A目前输入该方法。目前,var1是一个原子素对象(0初始值),var2的值为12(这是一个内存的数量,我们不在乎),var4的值是1(准备添加1个操作计数)。
首先,可以通过AtomicInteger对象和内存偏移获得主内存的数据值:
VAR5的价值为0,然后该程序将判断:
比较swapint()是局部方法。它的作用是比较和交换,即确定var1的值是否与从主内存中获取的var5值相同。在此TimeGive var1并返回true,然后转到false,因此周期结束了,最终方法返回1。
这是所有正常的操作过程,但是当它发生时,治疗不相同。假设螺纹a目前已执行到getAndDint()方法:
目前VAR1的值为0(VAR1是共享变量AtomicInteger)。当线程A准备执行它时,线程B是第一个执行的。对于0,它相对成功。目前,var1的值变为1;目前,这是线程A的转弯。它获得了Var5至1的值。目前,VAR1的值不等于Var1的值。IT将失败并重新输入周期。目前,VAR1的值已更改。目前,VAR5的值也为1,相对成功,因此将VAR1的值添加到2。线程修改了主内存中VAR1的值,操作将再次失败,并且程序将重新 -输入周期。
这是使用旋转来实现乐观的锁。因为它没有锁定,因此可以节省线程调度的资源,但也有必要避免旋转程序的过程。
使用CAS的原理可以轻松实现旋转锁。首先,AtomicReference中的初始值必须为null,因此第一个线程在调用lock()方法后将当前线程的对象成功地将当前线程的对象放入AtomicReference中。其他线程调用lock()方法,将是在周期中,因为线的对象与原子recreference中的对象不同。在执行第一个线程++操作之前,调用了Unlock()方法,并且线程仅为此线程。将AtomicReference放置在null上,而其他线程此时可以跳出循环。
通过CAS机制,我们可以在不添加锁的情况下模拟锁定的效果,但其缺点也很明显: