上QQ阅读APP看书,第一时间看更新
2.2.17 String常量池特性与同步问题
JVM具有String常量池的功能,所以如图2-48所示的结果为true。
图2-48 String常量池缓存
在将synchronized(string)同步块与String联合使用时,要注意常量池会带来一些意外。
新建名称为StringAndSyn的项目,类文件Service.java代码如下:
package service; public class Service { public static void print(String stringParam) { try { synchronized (stringParam) { while (true) { System.out.println(Thread.currentThread().getName()); Thread.sleep(1000); } } } catch (InterruptedException e) { e.printStackTrace(); } } }
两个自定义线程代码如图2-49所示。
运行类Run.java代码如下:
package test; import service.Service; import extthread.ThreadA; import extthread.ThreadB; public class Run { public static void main(String[] args) { Service service = new Service(); ThreadA a = new ThreadA(service); a.setName("A"); a.start(); ThreadB b = new ThreadB(service); b.setName("B"); b.start(); } }
图2-49 线程类代码
程序运行结果如图2-50所示。
图2-50 死循环
出现这种情况就是因为String的两个值都是”AA”,两个线程是持有相同的锁,造成线程B不能执行。这就是String常量池所带来的问题,所以大多数情况下,同步synchronized代码块都不使用String作为锁对象,而改用其他,例如new Object()实例化一个新的Object对象时,它并不放入缓存池中,或者执行new String()创建不同的字符串对象,形成不同的锁。
继续实验,创建名称为StringAndSyn2的项目,类文件Service.java代码如下:
package service; public class Service { public static void print(Object object) { try { synchronized (object) { while (true) { System.out.println(Thread.currentThread().getName()); Thread.sleep(1000); } } } catch (InterruptedException e) { e.printStackTrace(); } } }
两个自定义线程如图2-51所示。
图2-51 自定义线程代码
运行类Run.java代码如下:
package test; import service.Service; import extthread.ThreadA; import extthread.ThreadB; public class Run { public static void main(String[] args) { Service service = new Service(); ThreadA a = new ThreadA(service); a.setName("A"); a.start(); ThreadB b = new ThreadB(service); b.setName("B"); b.start(); } }
程序运行结果如图2-52所示。
图2-52 交替输出
交替输出的原因是持有的锁不是一个。