1. 程式人生 > 程式設計 >java執行緒

java執行緒

概述


相關關係:

  • 程式:可理解為一組靜態程式碼。
  • 程式:正在進行的程式(靜態的程式碼,執行起來)。
  • 執行緒:正在執行程式中的小單元。

執行緒:

1、主執行緒 :系統執行緒

2、使用者執行緒 :main

3、守護執行緒(精靈):比如GC(垃圾回收)
複製程式碼

執行緒狀態:

1、建立執行緒(new)

2、就緒狀態(start())

3、執行狀態(CPU分配run)

4、等待/掛起(wait)

5、-----異常/死亡(exception over)/(或重新回到就緒狀態)(notify/notifyAll--喚醒)
複製程式碼

基本操作

一 、建立執行緒

  • 方式一:繼承Thread類

       1、 定義繼承Thread;
      
       2、複寫Thread類中的run方法;(目的:將自定義程式碼儲存在run方法中,讓執行緒執行(同時執行,搶奪資源))
      
       3、呼叫執行緒的start方法;(目的:啟動執行緒,呼叫run方法)
    複製程式碼
  • 方式二:實現Runnable介面

      1、定義類實現Runnable介面;
      
      2、覆蓋Runnable介面中的run方法;
      
      3、通過Thread類建立執行緒物件;
      
      4、將Runnable介面的子類物件作為實際引數傳遞給Thread類的構造方法;
      
      5、呼叫Thread類的start方法,開啟執行緒並調節Runnable介面子類的run方法;
    複製程式碼

實現方式和繼承方式的區別:

  • 繼承只能繼承一個,具有侷限性。
  • 繼承Thread: 執行緒程式碼存放在子類run。
  • 實現runnable: 執行緒程式碼存放在介面的子類run。

應用舉例:

  • 方式一:

      //1、繼承Thread
          class Test extends Thread{
                private String name;
                Test(String name)
                {
                    this.name=name;
                }
                public void run(){     //2、run方法
                    for(int x=0;x<60;x++)
                    {
                        System.out.println(name+"----"+x);
            
                    }
                }
            }
            public class TestMain
            {
                public static void main(String[] args) {
                    Test q1=new Test("lisi");
                    Test q2=new Test("lisan");
                    q1.start(); //3、呼叫start
                    q2.start();
            
                }
            }
    複製程式碼
  • 方式二:

      - 
      class Ticket implements Runnable//1、實現Runnable介面
      {
          private int tick=100;
          public void run()//2、run方法
          {
              while(true)
              {
                  if(tick>0)
                      System.out.println(Thread.currentThread().getName()+"...sale:"+tick--);
              }
          }
      
      }
      class TestMain
      {
          public static void main(String[] args) {
              Ticket t=new Ticket();
            //3、建立執行緒
              Thread t1=new Thread(t);//4、傳遞引數
              Thread t2=new Thread(t);
              Thread t3=new Thread(t);
              Thread t4=new Thread(t);
              //5、呼叫start
              t1.start();
              t2.start();
              t3.start();
              t4.start();
      
      
          }
      }
    複製程式碼

二、獲取執行緒物件及名稱

  • 獲取當前執行緒物件:Thread.currentThread()
  • 獲取執行緒名稱:getName()

多執行緒的安全問題:

原因:

當多條語句在操作一個共享資料,一個資料對多條語句只執行了一部分, 還未執行完時,另一個參與進來,導致錯誤。

解決方法:

  • 同步程式碼塊:對於多條共享的語句,讓一個執行緒先執行完。

      synchronized(物件)//相當於“鎖”
      {
          需要被同步的程式碼
          }
    複製程式碼

多執行緒-同步函式

步驟:

1、明確哪些程式碼是多執行緒執行程式碼;

2、明確共享資料;

3、明確多執行緒執行程式碼中哪些語句是操作共享資料的;

多執行緒同步函式的鎖是this:

函式需要被物件呼叫,那麼函式都有一個所屬物件引用,即this,所以同步函式的鎖就是this.

死鎖:(死鎖程式要會寫)

產生原因:同步中巢狀同步,鎖卻不同。


單例設計模式:

單例模式的定義與特點:

單例(Singleton)模式的定義:指一個類只有一個例項,且該類能自行建立這個例項的一種模式。

例如,Windows中只能開啟一個工作管理員,這樣可以避免因開啟多個工作管理員視窗而造成記憶體資源的浪費,或出現各個視窗顯示內容的不一致等錯誤。

在計算機系統中,還有Windows的回收站、作業系統中的檔案系統、多執行緒中的執行緒池、顯示卡的驅動程式物件、印表機的後臺處理服務、應用程式的日誌物件、資料庫的連線池、網站的計數器、Web應用的配置物件、應用程式中的對話方塊、系統中的快取等常常被設計成單例。

單例模式有 3 個特點:

1、單例類只有一個例項物件;

2、該單例物件必須由單例類自行建立;

3、單例類對外提供一個訪問該單例的全域性訪問點;

程式碼實現:

//餓漢式。
/*
class Single
{
	private static final Single s = new Single();
	private Single(){}
	public static Single getInstance()
	{
		return s;
	}
}
*/


//懶漢式(延遲載入)

class Single
{
	private static Single s = null;
	private Single(){}


	public static  Single getInstance()
	{
		if(s==null)//雙重判斷。提高效率
		{
			synchronized(Single.class)//靜態沒有this,鎖是該類所屬的位元組碼類物件
			{
				if(s==null)
					//--->A;
					s = new Single();
			}
		}
		return s;
	}
}

class SingleDemo 
{
	public static void main(String[] args) 
	{
		System.out.println("Hello World!");
	}
}
複製程式碼

執行緒間通訊:

為瞭解決安全問題。

多個執行緒操作同一個資源,但是操作的動作不同。

程式碼演示:

class Res
{
	String name;
	String sex;
	boolean flag = false;
}

class Input implements Runnable
{
	private Res r ;
	Input(Res r)
	{
		this.r = r;
	}
	public void run()
	{
		int x = 0;
		while(true)
		{
			synchronized(r)
			{

				if(r.flag)
					try{r.wait();}catch(Exception e){}
				if(x==0)
				{
					r.name="mike";
					r.sex="man";
				}
				else
				{
					r.name="麗麗";
					r.sex = "女女女女女";
				}
				x = (x+1)%2;
				r.flag = true;
				r.notify();
			}
		}
	}
}

class Output implements Runnable
{
	private Res r ;
	
	Output(Res r)
	{
		this.r = r;
	}
	public void run()
	{
		while(true)
		{
			synchronized(r)
			{
				if(!r.flag)
					try{r.wait();}catch(Exception e){}
				System.out.println(r.name+"...."+r.sex);
				r.flag = false;
				r.notify();
			}
		}
	}
}


class  InputOutputDemo
{
	public static void main(String[] args) 
	{
		Res r = new Res();

		Input in = new Input(r);
		Output out = new Output(r);

		Thread t1 = new Thread(in);
		Thread t2 = new Thread(out);

		t1.start();
		t2.start();
	}
}


//notifyAll();

--------------------------------------------------

都使用在同步中,因為要對持有監視器(鎖)的執行緒操作。
所以要使用在同步中,因為只有同步才具有鎖。

**為什麼這些操作執行緒的方法要定義Object類中呢?**
因為這些方法在操作同步中執行緒時,都必須要標識它們所操作執行緒只有的鎖,
只有同一個鎖上的被等待執行緒,可以被同一個鎖上notify喚醒。
不可以對不同鎖中的執行緒進行喚醒。

也就是說,等待和喚醒必須是同一個鎖。

而鎖可以是任意物件,所以可以被任意物件呼叫的方法定義Object類中。
複製程式碼

等待喚醒機制:

wait() :等待,將正在執行的執行緒釋放其執行資格 和 執行權,並儲存到執行緒池中。

notify():喚醒,喚醒執行緒池中被wait()的執行緒,一次喚醒一個,而且是任意的。

notifyAll(): 喚醒全部:可以將執行緒池中的所有wait() 執行緒都喚醒。
複製程式碼

生產者、消費者:

    對於多個生產者和消費者?
    為什麼要定義while判斷標記?
    原因:讓被喚醒的執行緒再一次判斷標記。
    
    
    為什麼定義notifyAll?
    因為需要喚醒對方執行緒。
    因為只用notify,容易出現只喚醒本方執行緒的情況。導致程式中的所有執行緒都等待。
複製程式碼

停止執行緒:

    一般情況:
    run方法結束(開啟多執行緒執行,執行程式碼通常是迴圈結構,所以控制迴圈即可)
    
    特殊情況:
    當執行緒處於了凍結狀態。
    就不會讀取到標記。那麼執行緒就不會結束。
    
    當沒有指定的方式讓凍結的執行緒恢復到執行狀態是,這時需要對凍結進行清除。
    強制讓執行緒恢復到執行狀態中來。這樣就可以操作標記讓執行緒結束。
    
    Thread類提供該方法 interrupt();
複製程式碼

守護執行緒:(set .Deamon)

[注意:]啟用前呼叫;執行緒中只剩守護執行緒時,java退出虛擬機器器
複製程式碼

Join方法:

等待執行緒終止:

            當A執行緒執行到了B執行緒的.join()方法時,A就會等待。等B執行緒都執行完,A才會執行。
        
            join可以用來臨時加入執行緒執行。
複製程式碼

優先順序:yeild方法

即搶資源的頻率,最高10,預設5