前2天项目中的一个web应用,在做了几个操作后就死机,白板了,只能重启,找了半天没有错误日志,跟踪了一下业务操作,发现是前台页面的提交与后台的其他应用并发引起死锁操作,几人把该同志的代码评审了一下,讨论了一下同步的写法,发现其锁的实现是有漏洞的。在此也总结一下,可能对其他人有所帮助,当然,感觉这种实现应该是很早就该如此的。
业务场景是这样的:前台应用A,后台应用B,都需要同时对资源类C进行get和set操作,但前提是每个应用在get资源后一直到其set或不set退出之前,保持C资源为其应用独占,因为C资源的操作必须是看到什么才能做什么。
例:C资源数必须>=0,A 先get C 得到100的量,经过一些判断处理决定允许将C进行减50的操作,同时B也想对C进行减80的操作,这样一来,执行的正确顺序是
1)A get c=100
2)A 判断 是否允许-50
3)A set c=100-50
4)B get c=50
5)B 判断 是否允许-80
6)B 条件不允许 退出
测试的类代码如下:
public class ClassA extends Thread {
private String threadName = "";//线程名称
private int sleepTime = 10000 ;//线程sleep时间
private Object lock = new Object(); //锁
private int subValue = 0 ; //被减数
public ClassA(String name,int sleep,int subValue){
this.threadName = name ;
this.sleepTime = sleep ;
this.subValue = subValue ;
}
public void run(){
System.out.println(threadName + " is running ");
synchronized (lock){
System.out.println(threadName + " get self lock ");
synchronized (ClassC.getLock()){
int value = ClassC.getValue();
System.out.println(threadName + " get C.value = "+value );
try{
//线程sleep 模拟应用的其他处理
System.out.println(threadName + " now is sleeping ");
Thread.sleep(sleepTime);
}catch(InterruptedException e){
e.printStackTrace();
}
if(value >= this.subValue){
int newValue = value - this.subValue ;
ClassC.setValue(newValue);
System.out.println(threadName + " set new value = " + newValue);
}else{
System.out.println(threadName + " can't set new value,now is = "+ value) ;
}
}
}
}
public static void main(String[] args) throws Exception {
ClassA a = new ClassA("appA",6000,50);
a.start();
ClassA b = new ClassA("appB",2000,80);
b.start();
}
}
class ClassC {
private static int value = 100 ;
private static Object lock = new Object();
public static Object getLock(){
return lock ;
}
public static int getValue(){
return value ;
}
public static void setValue(int newValue){
value = newValue ;
}
}
当前代码执行后结果如下:
appA is running
appB is running
appB get self lock
appA get self lock
appA get C.value = 100
appA now is sleeping
appA set new value = 50
appB get C.value = 50
appB now is sleeping
appB can't set new value,now is = 50
我们发现的错误是在ClassA的lock使用上,当时的开发人员写的代码是下面这行:
private String lock = ""; //锁
ClassA的锁和ClassC的锁用的都是字串,而不是Object,执行的结果就变成了:
appA is running
appB is running
appB get self lock
appB get C.value = 100
appB now is sleeping
appB set new value = 20
appA get self lock
appA get C.value = 20
appA now is sleeping
appA can't set new value,now is = 20
结果appB先把100减去80变成20,appA则不能用了
在试验时,大家对这一结果颇感不解,后经一人提醒,相同值的String 对象jvm会进行优化,使用相同的内存地址,以下这句话可以打印true
String a="123";
String b="123";
System.out.println( a == b);
所以,自我总结,应该在java中使用同步操作时,选取的对象最好是一个唯一的对象,以防止大家使用相同的对象,而使并发锁在同一个对象上,造成死锁。
分享到:
相关推荐
执行monitorenter指令时,线程会为锁对象关联一个ObjectMonitor对象(c++)。线程遇到synchronized同步时,先会进入ObjectMonitor对象的EntryList队列中,然后尝试把ObjectMonitor对象的owner变量设置为当前线程,同时...
首先,每个用户在抢红包之前都会尝试获取一个分布式锁,以防止多个用户同时抢到同一个红包。在获取分布式锁的过程中,如果锁已经被其他用户获取,则当前用户会等待一段时间后重试。当用户成功获取到分布式锁后,就...
在实际使用场景中,例如多个用户同时操作一个银行账户的情况,就可以使用Lock锁进行线程同步,确保每次只有一个用户能进行操作。总的来说,Lock锁是Java多线程编程中的重要工具,能够有效保障程序运行的正确性和稳定...
*每一个java对象都是一个潜在的monitor(监视器) >synchronized 关键字 *所有现代JVM虚拟机都包含light-weight锁 >避免关联一个系统互斥信号量或者每个对象的条件变量(heavy-weight lock) >当没有竞争时...
在线程获取锁时会调用AQS的acquire()方法,该方法第一次尝试获取锁如果失败,会将该线程加入到CLH队列中:public final void acqui
而Lock接口则是一个更底层的同步机制,它提供了更丰富的功能,但需要显式地获取和释放锁,通常通过实现类如ReentrantLock来使用。 其次,从功能特性上来看,synchronized具有可重入性,即同一个线程可以多次获取同...
作用确保线程互斥的访问同步代码保证共享变量的修改能够及时可见有效解决重排序问题用法修饰普通方法(见程序)修饰静态方法(类)(见程序)修饰代码块(见程序)原理同步代码块每个对象有一个监视器锁(monitor)。...
混合Scala编程是一个大胆的尝试,可以弥补Java的一些弱点;当然,主要还是基于Java开发,至少一开始如此。 Spring仍旧是核心的组件,当然仍旧没有使用太多的Spring,因为我觉得Spring后面的很多功能已经背离了早先...
面试官,别挂电话,Synchronized,我还能说上半小时。 Synchronized关键字,经常被用于线程同步。执行Synchronized修饰的同步代码块的...互斥性(也叫原子性):同一时刻只允许一个线程获取对象锁 可见性:线程获取对
如果第一个不匹配,则判断它的下一个是红黑树还是链表。 红黑树就按照树的查找方式返回值。 不然就按照链表的方式遍历匹配返回值。 从这两个核心方法(get/put)可以看出 1.8 中对大链表做了优化,修改为红黑树之后...
* 这时候线程2会在判断instance==null的时候失败,返回一个不完整的intance对象。 * * 尝试最佳实现方法。在该方法中,Singleton有一个private类型静态内部类, * 内部类在外部类加载的时候并不会加载,只有在...
Spring批工作榛树播报 使用Spring Boot创建的简单PoC项目,具有Maven 3,Spring Batch和配置为作为独立实例或群集运行的Hazelcast服务器,并在新实例启动(或关闭)时自动更改连接... 如果您启动另一个本地终端并运行
是的,无法尝试获取失败的锁,也无法尝试获取锁定一段时间后再失败。 #### 同步已在该块中进行。 为此使用专用对象锁。 因为仅同步块,则此实现将更快,不会很多,但仍会更快。 而且,如果我们需要一些特殊的逻辑,...
可以通过原子变量将计数结果包装在一个同步块中,但是无法避免性能下降。 另外,我尝试通过提高交易的隔离级别进行处理为了防止其他事务读取特定事务(A)已读取的内容换句话说,在读取A之后,其他事务将等待直到A...
由于将来手机的键值映射种类会越来越多,因此,用户可以自己保存映射,请新建一个keymap.rc文件,如果没有设置过系统路径,请放在最后一个盘的根目录下,或者放在系统路径的根目录下,Anyview启动时会从keymap.rc...
1.2 编写第一个程序 8 1.3 使用命名空间 12 1.4 创建图形应用程序 15 第1章快速参考 22 第2章 使用变量、操作符和表达式 25 2.1 理解语句 25 2.2 使用标识符 26 2.3 使用变量 27 2.3.1 命名变量 27 2.3.2 ...