1. 程式人生 > 實用技巧 >【Java多執行緒 33】

【Java多執行緒 33】

一、執行緒的幾中狀態

JDK中用Thread.State類定義了執行緒的幾種狀態

要想實現多執行緒,必須在主執行緒中建立新的執行緒物件。Java語言使用Thread類及其子類的物件來表示執行緒,在它的一個完整的生命週期中通常要經歷如下五種狀態:

  • 新建:當一個Thread類或其子類的物件被宣告並建立時,新生的執行緒物件處於新建狀態
  • 就緒:處於新建狀態的執行緒被start()後,將進入執行緒佇列等待CPU時間片,此時它已具備了執行的條件,只是沒分配到CPU資源
  • 執行:當就緒的執行緒被排程並獲得CPU資源時,便進入執行狀態,run()方法定義了執行緒的操作和功能
  • 阻塞:在某種特殊情況下,被人為掛起或執行輸入輸出操作時,讓出CPU並臨時終止自己的執行,進入阻塞狀態
  • 死亡:執行緒完成了它的全部工作或執行緒被提前強制性的終止或出現異常導致結束

二、執行緒的同步

用執行緒同步來解決上面買票的存在的執行緒安全的問題

問題:

1、 取票過程中,出現重票、錯票 -->出現了執行緒的安全問題

2、問題出現的原因:當某個執行緒操作車票的過程中,尚未操作完成時,其他執行緒參與進來,也操作車票

如何解決:

當一個執行緒在操作ticket的時候,其他執行緒不能參與進來,直到執行緒a操作完ticket時,其他執行緒才可以開始操作ticket。這種情況即使執行緒a出現了阻塞,也不能被改變。

3、在Java中通過執行緒同步機制來解決

方式一:同步程式碼塊

  synchronized(同步監視器){

    //需要被同步的程式碼

  }

  說明:1、操作共享資料的程式碼,即為需要被同步的程式碼

     2、共享資料:多個執行緒共同操作的變數。比如:ticket就是共享資料

     3、同步監視器:俗稱:鎖。任何一個類的物件都可以充當鎖

       要求:多個執行緒必須要共用同一把鎖。

      補充:在實現Runable介面建立多執行緒的方式中,我們可以考慮使用this充當同步監視器

package com.csii.day02;

/**
 * @Author wufq
 * @Date 2020/11/24 15:21
 * 多視窗賣票,總票為100張,使用Runerable介面的方式
 * 存線上程安全,待解決
 
*/ public class WindowTest01 { public static void main(String[] args){ //只聲明瞭一個物件,放到了三個構造器內, // ticket就不需要前面加static,如果聲明瞭多個物件,在用ticket時就必須加static,主要作用是靜態變數公用 Ticket ticket = new Ticket(); /*Thread t1 = new Thread(ticket); Thread t2 = new Thread(ticket); Thread t3 = new Thread(ticket); t1.setName("視窗1"); t2.setName("視窗2"); t3.setName("視窗3"); t1.start(); t2.start(); t3.start();*/ for(int i=0;i<3;i++){ Thread t1 = new Thread(ticket); t1.setName("視窗"+i+" "); t1.start(); } } } class Ticket implements Runnable{ private int ticket =100; //同步監視器:鎖,類中的任一物件都可作為鎖 // 要求:多個執行緒必須要共用同一把鎖 Object obj = new Object(); //此時Ticket類中的dog物件也可以作為鎖,原因在於多個執行緒共用了dog這一個物件 Dog dog = new Dog(); @Override public void run() { /*Object obj = new Object(); * 如果把obj物件宣告在run()方法內也是不行的,原因在於多個執行緒新建時(Thread t1 = new Thread(ticket);) * 每new一次,就會呼叫一次run()方法,同時也就會建立一個obj物件,這樣就不滿足多個執行緒必須共用同一把鎖, * 所以就不不能解決執行緒安全的問題 * * 同樣synchronized(new Object){}也是不可以的,原因同上 * */ while (true){ //這時候也可以用this,因為this就是一個物件,誰呼叫就是誰,這麼看的話ticket物件呼叫就是ticket物件,並且只有這一個共享鎖 //所以說是可以的。這樣的好處就沒必要特意的去宣告一個物件了 synchronized (this){//synchronized(obj){ if(ticket>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"賣票,票號為:"+ticket); ticket --; }else { break; } } } } } class Dog{ }

在繼承Thread類出啊昂見多執行緒的方式中,慎用this充當同步監視器,考慮使用當前類充當同步監視器

package com.csii.day02;

/**
 * @Author wufq
 * @Date 2020/11/26 17:04
 */
public class WindowTest02 {
        public static void main(String[] args){

            for(int i=0;i<3;i++){
                Ticket1 ticket = new Ticket1();
                ticket.setName("視窗"+i+" ");
                ticket.start();
            }
        }
    }

    class Ticket1 extends Thread{

        private static int ticket =100;

        /*
        * 運用繼承Thread這種方式來建立多執行緒時,
        * 採用同步程式碼塊解決執行緒安全問題時,一定要在變數和同步監視器(鎖<-->物件)前面加上static使其變成共享
        * */
        private static Object obj = new Object();

        @Override
        public void run() {

            while (true){

                synchronized(obj){
                    if(ticket>0){
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+"賣票,票號為:"+ticket);
                        ticket --;
                    }else {
                        break;
                    }
                }

            }
        }
    }

方式二:同步方法

4、同步的方式,解決了執行緒的安全問題 ---好處

  操作同步程式碼時,只能有一個執行緒參與,其他執行緒等待。相當於是一個單執行緒的過程,效率低。--侷限性