要想实现内存可见性必须要做的两个步骤:
1)线程修改后的共享变量能够及时从线程本地内存刷新到主内存
2)其他线程能够及时把共享变量的最新值从主内存更显到自己的工作内存中
synchronized保证synchorized{}范围内的操作的原子性和共享数据的可见性
synchronized保证操作的原子性是添加互斥锁。在{}中锁的获取和释放是隐私的。
synchronized的内存可见性步骤:
1)获取互斥锁
2) 清空工作内存
3)从主内存拷贝变量的最新值到工作内存中
4)执行代码
5)将更改后的共享变量值刷新到主内存中
6)释放互斥锁
volatile保证内存可见性
volatile int a;
假设线程A修改了共享变量a,并把它刷新到主内存中。线程B需要读取a时,首先把线程本地内存中的a变量置为无效,然后从主内存中读取a的最新值。这也算是线程通过共享内存间接通信了。
volatile只能保证可见性,不能保证原子性。
这句话该如何理解呢?
看Demo
class VolatileExample {
int a = 0;
volatile boolean flag = false;
public void writer() {
a = 1; // 1
flag = true; // 2
}
public void reader() {
if (flag) { // 3
int i = a; // 4
……
}
}
}//线程A调用writer(),线程B调用reader()
在多线程并发时,线程切换时不可预测的,假设线程A操作还没有执行到a=1时,切换到线程B,结果线程B没有执行if语句,这样线程通信就失败了。volatile不能保证原子性是应为线程的交叉执行。
问:为什么不加synchorized和volatile,共享变量在多个线程中也可见?
答:是处理器或者说是JMM(java 内存模型)在不断刷新线程工作空间的值和主内存中的值。加上synchronized和volatile更安全。
线程的分类:守护线程(GC)和用户线程(main线程)
使用setDaemon(boolean)可以设置线程为守护线程
Thread.State是枚举类型,描述线程的状态:
new
running
blocking
waiting
timed-waiting
terminated
jdk/bin 目录下的jstack更够帮你生成jvm中所有线程的快照