攀枝花市建设银行网站在线制作图片及图片处理工具免费
web/
2025/10/6 19:01:01/
文章来源:
攀枝花市建设银行网站,在线制作图片及图片处理工具免费,移动端软件开发,wordpress原生html5播放器2 可见性
2.1 什么是可见性
可见性问题是基于CPU位置出现的#xff0c;CPU处理速度非常快#xff0c;相对CPU来说#xff0c;去主内存获取数据这个事情太慢了#xff0c;CPU就提供了 L1#xff0c;L2#xff0c;L3的三级缓存#xff0c;每次去主内存拿完数据后#x…2 可见性
2.1 什么是可见性
可见性问题是基于CPU位置出现的CPU处理速度非常快相对CPU来说去主内存获取数据这个事情太慢了CPU就提供了 L1L2L3的三级缓存每次去主内存拿完数据后就会存储到CPU的三级缓存每次去三级缓存拿数据效率肯定会提升。 这就带来了问题现在CPU都是多核每个线程的工作内存CPU三级缓存都是独立的会告知每个线程中做修改时只改自 己的工作内存没有及时的同步到主内存导致数据不一致问题。 [image] 可见性问题的代码逻辑
private static boolean flag true;
public static void main(String[] args) throws InterruptedException {
Thread t1 new Thread(() - {
while (flag) {
// ....
}System.out.println(t1线程结束);
});
t1.start();
Thread.sleep(10);
flag false;
System.out.println(主线程将flag改为false);
}
2.2 解决可见性的方式
2.2.1 volatile
volatile是一个关键字用来修饰成员变量。 如果属性被volatile修饰相当于会告诉CPU对当前属性的操作不允许使用CPU的缓存必须去和主内存操作volatile的内存语义 ● volatile属性被写当写一个volatile变量JMM会将当前线程对应的CPU缓存及时的刷新到主内存中 ● volatile属性被读当读一个volatile变量JMM会将对应的CPU缓存中的内存设置为无效必须去主内存中重新读取共享变 量 其实加了volatile就是告知CPU对当前属性的读写操作不允许使用CPU缓存加了volatile修饰的属性会在转为汇编之后 后追加一个lock的前缀CPU执行这个指令时如果带有lock前缀会做两个事情 ● 将当前处理器缓存行的数据写回到主内存 ● 这个写回的数据在其他的CPU内核的缓存中直接无效。 总结volatile就是让CPU每次操作这个数据时必须立即同步到主内存以及从主内存读取数据。
private volatile static boolean flag true;
public static void main(String[] args) throws InterruptedException {
Thread t1 new Thread(() - {
while (flag) {
// ....
}
System.out.println(t1线程结束);
});
t1.start();
Thread.sleep(10);
flag false;
System.out.println(主线程将flag改为false);
}
2.2.2 synchronized
synchronized也是可以解决可见性问题的synchronized的内存语义。 如果涉及到了synchronized的同步代码块或者是同步方法获取锁资源之后将内部涉及到的变量从CPU缓存中移除必须去主 内存中重新拿数据而且在释放锁之后会立即将CPU缓存中的数据同步到主内存。
private static boolean flag true;
public static void main(String[] args) throws InterruptedException {
Thread t1 new Thread(() - {
while (flag) {
synchronized (MiTest.class){
//...
}
System.out.println(111);
}
System.out.println(t1线程结束);
});
t1.start();
Thread.sleep(10);
flag false;
System.out.println(主线程将flag改为false);
}
2.2.3 Lock
Lock锁保证可见性的方式和synchronized完全不同synchronized基于他的内存语义在获取锁和释放锁时对CPU缓存做一个 同步到主内存的操作。 Lock锁是基于volatile实现的。Lock锁内部再进行加锁和释放锁时会对一个由volatile修饰的state属性进行加减操作。 如果对volatile修饰的属性进行写操作CPU会执行带有lock前缀的指令CPU会将修改的数据从CPU缓存立即同步到主内存 同时也会将其他的属性也立即同步到主内存中。还会将其他CPU缓存行中的这个数据设置为无效必须重新从主内存中拉取。
private static boolean flag true;
private static Lock lock new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Thread t1 new Thread(() - {
while (flag) {
lock.lock();
try{
//...
}finally {
lock.unlock();
}
}
System.out.println(t1线程结束);
});
t1.start();
Thread.sleep(10);
flag false;
System.out.println(主线程将flag改为false);
}
2.2.4 final
final修饰的属性在运行期间是不允许修改的这样一来就间接的保证了可见性所有多线程读取final属性值肯定是一样。 final并不是说每次取数据从主内存读取他没有这个必要而且final和volatile是不允许同时修饰一个属性的 final修饰的内容已经不允许再次被写了而volatile是保证每次读写数据去主内存读取并且volatile会影响一定的性能就不需要同时修饰。
3 有序性
3.1 什么是有序性
在Java中.java文件中的内容会被编译在执行前需要再次转为CPU可以识别的指令CPU在执行这些指令时为了提升执行 效率在不影响最终结果的前提下满足一些要求会对指令进行重排。 指令乱序执行的原因是为了尽可能的发挥CPU的性能。 Java中的程序是乱序执行的。 Java程序验证乱序执行效果
static int a,b,x,y;
public static void main(String[] args) throws InterruptedException {for (int i 0; i Integer.MAX_VALUE; i) {
a 0;
b 0;
x 0;
y 0;
Thread t1 new Thread(() - {
a 1;
x b;
});
Thread t2 new Thread(() - {
b 1;
y a;
});
t1.start();
t2.start();
t1.join();
t2.join();
if(x 0 y 0){
System.out.println(第 i 次x x ,y y);
}
}
}
单例模式由于指令重排序可能会出现问题 线程可能会拿到没有初始化的对象导致在使用时可能由于内部属性为默认值导致出现一些不必要的问题
private static volatile MiTest test;
private MiTest(){}
public static MiTest getInstance(){
// B
if(test null){
synchronized (MiTest.class){
if(test null){
// A , 开辟空间test指向地址初始化
test new MiTest();
}
}
}
return test;
}
3.2 as-if-serial
as-if-serial语义 不论指定如何重排序需要保证单线程的程序执行结果是不变的。 而且如果存在依赖的关系那么也不可以做指令重排。
// 这种情况肯定不能做指令重排序
int i 0;
i;
// 这种情况肯定不能做指令重排序
int j 200;
j * 100;
j 100;
// 这里即便出现了指令重排也不可以影响最终的结果20100
3.3 happens-before
具体规则
1. 单线程happen-before原则在同一个线程中书写在前面的操作happen-before后面的操作。 2. 锁的happen-before原则同一个锁的unlock操作happen-before此锁的lock操作。
3. volatile的happen-before原则 对一个volatile变量的写操作happen-before对此变量的任意操作。
4. happen-before的传递性原则 如果A操作 happen-before B操作B操作happen-before C操作那么A操作happen-before C操作。
5. 线程启动的happen-before原则同一个线程的start方法happen-before此线程的其它方法。 6. 线程中断的happen-before原则对线程interrupt方法的调用happen-before被中断线程的检测到中断发送的代码。
7. 线程终结的happen-before原则线程中的所有操作都happen-before线程的终止检 测。
8. 对象创建的happen-before原则一个对象的初始化完成先于他的finalize方法调用。 JMM只有在不出现上述8中情况时才不会触发指令重排效果。 不需要过分的关注happens-before原则只需要可以写出线程安全的代码就可以了。
3.4 volatile
如果需要让程序对某一个属性的操作不出现指令重排除了满足happens-before原则之外还可以基于volatile修饰属性从而对 这个属性的操作就不会出现指令重排的问题了。 volatile如何实现的禁止指令重排 内存屏障概念。将内存屏障看成一条指令。 会在两个操作之间添加上一道指令这个指令就可以避免上下执行的其他指令进行重排序。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/88066.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!