《多线程编程》学习之六:String的常量池特性,锁对象的改变,volatile关键字

来源:互联网 发布:连锁店软件多少钱 编辑:程序博客网 时间:2024/04/27 17:02

1、String的常量池特性

        在JVM中具有常量池缓存功能,如String s1 = "abc"; String s2 = "abd";   这里s1与s2代表同一个String对象。当我们在同步代码块synchronized(string){ }使用String作为锁对象时,可能会出现问题。看下面例子:

例一:



测试结果:


        

        可以看出,线程Thread-B得不到执行,这是因为String的两个值都是A,两个线程需要同一个锁对象,一旦Thread-A持有锁对象,就进入死循环,导致线程Thread-B得不到执行。这是String常量池带来的问题,大多数情况下,同步synchronized(){}代码块都不使用String作为对象锁,而改用其它,如new Object()实例化一个对象。


例二:




          可见修改后的程序,线程A与B是异步执行的。


2、锁对象的改变




          可见,两个线程是异步执行的,这是因为50毫秒过后,线程Thread-B获取的锁与Thread-A持有的锁不是同一个。如果把Thread.sleep(50)注释掉,则出现一下结果:


        可见,虽然将锁改变了,但结果是线程之间是同步的。因为两个线程共同抢占的是同一个锁。


3、volatile关键字非原子特性

        volatile关键字只能用于修饰变量,解决的是变量在多线程之间的可见性(内存模型中私有堆栈与公有堆栈,关键字volatile提醒线程每次从共享内存中读取变量,而不是从私有内存中读取),但不能保证原子性。多线程访问volatile变量不会发生阻塞。线程安全包括原子性可见性两方面。




          可见volatile关键字不保证同步性。解决办法是在addCount()方法前添加synchronized关键字。

修改如下:


运行结果:



4、使用原子类进行i++操作


        可见,成功累加到10000。

        但是原子类也并不完全安全。虽然incrementAndGet()方法是原子性的,但方法与方法之间的调用不是原子时,仍会出现非线程安全。例如,在一个非原子的方法中调用多个原子方法,而多线程访问这个非原子方法,则可能出现问题。


5、补充

1)volatile关键字是线程同步的轻量级实现,性能上比synchronized要好。

2)volatile能保证数据的可见性,但不能保证原子性;synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做同步。


0 0