# 什么是临界区:一段代码内,如果存在对共享资源的读写操作,称这段代码块为临界区

# 静态条件:多个线程在临界区内执行,多个代码的执行序列不同而导致结果无法预测

# 1.语法

synchronized(对象) // 线程1, 线程2(blocked)
{
    临界区
}

# 2.方法上的synchronized

class Test{
    public synchronized void test() {
    }
}
等价于
class Test{
    public void test() {
        synchronized(this) {
        }
    }
}
  • 相当于获得了this对象的锁(简单来说是同一个对象的锁,比如你new了这个对象出来,不管有多少个线程只要执行这个上面这种带synchronized的方法,所有线程抢占的都是你new出来的这个对象实例的锁)
class Test{
    public synchronized static void test() {
    }
}
等价于
class Test{
    public static void test() {
        synchronized(Test.class) {
        }
    }
}
  • 这种情况就相当于锁住了这个类对象(一般来说类对象在内存中只有一份(类只有一个,但可以new出来多个对象实例),所以无论你new出了多少个对象,执行上面带synchronized的方法,都抢占的是同一把锁)

# 3.变量的线程安全分析

  • 成员变量和静态变量是否线程安全?
    • 如果它们没有共享,则线程安全
    • 如果它们被共享了,根据它们的状态是否能够改变,又分两种情况
      • 如果只有读操作,则线程安全
      • 如果有读写操作,则这段代码是临界区,需要考虑线程安全
  • 局部变量是否线程安全?
    • 局部变量是线程安全的
    • 但局部变量引用的对象则未必
      • 如果该对象没有逃离方法的作用访问,它是线程安全的
      • 如果该对象逃离方法的作用范围,需要考虑线程安全
  • 例1
public static void test1() {
   int i = 10;
   i++;
}
  • 这种情况下,每个线程调用方法test1()的局部变量i时,会在每个线程的栈帧内存中被创建多份,因此不存在共享。(意思就是无论每个线程调用test1时会单独创建一个栈帧来执行这个方法,i变量只在这个栈帧中有效,因此这种情况就是线程安全的)
  • 例2
class ThreadSafe {
    public final void method1(int loopNumber) {
        ArrayList<String> list = new ArrayList<>();
        for (int i = 0; i < loopNumber; i++) {
            method2(list);
            method3(list);
        }
    }
    private void method2(ArrayList<String> list) {
        list.add("1");
    }
    public void method3(ArrayList<String> list) {
        list.remove(0);
    }
}
class ThreadSafeSubClass extends ThreadSafe{
    @Override
    public void method3(ArrayList<String> list) {
        new Thread(() -> {
            list.remove(0);
        }).start();
    }
}
  • 这种情况会出现线程安全问题,简单来说就是当你有一个线程之心了method1()方法,然后继续向下执行,执行到method3()方法时又创建了一个线程,这种情况就相当于你现在有俩个线程都可以去访问list变量,肯定会出现线程安全的问题。