【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的对象,需要做到在同步块中避免对锁对象的修改。

posted @ 2017-11-01 18:20  sqdmydxf  阅读(1150)  评论(0编辑  收藏  举报