並發編程學習(一)
阿新 • • 發佈:2018-08-18
ssi runt ace bool pre rgs ati atomic exception
基本概念:
synchronized上加static和不加的區別就是有static是類鎖,不加就是對象鎖。
線程安全:當多個線程訪問某一個類(對象或方法)時,這個類始終能表現出正確的行為,那麽這個類就是一個線程安全的。
synchronized:可以在任何對象及方法上加鎖,而加鎖的這段代碼稱為"互斥區"或"臨界區"。
線程安全:
public class MyThread extends Thread{ private int count = 5; public void run(){ count--; System.out.println(this.currentThread().getName()+" count = "+count); } public static void main(String[] args) { MyThread myThread = new MyThread(); Thread t1 = new Thread(myThread,"t1"); Thread t2 = new Thread(myThread,"t2"); Thread t3 = new Thread(myThread,"t3"); Thread t4= new Thread(myThread,"t4"); Thread t5 = new Thread(myThread,"t5"); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } }
輸出結果:
t1 count = 4
t4 count = 2
t3 count = 1
t2 count = 3
t5 count = 0
可以看出並沒有按照我們程序的順序調用,要實現線程安全那就要加synchronized;
publicsynchronized void run(){ count--; System.out.println(this.currentThread().getName()+" count = "+count); }
有時候我們會調用同一對象內的兩個方法,一個加鎖,一個未加鎖,未加鎖的不會受加鎖的影響。
以下程序會出現臟讀:
public class MyObject { private String name = "test"; private String pwd = "123"; public synchronized void setValue(String name,String pwd){ this.name = name; try{ Thread.sleep(2000);//睡眠2秒,但name值已經設置過了 } catch (InterruptedException e) { e.printStackTrace(); } this.pwd = pwd; System.out.println("set結果name="+name+",pwd="+pwd); } public void getValue(){ System.out.println("get結果name="+name+",pwd="+pwd); } public static void main(String[] args) throws InterruptedException { MyObject myObject = new MyObject();//同一對象 Thread t1 = new Thread(new Runnable() {//主線程 @Override public void run() { myObject.setValue("wt","456"); } }); t1.start();//調set方法 Thread.sleep(1000);//休眠一秒調get方法 myObject.getValue(); } }
執行結果:
get結果name=wt,pwd=123
set結果name=wt,pwd=456
原因:調set方法時休眠了兩秒,但name值已經設置過了,get方法沒加鎖,所以取出來是只設置name的值。
public synchronized void getValue(){//get方法加鎖輸出結果就不會出現臟讀
System.out.println("get結果name="+name+",pwd="+pwd);
}
Volatile關鍵字:
作用:就是強制線程到主內存裏去讀取變量,而不去線程工作內存區裏去讀取,從而實現了多個線程間的變量可見,也就是滿足線程安全的可見性。
線程可以執行的操作有use(使用)、assign賦值、load裝載、store存儲、鎖定、解鎖。
主內存的操作有:read,write,lock,unlock每個操作都是原子性的。
volatile一般用於只針對多個線程可見的變量操作,並不能代替synchronzied的同步功能,它不具有原子性,要實現原子 性建議使用atomic類的系統對象,
public class RunThread extends Thread { private boolean isRunning = true; private void setRunning(boolean isRunning){ this.isRunning = isRunning; } public void run(){ System.out.println("進入run方法"); while (isRunning == true){ } System.out.println("線程停止"); } public static void main(String[] args) throws InterruptedException { RunThread rt = new RunThread(); rt.start(); Thread.sleep(3000); rt.setRunning(false); System.out.println("isrunning設置為false"); Thread.sleep(1000); System.out.println("isRunning:"+rt.isRunning); } }
輸出結果:
進入run方法
isrunning設置為false
isRunning:false
程序仍然處於運行中
加入volatile後:private volatile boolean isRunning = true;
執行結果:
進入run方法
isrunning設置為false
線程停止
isRunning:false
出現上面的結果是因為,每個線程都有自己的一個內存區域(這樣運行更快),在程序運行時,把需要的數據裝載到自己的工作內存,而主內存中的數據仍然存在,我們上面程序修改isRunning為false,只是修改了主內存中的數據,線程的工作內存並沒有修改,所以會出現主程序走完了,線程還在執行。
當isRunning被volatile修飾時,變量改變時會強制線程執行引擎去主內存中讀取。
並發編程學習(一)