1. 程式人生 > >並發編程學習(一)

並發編程學習(一)

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;

   public
synchronized 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修飾時,變量改變時會強制線程執行引擎去主內存中讀取。

並發編程學習(一)