线程状态
6种状态:
- start之前是new状态
操作系统层面的划分(5种状态)
防止死锁
- 顺序加锁
tryLock尝试加锁,失败就释放已加的锁。
public static boolean tryTransfer(Account from,Account to,double money) throws NoEnoughMoney {
if(from.tryLock()) {
try {
if(to.tryLock()) {
try {
if(from.getMoney() >= money) {
from.reduce(money);
to.add(money);
} else {
throw new NoEnoughMoney();
}
return true;
} finally {
to.unlock();
}
}
} finally {
from.unlock();
}
}
return false;
}
volatile
单利模式为什么要volatile
为什么volatile不能保证原子性
每个线程都会把 i 的值读入到 CPU 缓存中,再执行 + 1 操作,然后把 + 1 之后的值写入内存。多线程有问题。
volatile是如何保存有序性和可见性的?
通过内存屏障来保证有序性、可见性【final也是通过内存屏障来保障可见性】
- 内存屏障能禁止指令重排
- 看完这篇volatile,估计面试官再也不敢问你了! - 知乎
- 通过MESI(基于失效的缓存一致性协议)来保证可见性【不全面】
MESI
缓存一致性协议MESI 和 volatile什么关系?
volatile定义了语义(可见性、有序性),实现过程使用了MESI特性。还需要其他手段。
为什么有MESI了还要volatile?你真的了解volatile吗我没有在划水的博客-CSDN博客mesi与volatile
指令重排
public class PossibleReordering {
static int x = 0, y = 0;
static int a = 0, b = 0;
public static void main(String[] args) throws InterruptedException {
Thread one = new Thread(new Runnable() {
public void run() {
a = 1;
x = b;
}
});
Thread other = new Thread(new Runnable() {
public void run() {
b = 1;
y = a;
}
});
one.start();
other.start();
one.join();
other.join();
System.out.println(“(” + x + “,” + y + “)”);
}
运行结果可能为(1,0)、(0,1)或(1,1) 也可能是(0,0).
JVM内存模型、指令重排、内存屏障概念解析 - 陈洋Cy - 博客园
@sun.misc.Contended避免伪共享【代替字节填充】
伪共享
缓存行被读入多核的cache中,多核同时修改缓存行,因为MESI,导致对方的缓存行失效。
- 缓存行的大小是64个字节
- disruptor高性能队列中使用字节填充来避免伪共享
long是8字节,前后填充56,保障8字节内容不被其他内容影响
- Java8为了方便,提供了@sun.misc.Contended避免伪共享
- 需要JVM参数配添加上-XX:-RestrictContended
longAdder使用了@sun.misc.Contended
LongAdder原理
有一个base变量,和一个cells数组,累计求和
- 初始化cell数组长度为2
- 使用@sun.misc.Contended,使不同的cell对象保存在不同的缓存行
concurrentHashMap也使用类似LongAdder的方式来统计大小
加锁不能在String Integer对象上加
Integer,String都是不可变变对象,一但对他们进行赋值就会变成新的对象,加的锁就失效了
JMM & happens-before
JMM为了防止指令重排定义了happens before关系,具体实现通过内存屏障来实现。
JMM对特殊Java语义的特殊规则支持
- volatile
- synchroized
- Java虚拟机规范中定义了Java内存模型(Java Memory Model,JMM),用于屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到一致的并发效果
- 在JMM中,如果一个操作执行的结果需要对另一个操作可见(两个操作既可以是在一个线程之内,也可以是在不同线程之间),那么这两个操作之间必须要存在happens-before关系
- happens-before是指令有序性的规定,是保证有序性的抽象概念,利用内存屏障技术对该规定进行实现
- Java编译器在生成指令序列的适当位置会插入内存屏障指令来禁止特定类型的处理器重排序。