数据类型String的常量池特性
在JVM中具有String常量池缓存的功能。
将synchronized(string)同步块与String联合使用时,要注意常量池以带来的一些例外。
package test;
/**
* @Author LiBinquan
*/
public class Service {
public static void print(String str){
try {
synchronized (str) {
while (true) {
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000);
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
线程1:
package test;
/**
* @Author LiBinquan
*/
public class ThreadTest1 extends Thread{
private Service service;
public ThreadTest1(Service service){
super();
this.service = service;
}
@Override
public void run() {
service.print("AA");
}
}
线程2:
package test;
/**
* @Author LiBinquan
*/
public class ThreadTest2 extends Thread{
private Service service;
public ThreadTest2(Service service){
super();
this.service = service;
}
@Override
public void run() {
service.print("BB");
}
}
运行类:
package test;
/**
* @Author LiBinquan
*/
public class Run {
public static void main(String[] args) throws InterruptedException {
Service service = new Service();
ThreadTest1 threadTest1 = new ThreadTest1(service);
threadTest1.setName("A");
threadTest1.start();
ThreadTest2 threadTest2 = new ThreadTest2(service);
threadTest2.setName("B");
threadTest2.start();
}
}
输出:
出现输出的情况就是因为String的两个值都是AA,两个线程持有相同的锁,所以造成线程B不能执行。这就是String常量池锁带来的问题。因此在大多数的情况下,同步synchronized代码块都不使用String作为锁对象,而改用其他,比如new Object()实例化一个Object对象,但它并不放入缓存中。
这边 我们修改一下 测试代码:
package test;
/**
* @Author LiBinquan
*/
public class Service {
public static void print(Object o){
try {
synchronized (o) {
while (true) {
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000);
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
线程1:
package test;
/**
* @Author LiBinquan
*/
public class ThreadTest1 extends Thread{
private Service service;
public ThreadTest1(Service service){
super();
this.service = service;
}
@Override
public void run() {
service.print(new Object());
}
}
线程2:
package test;
/**
* @Author LiBinquan
*/
public class ThreadTest2 extends Thread{
private Service service;
public ThreadTest2(Service service){
super();
this.service = service;
}
@Override
public void run() {
service.print(new Object());
}
}
输出:
交替打印的原因是持有的锁不是一个。
2.同步synchronized方法无限等待与解决
同步方法很容易死循环。
package test;
/**
* @Author LiBinquan
*/
public class Service {
synchronized public void methodA(){
System.out.println("methodA begin");
boolean isContinueRun = true;
while (isContinueRun){
}
System.out.println("methodA end");
}
synchronized public void methodB(){
System.out.println("methodB begin");
System.out.println("methodB end");
}
}
线程1:
package test;
/**
* @Author LiBinquan
*/
public class ThreadTest1 extends Thread{
private Service service;
public ThreadTest1(Service service){
super();
this.service = service;
}
@Override
public void run() {
service.methodA();
}
}
线程2:
package test;
/**
* @Author LiBinquan
*/
public class ThreadTest2 extends Thread{
private Service service;
public ThreadTest2(Service service){
super();
this.service = service;
}
@Override
public void run() {
service.methodB();
}
}
运行类:
package test;
/**
* @Author LiBinquan
*/
public class Run {
public static void main(String[] args) throws InterruptedException {
Service service = new Service();
ThreadTest1 threadTest1 = new ThreadTest1(service);
threadTest1.setName("A");
threadTest1.start();
ThreadTest2 threadTest2 = new ThreadTest2(service);
threadTest2.setName("B");
threadTest2.start();
}
}
输出:
如上图所示,线程B永远得不到运行的机会,锁死了。
这时可以通过同步块来解决这样的问题。
更改后的代码如下:
package test;
/**
* @Author LiBinquan
*/
public class Service {
Object o1 = new Object();
public void methodA(){
synchronized (o1) {
System.out.println("methodA begin");
boolean isContinueRun = true;
while (isContinueRun) {
}
System.out.println("methodA end");
}
}
Object o2 = new Object();
public void methodB(){
synchronized(o2) {
System.out.println("methodB begin");
System.out.println("methodB end");
}
}
}
输出: