本文内容部分引自《Java多线程编程核心技术》,感谢作者!!!
代码地址:https://github.com/xianzhixianzhixian/thread.git
什么是常量池
这里单单只说Java常量池,Java中的常量池,实际上分为两种形态:静态常量池和运行时常量池。
所谓静态常量池,即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)字面量,还包含类、方法的信息,占用class文件绝大部分空间。这种常量池主要用于存放两大类常量:字面量(Literal)和符号引用量(Symbolic References),字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等,符号引用则属于编译原理方面的概念,包括了如下三种类型的常量:
- 类和接口的全限定名
- 字段名称和描述符
- 方法名称和描述符
而运行时常量池,则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。
运行时常量池相对于CLass文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是并非预置入CLass文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中,这种特性被开发人员利用比较多的就是String类的intern()方法。
String的intern()方法会查找在常量池中是否存在一份equal相等的字符串,如果有则返回该字符串的引用,如果没有则添加自己的字符串进入常量池。
再说说String str="a"和String str=new String("a")的区别
经常有一些面试题会问
String a = "a"; String b = "a"; String c = new String("a"); String d = new String("a"); 那么 a==b和c==d的结果分别是什么?
答案是:a==b是true,而c==d是false。为什么呢?
1、==符号比较的是两个数据的地址。
2、a==b为true的原因很显然,是String常量池的问题。
3、c==d为false是因为,String c=new String("a");这段语句的真实意思是“把new出来的String对象地址分配给c”,c变量其实存放的是是new出来的String对象的地址,存放在栈中。而new 出来的String对象是内容,存放在堆中。同理可以类比Integer i=1和Integer k=new Integer(1)的区别,其实和String一样。
接下来讨论String常量池结合线程的问题
StringTest.java
package thread.synchronize.string;
/**
* 探究String的常量池特性在多线程中的影响
* @author: xianzhixianzhixian
* @date: 2018-12-18 20:32
*/
public class StringTest {
public void testStringSync(String str){
synchronized(str){
try {
System.out.println("线程"+Thread.currentThread().getName()+"运行开始");
Thread.sleep(1000);
System.out.println("线程"+Thread.currentThread().getName()+"运行结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
ThreadA.java
package thread.synchronize.string;
/**
* @author: xianzhixianzhixian
* @date: 2018-12-18 20:30
*/
public class ThreadA extends Thread {
private StringTest test;
public ThreadA(StringTest test) {
this.test = test;
}
@Override
public void run() {
super.run();
test.testStringSync(new String("AA"));
}
}
ThreadB.java
package thread.synchronize.string;
/**
* @author: xianzhixianzhixian
* @date: 2018-12-18 20:30
*/
public class ThreadB extends Thread {
private StringTest test;
public ThreadB(StringTest test) {
this.test = test;
}
@Override
public void run() {
super.run();
test.testStringSync("AA");
}
}
ThreadC.java
package thread.synchronize.string;
/**
* @author: xianzhixianzhixian
* @date: 2018-12-18 20:30
*/
public class ThreadC extends Thread {
private StringTest test;
public ThreadC(StringTest test) {
this.test = test;
}
@Override
public void run() {
super.run();
test.testStringSync("AA");
}
}
Run.java
package thread.synchronize.string;
/**
* @author: xianzhixianzhixian
* @date: 2018-12-18 20:35
*/
public class Run {
public static void main(String[] args) {
StringTest testStr = new StringTest();
ThreadA threadA = new ThreadA(testStr);
threadA.setName("a");
ThreadB threadB = new ThreadB(testStr);
threadB.setName("b");
ThreadC threadC = new ThreadC(testStr);
threadC.setName("c");
threadA.start();
threadB.start();
threadC.start();
}
}
运行结果:线程a和线程c是同步运行的,而线程b和其它线程是异步运行的,这也说明了String常量池和new String()对线程运行的影响以及两者间的区别