1 为什么要加锁
所先JVM将内存划分成2个区域
- 主内存:所有线程共享的内存区域,存储所有的共享变量
- 工作内存:每个线程独有的内存区域,存储该线程使用到的共享变量的副本
线程对变量的操作(读取、赋值)必须在工作内存完成,从主内存读取变量到工作内存,在对工作内存的变量进行操作,返回主内存。因此,当不同线程同时操作一个变量时,就会出现操作的数据实际值和主内存不一致。
2 需要解决那些问题
2.1 可见性问题
当一个共享变量被其他线程改变值后,其他线程无法立即感知到这个变量的值。
2.2 原子性
无法确保对同一个共享变量操作是原子性的。
2.3 指令重排
操作系统可能对一些操作指令进行重新排序,从而提高运行速度。例如一个对象赋值操作。 A a = new A()。 单例模式时,会判断对象是否是空对象,所有要两次判断非空
原本顺序:
1. 分配内存空间
2. 初始化对象
3. 将引用指向内存
重排序后顺序:
1. 分配内存空间
2. 将引用指向内存
3. 初始化对象
3. 初始化对象
2 常见锁类型
2.1 Synchronized
public class SynchronizedDemo {int i = 0;public static void main(String[] args) {SynchronizedDemo synchronizedDemo = new SynchronizedDemo();synchronizedDemo.init1();try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(synchronizedDemo.i);}public void init1() {Thread[] threads = new Thread[100];for (int i = 0; i < 100; i++) {threads[i] = new Thread(this::fun1);threads[i].start();}}/*** 方法上锁*/synchronized void fun1() {i++;}}