java多線程知識點匯總(二)多線程實例解析


本實驗主要考察多線程對單例模式的操作,和多線程對同一資源的讀取,兩個知識。實驗涉及到三個類:

1)一個pojo類Student,包括set/get方法。

2)一個線程類,設置student的成員變量age和name的值為111和111

3)另一個線程類,設置student的成員變量age和name的值為222和2222

4)main類,for循環200次,分別創建200個線程1和線程2對同一資源訪問。(共400個線程)

 

1.第一種情況:餓漢式單例模式保證多線程操控的是同一對象

//餓漢式單例模式pojo類
public
class Student { private String age = "12"; private String name = "Tome"; private static Student student = new Student();//類加載時候創建對象 public String getNameAndAge() { return name+":"+age; } public void setNameAndAge(String name,String age) { this.name = name; this.age = age; } private Student() //構造函數私有化 { } public static Student GetInstace() { //方法區函數,靜態函數 return student; } }

線程2類:

public class MyThread extends Thread {
    @Override
    public void run() {
        // TODO Auto-generated method stub
        System.out.println(Student.GetInstace().hashCode());
    }
}

測試類,創建並啟動400個線程:

public class AppMain implements Runnable{

    public static void main(String[] args) {
        AppMain appMain = new AppMain();
        for(int i =0;i<200;i++)
        {
        Thread thread1 = new Thread(appMain);//線程1
        MyThread thread2 = new MyThread();//線程2
        thread1.start();
        thread2.start();
        }
    }    
    @Override
    public void run() {
        // TODO Auto-generated method stub
        System.out.println(Student.GetInstace().hashCode());
    }
}

結果:

 

2.第二種情況:共享資源的寫方法不設置任何同步,多個線程可以交叉寫數據

  public String getNameAndAge() {
        return name+":"+age;
    }
     public void setNameAndAge(String name,String age) { //沒有設置任何寫同步
        this.name = name;
        this.age = age;
    }

倆線程操控類:

public class MyThread extends Thread {
    @Override
    public void run() {
        // TODO Auto-generated method stub
        Student.GetInstace().setNameAndAge("111", "111");//設置name和age值為1
System.out.println(Student.GetInstace().getNameAndAge();); } }

線程2

public class AppMain implements Runnable{   
public static void main(String[] args) { AppMain appMain = new AppMain(); for(int i =0;i<200;i++) { Thread thread1 = new Thread(appMain); MyThread thread2 = new MyThread(); thread1.start(); thread2.start(); } } @Override public void run() { // TODO Auto-generated method stub Student.GetInstace().setNameAndAge("222", "2222");//設置name和age為2
System.out.println(Student.GetInstace().getNameAndAge();); } }

執行結果:

3.第三種情況:共享資源的寫方法設置同步synchronized,保證同一時刻只有一個線程才能執行寫,執行完后才釋放鎖。

  public String getNameAndAge() {
        return name+":"+age;
    }
    synchronized public void setNameAndAge(String name,String age) { //寫方法設置synchronized了
        this.name = name;
        this.age = age;
    }

測試類添加打印:

public static void main(String[] args) {
        AppMain appMain = new AppMain();
        for(int i =0;i<200;i++)
        {
        Thread thread1 = new Thread(appMain);
        MyThread thread2 = new MyThread();
        thread1.start();
        thread2.start();
        System.out.println(Student.GetInstace().getNameAndAge());//添加打印,顯示name和age值
        }
    }    

這樣就能多個線程按序設置name和set值了。但為什么測試結果依然有臟數據呢?比如111:222這種臟數據呢?

答案:因為沒設置單例對象讀get方法的鎖,這樣讀方法可以隨時獲取值,即使set線程還沒執行完,因為沒有synchronized限制可以隨時訪問。

4.第四種情況,共享資源的讀方法不同步不synchronized,方便隨時讀取不受鎖的限制。但就像之前說的,會讀到寫線程還沒執行完時的數據,造成數據混亂。因為讀線程可以隨時讀,沒有鎖的限制。

  public String getNameAndAge() { //讀方法沒有做同步synchronized處理,可以隨時讀取,就可以讀出寫線程未執行完的中間數據 return name+":"+age;
    }
    synchronized public void setNameAndAge(String name,String age) {
        this.name = name;
        this.age = age;
    }

操作結果:

5.第五種情況,讀方法也設置synchronized,鎖的對象也是this。保證寫的時候不能讀,保證讀的時候不能寫。即讀寫用同一個鎖。

    synchronized public String getNameAndAge() {
        return name+":"+age;
    }
    synchronized public void setNameAndAge(String name,String age) {
        this.name = name;
        this.age = age;
    }

測試結果:

這樣數據就全部准確了,但是這樣效率很低,因為讀寫共同設置一個鎖。讀的時候不能寫,寫的時候不能讀。全部都是按序來訪問。

結論:當多線程共同訪問同一資源時候,此共享對象的讀寫方法,要都設置同一個鎖,保證寫的時候不能讀,讀的時候不能寫,且讀寫都是按序執行。才能保證數據的准確性。

同時,也說明了,沒有設置鎖的方法可以隨時執行,隨時執行,隨時可能被cpu調度以至打斷線程的執行,以至讀到線程執行一半產生的臟數據。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM