多線程簡單入門之如何實現同步

多線程簡單入門之如何實現同步?

一.什麼是線程安全?

         當多個線程同時或共享同一全局變量或靜態變量時,並做寫的操作時,可能會遇到數據衝突問題,這就是線程安全問題。做讀的操作時是不會發生數據衝突問題的。

        例:模仿兩個窗口20張車票出售情況,代碼如下

class ThreadTestDemo implements  Runnable{
    /**
     * 模擬車票出售
     * 2019/11/26
     */
    private int num=20;
    @Override
    public void run(){
        while (num>0){
            try {
                Thread.sleep(10);   //線程休眠
            }catch (Exception excepton){
                excepton.printStackTrace();
            }
            sale();
        }
    }
    //車票出售方法
    public void sale(){
       if (num>0){
           try {
               Thread.sleep(10);
           }catch (Exception e){
               e.printStackTrace();
           }
           System.out.println(Thread.currentThread().getName()+"出售第"+(20-num+1)+"張車票!");
           num--;
       }
    }
}
public class ThreadTest{
    public static void main(String[] args) {
       ThreadTestDemo threadTestDemo=new ThreadTestDemo();//此處爲多線程共享同一變量
       Thread thread1=new Thread(threadTestDemo);
       thread1.setName("售票窗口1");
       Thread thread2=new Thread(threadTestDemo);
       thread2.setName("售票窗口2");
       thread1.start();
       thread2.start();
    }
}
運行結果如下:

       雖然出售了20張車票,但是會出現兩個窗口同時出售同一張車票的情況,導致衝突出現問題。這就是多個線程同時或共享同一全局變量或靜態變量作寫的操作導致的線程不安全問題。

二.線程安全問題的解決辦法?

        使用synchronize同步代碼塊;JDK1.5併發包lock;同步函數(同步方法)。

         解釋:同步代碼塊也就是使用synchronize包裹起來的代碼塊;同步函數(同步方法)也就是使用synchronize修飾的方法

      1.使用同步代碼塊。

             synchronized(同一個數據){

                  可能會發生線程的衝突問題

             }

         修改上面的“sale()”方法,代碼如下:       

  //車票出售方法
  private Object object=new Object(); //自定義同步代碼鎖(Object對象鎖)
  public void sale(){
    synchronized (object) {
        if (num > 0) {
            try {
                Thread.sleep(10);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "出售第" + (20 - num + 1) + "張車票!");
            num--;
        }
     }
   }

        運行結果如下:

     這樣就實現了兩個窗口不重複出售同一張車票的情況,這也就是線程安全。

2.使用同步函數(同步方法) 。

    修改上面的“sale()”方法,代碼如下:  

//車票出售方法
private Object object=new Object(); //自定義同步代碼鎖(Object對象鎖)
public synchronized void sale(){
        if (num > 0) {
            try {
                Thread.sleep(10);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "出售第" + (20 - num + 1) + "張車票!");
            num--;
        }
    }

  運行結果如下

 該線程也實現了兩個窗口不重複出售同一張車票的情況,這也是線程安全的。

注:同步函數(同步方法)使用的是this鎖。

證明方式:一個線程使用同步函數(同步方法)this鎖,另一個線程使用同步代碼塊對象鎖,如果售票不能同步則會出現數據錯誤。

代碼如下:

class ThreadTestDemo implements Runnable {
    /**
     * 模擬車票出售
     * 2019/11/26
     */
    private int num = 20;
    boolean aa = true;
    private Object object = new Object(); //自定義同步代碼鎖(Object對象鎖)

    @Override
    public void run() {
        if (aa) {
            while (true) {
                synchronized (object) {
                    if (num > 0) {
                        try {
                            Thread.sleep(10);   //線程休眠
                        } catch (Exception excepton) {
                            excepton.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "出售第" + (20 - num + 1) + "張車票!");
                        num--;
                    }
                }
            }
        } else {
            while (true) {
                sale();
            }
        }
    }
    //車票出售方法

    public synchronized void sale() {
        if (num > 0) {
            try {
                Thread.sleep(10);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "出售第" + (20 - num + 1) + "張車票!");
            num--;
        }
    }

}

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        ThreadTestDemo threadTestDemo = new ThreadTestDemo();//此處爲多線程共享同一變量
        Thread thread1 = new Thread(threadTestDemo);
        thread1.setName("售票窗口1");
        Thread thread2 = new Thread(threadTestDemo);
        thread2.setName("售票窗口2");
        thread1.start();
        threadTestDemo.aa = false;
        Thread.sleep(40);
        thread2.start();
    }
}

運行結果如下:

正常同步售票,說明線程同步。

     靜態同步函數。

        什麼是靜態同步函數?

        方法上加上static關鍵字,使用sysynchronize修飾,靜態同步函數使用的鎖是 該函數所屬類字節碼文件對象。

可以用getClass方法獲取,也可以用當前類的 類名.class表示。

三.什麼是多線程死鎖?

        同步嵌套同步,導致死鎖無法釋放。

class ThreadTestDemo implements Runnable {
    /**
     * 模擬車票出售
     * 2019/11/26
     */
    private int num = 100;
    boolean aa = true;
    private Object object = new Object(); //自定義同步代碼鎖(Object對象鎖)

    @Override
    public void run() {
        if (aa) {
            while (true) {
                synchronized (object) {
                        sale();
                    }
                }
            }else{
                while (true) {
                    sale();
                }
            }
    }
    //車票出售方法

    public synchronized void sale() {
        synchronized (object) {
            if (num > 0) {
                try {
                    Thread.sleep(40);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "出售第" + (100 - num + 1) + "張車票!");
                num--;
            }
        }
    }

}

public class ThreadTest {
    public static void main(String[] args) throws InterruptedException {
        ThreadTestDemo threadTestDemo = new ThreadTestDemo();//此處爲多線程共享同一變量
        Thread thread1 = new Thread(threadTestDemo);
        thread1.setName("售票窗口1");
        Thread thread2 = new Thread(threadTestDemo);
        thread2.setName("售票窗口2");
        thread1.start();
        Thread.sleep(40);
        threadTestDemo.aa = false;

        thread2.start();
    }
}
運行結果:

所以線程被鎖住了。解決辦法:不要在同步中嵌套同步。

該博客爲觀看螞蟻課堂教學視頻後整理的學習筆記,如有問題,歡迎指出、交流。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章