JUC学习笔记——共享模型之管程( 四 )


/*局部变量*/ // 源代码展示:public static void test1() {int i = 10;i++;}// 我们查看底层代码:每个线程调用 test1() 方法时局部变量 i,会在每个线程的栈帧内存中被创建多份,因此不存在共享public static void test1(); descriptor: ()V flags: ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=1, args_size=0 0: bipush 10 2: istore_0 3: iinc 0, 1 6: return LineNumberTable: line 10: 0 line 11: 3 line 12: 6 LocalVariableTable: Start Length Slot Name Signature 340iI/*成员变量*/// 源代码展示:static final int THREAD_NUMBER = 2;static final int LOOP_NUMBER = 200;public static void main(String[] args) {// 一个test对象ThreadUnsafe test = new ThreadUnsafe();// 两个线程去操纵for (int i = 0; i < THREAD_NUMBER; i++) {// 均执行method1方法new Thread(() -> {test.method1(LOOP_NUMBER);}, "Thread" + i).start();}}class ThreadUnsafe {// 这里的list是属于对象的,创建在堆中,属于线程共同操纵对象ArrayList<String> list = new ArrayList<>();// 不断调用method2,3方法200次public void method1(int loopNumber) {for (int i = 0; i < loopNumber; i++) {// { 临界区, 会产生竞态条件method2();method3();// } 临界区}}private void method2() {list.add("1");}private void method3() {list.remove(0);}}// 运行结果的其中一种:如果线程2 还未 add,线程1 remove 就会报错Exception in thread "Thread1" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0 at java.util.ArrayList.rangeCheck(ArrayList.java:657) at java.util.ArrayList.remove(ArrayList.java:496) at cn.itcast.n6.ThreadUnsafe.method3(TestThreadSafe.java:35) at cn.itcast.n6.ThreadUnsafe.method1(TestThreadSafe.java:26) at cn.itcast.n6.TestThreadSafe.lambda$main$0(TestThreadSafe.java:14) at java.lang.Thread.run(Thread.java:748) /*成员变量局部优化*/// 源代码展示static final int THREAD_NUMBER = 2;static final int LOOP_NUMBER = 200;public static void main(String[] args) {// 一个test对象ThreadUnsafe test = new ThreadUnsafe();// 两个线程去操纵for (int i = 0; i < THREAD_NUMBER; i++) {// 均执行method1方法new Thread(() -> {test.method1(LOOP_NUMBER);}, "Thread" + i).start();}}class ThreadSafe {public final void method1(int loopNumber) {// 这里将list变为局部变量,每个线程独自在自己的栈中创建,就不会产生安全问题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");}private void method3(ArrayList<String> list) {list.remove(0);}}/*方法public化缺陷*/// 源代码展示:class ThreadSafe {public final void method1(int loopNumber) {ArrayList<String> list = new ArrayList<>();for (int i = 0; i < loopNumber; i++) {method2(list);method3(list);}}public void method2(ArrayList<String> list) {list.add("1");}public void method3(ArrayList<String> list) {list.remove(0);}}class ThreadSafeSubClass extends ThreadSafe{@Overridepublic void method3(ArrayList<String> list) {new Thread(() -> {list.remove(0);}).start();}}// 如果我们将method2,3改为public,就可能会导致其他线程直接调用method2,3导致安全性问题// 同时甚至可能出现其他子类继承父类导致修改原方法,同时创建一个线程导致多线程问题出现常见线程安全类我们在下面介绍一下我们常用的线程安全类:

  • String
  • Integer
  • StringBuffer
  • Random
  • Vector
  • Hashtable
  • java.util.concurrent 包下的类
同时我们需要知道无法改变的类型也是线程安全的:
  • String、Integer 等都是不可变类,因为其内部的状态不可以改变,因此它们的方法都是线程安全的
  • 这里需要注意:String的各类修改方法都是直接创建一个新的String类型而不是在String本体进行增删
我们再进行简单解释: