java——多線程講解二

多線程是多個線程同時對同一個數據執行相同或不同的任務。要注意的是,這樣的操作很大可能會出現線程安全問題,和線程死鎖問題。
現介紹一下為什么要用同步線程:

public class MyThread implements Runnable {
    private int ticket=10;
    @Override
    public void run() {
       for(int i=0;i<10;i++){
           if(this.ticket>0){
             try {
                 Thread.sleep(100);
             } catch (InterruptedException e) {
                e.printStackTrace();
             }     
             System.out.println(Thread.currentThread().getName()+"正在出售倒數第:"+this.ticket--+"張票");
           }
       }
    }
}
單多個線程訪問同一個對象是,會出現其中一條線程執行業務代碼的時候,CPU的執行權被另一條線程搶走了,通過上面代碼可以明顯看出輸出了負數,這和我們現實生活是完全不存在的。這就是線程安全問題

這使得我們需要通過靜態化ticket,或者是用同步線程來實現我們要的功能。

下面先介紹一下怎么解決線程安全問題的實現;
方式一:可以使用同步代碼塊來實現。
示例:

public class MyThread implements Runnable {
    private int ticket=10;
    @Override
    public synchronized void run() {
       for(int i=0;i<10;i++){
           if(this.ticket>0){
             try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }      
             System.out.println(Thread.currentThread().getName()+"正在出售倒數第:"+this.ticket--+"張票");
           }
       }
    }
}

示例二:使用同步代碼塊來實現

public class MyThread implements Runnable {
    private int ticket=10;
    @Override
    public void run() {
       for(int i=0;i<10;i++){
            synchronized ("鎖"){
                  if(this.ticket>0){
             try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }      
                 System.out.println(Thread.currentThread().getName()+"正在出售倒數第:"+this.ticket--+"張票");
           }
       }
          }
           
    }
}

上面兩種方式都能實現代碼的同步,
線程之所以能同步,是因為加了一個鎖對象,在使用同步函數的時候,鎖對象是對象本身this,而在使用同步代碼塊的時候,鎖對象是自己定義的,可以是任何對象。
建議:要是用同步線程的時候,建議優先使用同步代碼塊,因為同步代碼塊鎖對象是自己定義的,方便對鎖進行操作。這個原因下面將要說到。

通過上面的代碼會發現,我在方法中使用了sleep方法。sleep()方法是Thread類中的一個方法,為什么沒有把它放到上一節將,是因為這個方法有點特殊。這個涉及到線程的各種狀態:
線程開始的時候是創建狀態:(new 對象的時候.)
當調用了start方法的時候,線程處于可以運行狀態,在這個狀態下的線程具備CPU的等待權,但是不具備CPU的執行權。
當線程執行的時候他具備了CPU的執行權。這個時候稱為運行狀態。
當完成任務代碼塊的時候,線程死亡。
在可運行狀態和運行狀態之間還有一個狀態,這個狀態稱為臨時阻塞狀態。進入這個狀態需要調用sleep()或wait()方法。這個時候線程不具備CPU的等待資格,和CPU的執行權。
sleep()方法是自己設置阻塞時間。當時間一到,線程會自動轉換成可運行狀態。他是Thread中的一個方法。當一個線程在同步方法中調用了sleep方法,其它線程不能調用它的其它同步方法。
swit()是Object中的一個方法,當線程調用了這個方法。需要通過notify或者notifyAll方法來喚醒線程。線程阻塞是存放在線程池中的。一個對象對應一個線程池。
當線程阻塞是,線程會自動釋放CPU的等待權和CPU的執行權。但是如果使用的sleep方法,線程是不會釋放鎖對象的。而wait方法是會釋放鎖對象。
下面說下經典的線程通訊:

//產品類
class Product{
    
    String name;  //名字
    
    double price;  //價格
    
    boolean flag = false; //產品是否生產完畢的標識,默認情況是沒有生產完成。
    
}

//生產者
class Producer extends Thread{
    
    Product  p ;    //產品
    
    public Producer(Product p) {
        this.p  = p ;
    }
    
    
    
    @Override
    public void run() {
        int i = 0 ; 
        while(true){
         synchronized (p) {
            if(p.flag==false){
                 if(i%2==0){
                     p.name = "蘋果";
                     p.price = 6.5;
                 }else{
                     p.name="香蕉";
                     p.price = 2.0;
                 }
                 System.out.println("生產者生產出了:"+ p.name+" 價格是:"+ p.price);
                 p.flag = true;
                 i++;
                 p.notifyAll(); //喚醒消費者去消費
            }else{
                //已經生產 完畢,等待消費者先去消費
                try {
                    p.wait();   //生產者等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
             
        }   
      } 
    }
}


//消費者
class Customer extends Thread{
    
    Product p; 
    
    public  Customer(Product p) {
        this.p = p;
    }
    
    
    @Override
    public void run() {
        while(true){
            synchronized (p) {  
                if(p.flag==true){  //產品已經生產完畢
                    System.out.println("消費者消費了"+p.name+" 價格:"+ p.price);
                    p.flag = false; 
                    p.notifyAll(); // 喚醒生產者去生產
                }else{
                    //產品還沒有生產,應該 等待生產者先生產。
                    try {
                        p.wait(); //消費者也等待了...
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }   
    }
}

public class Demo5 {
    
    public static void main(String[] args) {
        Product p = new Product();  //產品
        //創建生產對象
        Producer producer = new Producer(p);
        //創建消費者
        Customer customer = new Customer(p);
        //調用start方法開啟線程
        producer.start();
        customer.start();
        
        
    }
    
}

線程死鎖的體現:

lass DeadLock extends Thread{
    
    public DeadLock(String name){
        super(name);
    }
    
    
    public void run() {
        if("張三".equals(Thread.currentThread().getName())){
            synchronized ("遙控器") {
                System.out.println("張三拿到了遙控器,準備 去拿電池!!");
                synchronized ("電池") {
                    System.out.println("張三拿到了遙控器與電池了,開著空調爽歪歪的吹著...");
                }
            }
        }else if("狗娃".equals(Thread.currentThread().getName())){
            synchronized ("電池") { 
                System.out.println("狗娃拿到了電池,準備去拿遙控器!!");
                synchronized ("遙控器") {
                    System.out.println("狗娃拿到了遙控器與電池了,開著空調爽歪歪的吹著...");
                }
            }
            
        }   
    }
    
    
}

public class Demo2 {

    public static void main(String[] args) {
        DeadLock thread1 = new DeadLock("張三");
        DeadLock thread2 = new DeadLock("狗娃");
        //開啟線程
        thread1.start();
        thread2.start();
        
        
    }
    
}

死鎖現象出現 的根本原因:

  1. 存在兩個或者兩個以上的線程。
  2. 存在兩個或者兩個以上的共享資源。

【注】講解可能有不足的地方,希望各位小伙伴能幫我補充不足之處,謝謝!
復制得來終覺淺,要想知道自己敲!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Java多線程學習 [-] 一擴展javalangThread類 二實現javalangRunnable接口 三T...
    影馳閱讀 2,987評論 1 18
  • 本文主要講了java中多線程的使用方法、線程同步、線程數據傳遞、線程狀態及相應的一些線程函數用法、概述等。 首先講...
    李欣陽閱讀 2,493評論 1 15
  • 寫在前面的話: 這篇博客是我從這里“轉載”的,為什么轉載兩個字加“”呢?因為這絕不是簡單的復制粘貼,我花了五六個小...
    SmartSean閱讀 4,791評論 12 45
  • 該文章轉自:http://blog.csdn.net/evankaka/article/details/44153...
    加來依藍閱讀 7,381評論 3 87
  • HTML表格概述 表格的基本結構 表格的基本標簽有標簽 (表格), 標簽(表格行), 標簽(表格單元格)。 標簽和...
    進擊的小明閱讀 397評論 0 2