??写在前面
这里是温文艾尔の学习之路- 如果对你有帮助,给博主一个免费的点赞以示鼓励把QAQ
- 博客主页 温文艾尔の学习小屋
- ??更多文章?请关注温文艾尔主页
- 文章发布日期:2022.02.08
- java学习之路!
- 欢迎各位点赞评论收藏??
- 新年快乐朋友们
- jvm学习之路!
- ??上一篇内容:【JVM】JVM07(类加载阶段详细解析)
对于JMM的权威介绍,请参考java内存模型-Java Memory Model的意思,这个要和java内存结构进行区分
getstatic i //获取静态变量i的值
iconst_1 //准备常量1
iadd //自增
putstatic i //将修改后的值存入静态变量
getstatic i //获取静态变量i的值
iconst_1 //准备常量1
isub //自减
putstatic i //将修改后的值存入静态变量
//i的初始值为0
getstatic i //获取静态变量i的值
iconst_1 //准备常量1
iadd //自增 线程内i=1
putstatic i //将修改后的值存入静态变量,静态变量i=1
getstatic i //获取静态变量i的值,线程内i=1
iconst_1 //准备常量1
isub //自减 线程内i=0
putstatic i //将修改后的值存入静态变量 静态变量i=0
getstatic i //线程1-获取静态变量i的值,线程内i=0
getstatic i //线程2-获取静态变量i的值,线程内i=0
iconst_1 //线程1-准备常量1
iadd //线程1-自增 线程内i=1
putstatic i //线程1-将修改后的值存入静态变量,静态变量i=1
iconst_1 //线程2-准备常量1
iadd //线程2-自减 线程内i=-1
putstatic i //线程2-将修改后的值存入静态变量,静态变量i=-1
getstatic i //线程1-获取静态变量i的值,线程内i=0
getstatic i //线程2-获取静态变量i的值,线程内i=0
iconst_1 //线程1-准备常量1
iadd //线程1-自增 线程内i=1
iconst_1 //线程2-准备常量1
iadd //线程2-自增 线程内i=-1
putstatic i //线程2-将修改后的值存入静态变量,静态变量i=-1
putstatic i //线程1-将修改后的值存入静态变量,静态变量i=1
synchronized (对象){
要作为原子操作代码
}
public class Demo01 {
static int i = 0;
static Object obj = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
for (int j = 0;
j < 5000;
j++) {
synchronized (obj){
i++;
}
}
});
Thread t2 = new Thread(()->{
for (int j = 0;
j < 5000;
j++) {
synchronized (obj){
i--;
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
}
i=0
可以想象Thread1和Thread2两个人,要执行的是一个房间,而object是一把锁,Thread1进入房间(执行)后房间上锁,Thread2要想进入房间除非Thread1出房间
注意上例中Thread1和Thread2必须用synchronized锁住同一个obj对象,如果ti锁住的是m1对象,t2锁住的是m2对象,就好比两个人分别进入了两个不同的房间,没法起到同步的效果1.2可见性 先来看一个现象,main线程对run变量的修改对于t线程不可见,导致了t线程无法停止:
public class Demo02 {
static boolean run = true;
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
while (run){
//...
}
});
t.start();
Thread.sleep(1000);
run = false;
}
}
他可以用来修饰成员变量和静态成员变量,他可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取值。线程操作volatile变量都是直接操作主存
public class Demo02 {
static volatile boolean run = true;
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
while (run){
//...
}
});
t.start();
Thread.sleep(1000);
run = false;
}
}
上面例子体现的实际就是可见性,他保证的是在多个线程之间,一个线程对volatile变量的修改对另一个线程可见,不能保证原子性,仅用在一个写线程,多个读线程的情况:上例从字节码理解是这样的
getstatic run //线程t获取 run true
getstatic run //线程t获取 run true
getstatic run //线程t获取 run true
getstatic run //线程t获取 run true
putstatic run //线程main修改run为false,仅此一次
getstatic run //线程t获取 run false
getstatic i //线程1-获取静态变量i的值,线程内i=0
getstatic i //线程2-获取静态变量i的值,线程内i=0
iconst_1 //线程1-准备常量1
iadd //线程1-自增 线程内i=1
putstatic i //线程1-将修改后的值存入静态变量,静态变量i=1
iconst_1 //线程2-准备常量1
iadd //线程2-自减 线程内i=-1
putstatic i //线程2-将修改后的值存入静态变量,静态变量i=-1
public void println() {
newLine();
}private void newLine() {
try {
synchronized (this) {
ensureOpen();
textOut.newLine();
textOut.flushBuffer();
charOut.flushBuffer();
if (autoFlush)
out.flush();
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}
int num = 0;
boolean ready = false;
//线程1执行此方法
public void actor1(I_Result r){
if(ready){
r.r1 = num + num;
}else{
r.r1 = 1;
}
}//线程2执行此方法
public void actor2(I_Result r){
num = 2;
ready = true;
}
I_Result是一个对象,有属性r1用来保存结果,那么可能的结果有几种呢?1.3.1解决方法 volatile修饰的变量可以禁用指令重排
除了结果为1,4的情况外,结果还有可能为0
这种情况为:
线程2执行ready=true,切换到线程1,进入if分支,相加为0,再切回线程2执行num=2
这种现象叫做指令重排,是JIT编译器在运行时做的一些优化
static int i;
static int j;
//在某个线程内执行如下赋值操作
i = ...;
//较为耗时的操作
j = ...;
i = ...;
//较为耗时的操作
j = ...;
j = ...;
i = ...;
//较为耗时的操作
public class Singleton {
public Singleton() {}
private static Singleton INSTANCE = null;
public static Singleton getInstance(){
//实例没创建,才会进入内部的synchronized代码块
if (INSTANCE == null){
synchronized (Singleton.class){
//也许有其他线程已经创建实例,所以再判断一次
if (INSTANCE == null){
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
new#2
dup
invokespecial #3
putstatic #4
时间1 t1 线程执行到INSTANCE = new Singleton();
时间2 t1 线程分配空间,为Singleton对象生成了引用地址(0处)
时间3 t1线程将引用地址赋值给INSTANCE, 这时INSTANCE != null (7处)
时间4 t2 线程进入getInstance()方法,发现INSTANCE != null (synchronized块外) ,直接返回
INSTANCE 时间5 t1 线程执行Singleton的构造方法(4处)
下一篇:内存管理