public class T {
/*volatile*/ boolean running=true;
void m() {
System.out.println("m start");
while(running) {
}
System.out.println("m end");
}
public static void main(String args[]) {
T t =new T();
new Thread(()->t.m(),"t1").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t.running=false;
}
这样一段话并不会使t1线程终止,因为java内存模型,每一个线程都拥有一个工作内存,其内部存储了主内存变量的副本,在主内存中修改了running的值,t1线程并不知道,他工作内存中running副本的值还未改变。
这个问题可以使用volatile来解决。
那么,sychronized呢?
当ThreadA释放锁M时,它所写过的变量(比如,x和y,存在它工作内存中的)都会同步到主存中,而当ThreadB在申请同一个锁M时,ThreadB的工作内存会被设置为无效,然后ThreadB会重新从主存中加载它要访问的变量到它的工作内存中。
也就是说,synchronized 只会保证该同步块中的变量的可见性,发生变化后立即同步到主存。另一个线程在申请锁的时候,会重新更新工作内存。
public class T {
/*volatile*/ boolean running=true;
synchronized void m() {
System.out.println("m start");
while(running) {
}
System.out.println("m end");
}
public static void main(String args[]) {
T t =new T();
new Thread(()->t.m(),"t1").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t.running=false;
}
}
这样还是不可以的,因为sychronied修饰的代码块中并没有改写running变量。
但是,当我在循环里里面加了一个打印输出的语句,就可以终止线程了,为什么呢?
public class T {
/*volatile*/ boolean running=true;
void m() {
System.out.println("m start");
while(running) {
System.out.println("2");
}
System.out.println("m end");
}
public static void main(String args[]) {
T t =new T();
new Thread(()->t.m(),"t1").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t.running=false;
}
}
实际上,JVM对于现代的机器做了最大程度的优化,也就是说,最大程度的保障了线程和主存之间的及时的同步,也就是相当于虚拟机尽可能的帮我们加了个volatile,但是,当CPU被一直占用的时候,同步就会出现不及时,也就出现了后台线程一直不结束的情况。
也就是说,在cpu空闲的时候,可能会更新一下主内存的内容。
比如,我们在循环中sleep一下,亦可以结束线程。