最近在看多线程,synchronized代码块时候说到用String对象来当作锁,然后说最好不要用String对象来当作锁,这是为什么了?可以先看一段程序。
public class StringLock {
public void method() {
//new String("字符串常量")
synchronized ("字符串常量") {
try {
while(true){
System.out.println("当前线程 : " + Thread.currentThread().getName() + "开始");
Thread.sleep(1000);
System.out.println("当前线程 : " + Thread.currentThread().getName() + "结束");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
final StringLock stringLock = new StringLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
},"t2");
t1.start();
t2.start();
}
}
程序运行的结果(部分)是:
当前线程 : t1开始
当前线程 : t1结束
不断输出这两句话,这是为什么了?因为锁"字符串常量"
是一个常量锁,当你将锁"字符串常量"
改成new String("字符串常量")
,控制台会输出什么了,t2线程也可以开始运行了。程序运行的结果(部分)是:
当前线程 : t1开始
当前线程 : t2开始
当前线程 : t2结束
当前线程 : t1结束
这是因为new String("字符串常量")
创建的是不同的对象,所以锁不同,两个线程都能执行。这让我想起了我们刚学java的时候有一些书本上会讲到这个列子:
public static void main(String[] args) {
String s1 = new String("hello");
String s2 = "hello";
System.out.println(s1 == s2);// false
System.out.println(s1.equals(s2));// true
}
new String("hello")
和"hello"
是两个不同的对象,一个是保存在常量池中,一个是保存在堆中。有些时候业务上就是需要传入一个new String的对象锁了,可以将锁改成new String("字符串常量").intern()
,这个锁的本质其实和"字符串常量"
这个锁是一样的。
看看jdk的文档说的:一个初始为空的字符串池,它由类 String 私有地维护。 当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。
对于String对象锁在业务上的例子可以参考这篇博文https://www.cnblogs.com/xrq730/p/6662232.html