java多執行緒——執行緒之間的可見性
阿新 • • 發佈:2019-02-01
目錄
一、簡介
我們知道執行緒在工作的時候有自己的私有記憶體,工作記憶體。程式執行的時候從主記憶體拉取需要的變數到工作記憶體,處理完再返回主記憶體。這篇文章總結哪些程式碼會使執行緒去主記憶體拉取變數。
二、volatile
volatile修飾的變數,不論什麼語句都會從主記憶體拉取變數。
public class Test01 { public volatile boolean b = true; public void m() { System.out.println("start"); while(this.b){ } System.out.println("end"); } public static void main(String[] args) { Test01 test = new Test01(); new Thread(new Runnable() { @Override public void run() { test.m(); } }).start(); try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) { e.printStackTrace(); } System.out.println("set"); test.b = false; } }
該程式能順利完成,不會死迴圈。
三、synchronized
呼叫其他synchronized加鎖的程式碼塊,系統會從主記憶體拉取變數
public class Test01 { public boolean b = true; public Object lock = new Object(); public void m() { System.out.println("start"); while(this.b){ synchronized(lock) { } } System.out.println("end"); } public static void main(String[] args) { Test01 test = new Test01(); new Thread(new Runnable() { @Override public void run() { test.m(); } }).start(); try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) { e.printStackTrace(); } System.out.println("set"); test.b = false; } }
這種情況同樣不會死迴圈。
注意,synchronized加鎖後用到的變數才會從主記憶體拉取。
public class Test01 { public boolean b = true; public Object lock = new Object(); public void m() { System.out.println("start"); synchronized(lock) { while(this.b){ } } System.out.println("end"); } public static void main(String[] args) { Test01 test = new Test01(); new Thread(new Runnable() { @Override public void run() { test.m(); } }).start(); try { TimeUnit.SECONDS.sleep(1); } catch (Exception e) { e.printStackTrace(); } System.out.println("set"); test.b = false; } }
這種情況,程式會死迴圈。
四、不會從主記憶體拉取的操作
只要不影響到變數的值,變數不參與運算,則不會從主記憶體中拉取,比如while,if,賦值給其他變數,作為方法引數等
public class Test01 {
public boolean b = true;
public Object lock = new Object();
public void m() {
System.out.println("start");
while(this.b){
doSomething();
if(b) {
}
boolean value = b;
}
System.out.println("end");
}
public void doSomething() {
}
public static void main(String[] args) {
Test01 test = new Test01();
new Thread(new Runnable() {
@Override
public void run() {
test.m();
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("set");
test.b = false;
}
}
這種情況,程式會死迴圈。
如果呼叫了被synchronized加鎖的方法,或者方法中有被synchronized加鎖的程式碼塊,那麼變數將會從主記憶體中拉取。
public class Test01 {
public boolean b = true;
public Object lock = new Object();
public void m() {
System.out.println("start");
while(this.b){
doSomething();
}
System.out.println("end");
}
public void doSomething() {
synchronized(lock) {
}
}
public static void main(String[] args) {
Test01 test = new Test01();
new Thread(new Runnable() {
@Override
public void run() {
test.m();
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("set");
test.b = false;
}
}
呼叫的synchronized加鎖的方法即使鎖的是毫不相干的物件,同樣會使得變數從主記憶體拉取
public class Test01 {
public boolean b = true;
public Object lock = new Object();
public void m() {
System.out.println("start");
while(this.b){
System.out.println(this.b);
}
System.out.println("end");
}
public static void main(String[] args) {
Test01 test = new Test01();
new Thread(new Runnable() {
@Override
public void run() {
test.m();
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("set");
test.b = false;
}
}
這裡的System.out.println鎖的是PrintStream的例項,更是毫不相關
這種情況,程式不會死迴圈。
五、從主記憶體中拉取的操作
變數被賦值,變數參與計算,比如加減乘除,字串拼接,++,--等操作,執行緒休眠會使從主記憶體中拉取變數。
public class Test01 {
public boolean b = true;
public Object lock = new Object();
public void m() {
System.out.println("start");
while(this.b){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("end");
}
public static void main(String[] args) {
Test01 test = new Test01();
new Thread(new Runnable() {
@Override
public void run() {
test.m();
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("set");
test.b = false;
}
}
public class Test01 {
public boolean b = true;
public Object lock = new Object();
public void m() {
System.out.println("start");
while(this.b){
String str = b + "";
}
System.out.println("end");
}
public static void main(String[] args) {
Test01 test = new Test01();
new Thread(new Runnable() {
@Override
public void run() {
test.m();
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("set");
test.b = false;
}
}
這種情況,程式不會死迴圈