变量的线程安全分析
成員變量和靜態變量是否線程安全?
- 如果它們沒有共享,則線程安全。
- 如果它們被共享了,根據他們的狀態是否能夠改變,又分兩種情況
- 只是讀操作,則線程安全
- 如果有讀寫操作,則這段代碼是臨界區,需要考慮線程安全
局部變量是否線程安全?
局部變量是線程安全的
但局部變量引用的對象則未必
如果該對象沒有逃離方法的作用范圍,它是線程安全的
如果該對象逃離方法的作用范圍,需要考慮線程安全
局部變量線程安全分析
想一想,這段代碼被多個線程訪問,它是線程安全的嗎?
public static void test1() {int i = 10;i++;}i++不是一個原子操作,它是線程不安全的,也許會從這方面去考慮,但實際上這段代碼是線程安全的。
反編譯為字節碼后,一共有4行指令,第一行指令準備了一個常量10 第二行指令把10賦值給i,第三行指令的時候在局部變量i上進行自增1的操作,第四行指令就return了。
從這幾行代碼來看, 發現局部變量的i++操作和靜態變量的i++的字節碼指令是稍有不同的。incc這個指令只有一行就完成了++操作,就不會像靜態變量的i++一樣指令被分成了多個,影響原子性。
而每個線程調用此方法時,局部變量i會在每個線程的棧幀內存中被創建多份,因此不存在共享。
還記得局部變量是線程私有的嗎?
線程分別在自己的棧幀中完成了i++操作,互不影響。
局部變量引用
在循環里調用method1,method2,method3,讓mothod2在list里加一個元素,再讓method3去remove這個元素。
調用執行后很快的就報錯了。
public class TestThreadSafe {static final int THREAD_NUMBER = 2;static final int LOOP_NUMBER = 200;public static void main(String[] args) {ThreadSafeSubClass test = new ThreadSafeSubClass();for (int i = 0; i < THREAD_NUMBER; i++) {new Thread(() -> {test.method1(LOOP_NUMBER);}, "Thread" + (i+1)).start();}} } class ThreadUnsafe {ArrayList<String> list = new ArrayList<>();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);} } }因為method2和methoed3都調用的同一個對象中的list成員變量。
?進行一些小改動,讓它變得線程安全,讓list成為方法內的局部變量。
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");}private void method3(ArrayList<String> list) {System.out.println(1);list.remove(0);} }分析這段代碼為什么變成了線程安全的?
- list是局部變量,每個線程調用時會創建其不同的實例,沒有共享。
- 而method2的參數是method1中傳遞過來的,與method1引用同一個對象。
- method3的參數分析與method2相同。
?
總結
- 上一篇: luogu P3041 [USACO12
- 下一篇: 卡方检验 两分类实现