【java】锁对象状态的改变会导致非线程安全
问题描述
使用synchronized在一个非final对象上加了锁之后,在synchronized体(同步代码块)中,将该对象的值(状态)改变之后,会导致线程不安全,即其他线程会拿到改变之后对象的锁,从而进入同步代码块。
场景设计
public class TestLock extends Thread{ private AAA aaa; public TestLock(AAA aaa) { this.aaa = aaa; } @Override public void run() { this.aaa.lockMethod(); } public static void main(String[] args) { AAA aaa = new AAA(); Thread a1 = new TestLock(aaa); Thread a2 = new TestLock(aaa); a1.start(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } a2.start(); } } class AAA{ private Boolean lock = true; public void lockMethod() { synchronized (lock) { lock = !lock; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(lock); } } }
场景说明
两个线程a1,a2,访问同一个AAA对象aaa(临界资源),AAA中的lockMethod方法中是一个同步代码块,锁加在非final的lock对象上,在同步代码块中改变lock对象的值。main函数中500ms的休眠是能够让线程a1先获得lock的锁,线程a1先访问同步代码块,将lock的值变成false,接着休眠一秒。
假设
如果lock值的改变不会影响线程安全的特性,那么线程a2拿不到lock的锁,在休眠结束之后,应该输出false,然后,线程a2拿到lock锁,进入同步代码块,将lock的值变成true,然后休眠一秒,休眠结束后,输出lock的值true,即输出的两个值是false和true。
程序运行结果
true和true
程序结果分析
第一个的输出结果是true,说明,线程a1进入同步代码块之后,将lock的值变成false,进入休眠,线程a2能够拿到修改后的lock对象的锁,进入同步代码块,将lock的值变成true,然后线程a2进入休眠,线程a1休眠结束之后,输出被线程a2修改之后的lock值true,然后,线程a2休眠结束之后,同样输出true,符合程序输出结果。
总结与注意
对于非对象上的锁,在同步代码块中修改起状态后,会导致线程不安全,即其他线程会拿到修改后的对象上的锁。
对于需要加锁的对象,可以设置成final,这样就避免了对其的修改,如果是非final的对象,需要做到在同步块中避免对锁对象的修改。