线程安全问题
程序员文章站
2022-05-04 10:09:46
...
一、可见性、有序性
// 1、 jre/bin/server 放置hsdis动态链接库
// 测试代码 将运行模式设置为-server, 变成死循环 。 没加默认就是client模式,就是正常(可见性问题)
// 2、 通过设置JVM的参数,打印出jit编译的内容 (这里说的编译非class文件),通过可视化工具jitwatch进行查看
// -server -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:+LogCompilation -XX:LogFile=jit.log
// 关闭jit优化-Djava.compiler=NONE
public class VisibilityDemo {
private volatile boolean flag = true;
public static void main(String[] args) throws InterruptedException {
VisibilityDemo demo1 = new VisibilityDemo();
Thread thread1 = new Thread(new Runnable() {
public void run() {
int i = 0;
// class -> 运行时jit编译 -> 汇编指令 -> 重排序, 热点代码重排序成 if((demo1.flag)){while(true){i++}}
while (demo1.flag) { // 指令重排序
i++;
}
System.out.println(i);
}
});
thread1.start();
TimeUnit.SECONDS.sleep(2);
// 设置is为false,使上面的线程结束while循环
demo1.flag = false;
System.out.println("被置为false了.");
}
}
二、原子性
public class LockDemo1 {
/*volatile*/ int value = 0;
static Unsafe unsafe; // 直接操作内存,修改对象,数组内存....强大的API
private static long valueOffset;
static {
try {
// 反射技术获取unsafe值
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
// 获取到 value 属性偏移量(用于定于value属性在内存中的具体地址)
valueOffset = unsafe.objectFieldOffset(LockDemo1.class.getDeclaredField("value"));
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void add() {
// TODO xx00
// i++;// JAVA 层面三个步骤:读取i的值 、i++、i的值写回主内存
// CAS + 循环 重试
int current;
do {
// 操作耗时的话, 那么 线程就会占用大量的CPU执行时间
//失败就从要修改的地址中重新获取值在操作(自旋锁)
current = unsafe.getIntVolatile(this, valueOffset);
//修改this当前对象的 valueOffset地址的值,先对比线程旧值和地址值是否一致,一致则修改成功返回true
} while (!unsafe.compareAndSwapInt(this, valueOffset, current, current + 1));
// 可能会失败
}
public static void main(String[] args) throws InterruptedException {
LockDemo1 ld = new LockDemo1();
for (int i = 0; i < 2; i++) {
new Thread(() -> {
for (int j = 0; j < 10000; j++) {
ld.add();
}
}).start();
}
Thread.sleep(2000L);
System.out.println(ld.value);
}
}
上一篇: PHP接收上传文件_PHP教程
下一篇: Jenkins