javaSE基礎-多線程基礎

繼承Thread類實現多線程示例代碼:

class Thread1 extends Thread{

	//使用構造函數給線程起名稱
    Thread1(String  name){
        super(name);
    }

    public void run(){
        for (int i = 0; i<60; i++)
            System.out.println(this.getName()+" run"+i);
    }
}

public class ThreadDemo1{

    public static void main(String[] args) {
        Thread1 thread1 = new Thread1("test1");
        Thread1 thread2 = new Thread1("test2");
        thread1.start();
        thread2.start();

        for (int i = 0; i<200; i++)
            System.out.println("main run"+i);
    }
}

多線程特性:隨機性
爲什麼要覆蓋run方法:
Thread類用於描述線程,該類定義了一個功能(run() run方法),用於存儲線程要運行的代碼
線程的五種狀態(如圖):
線程的五種狀態
線程都有自己的默認名稱:Thread-編號 編號從0開始
static Thread currentThread():獲取當前線程對象
getName():獲取線程名稱
設置線程名稱方式:setName()或者構造函數

創建線程的第二種方式:實現Runnable接口
步驟:
1定義類實現Runnable接口
2覆蓋Runnable接口中的run方法(將線程所需要運行的代碼存放在該run方法中)
3通過Thread類建立對象
4將Runnable接口的子類對象做爲參數傳遞給Thread的構造函數(原因:自定義的run方法所屬對象是Runnable接口的子類對象,所以要讓線程去執行指定對象的run方法,就必須指明改run方法所屬對象)
5調用Thread類的start()開啓線程並調用Runnable接口子類的run方法

兩種方法的區別:
接口實現方式的好處:避免了單繼承的侷限性,建議使用(java只支持單繼承)
繼承Thread,線程代碼存放在Thread子類run方法中。實現Runnable代碼存放在接口實現類的run方法中

同步函數(格式 public synchronized void test(){})使用的鎖是this
靜態同步函數(格式 public static synchronized void test(){})使用的鎖是該方法所在類的字節碼對象類名.class(靜態進入內存時,內存中沒有對象,但是一定有該類對應的字節碼文件對象,類名.class)

知識點:
死鎖demo
1、 t1先運行,這個時候flag == true,先鎖定obj1,然後睡眠1秒鐘
2、 而t1在睡眠的時候,另一個線程t2啓動,flag == false,先鎖定obj2,然後也睡眠1秒鐘
3、 t1睡眠結束後需要鎖定obj2才能繼續執行,而此時obj2已被t2鎖定
4、 t2睡眠結束後需要鎖定obj1才能繼續執行,而此時obj1已被t1鎖定
5、 t1、t2相互等待,都需要得到對方鎖定的資源才能繼續執行,從而死鎖。

public class DeadLock implements Runnable{
    
    private static Object obj1 = new Object();
    private static Object obj2 = new Object();
    private boolean flag;
    
    public DeadLock(boolean flag){
        this.flag = flag;
    }
    
    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName() + "運行");
        
        if(flag){
            synchronized(obj1){
                System.out.println(Thread.currentThread().getName() + "已經鎖住obj1");
                try {  
                    Thread.sleep(1000);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
                synchronized(obj2){
                    // 執行不到這裏
                    System.out.println("1秒鐘後,"+Thread.currentThread().getName()
                                + "鎖住obj2");
                }
            }
        }else{
            synchronized(obj2){
                System.out.println(Thread.currentThread().getName() + "已經鎖住obj2");
                try {  
                    Thread.sleep(1000);  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                }  
                synchronized(obj1){
                    // 執行不到這裏
                    System.out.println("1秒鐘後,"+Thread.currentThread().getName()
                                + "鎖住obj1");
                }
            }
        }
    }

}

兩個線程操作統一資源情景,同步解決方案
概念:
notify() – 喚醒在此對象監視器上等待的單個線程。
notifyAll() – 喚醒在此對象監視器上等待的所有線程。
wait() – 讓當前線程處於“等待(阻塞)狀態”,“直到其他線程調用此對象的 notify() 方法或 notifyAll() 方法”,當前線程被喚醒(進入“就緒狀態”)。
wait(long timeout) – 讓當前線程處於“等待(阻塞)狀態”,“直到其他線程調用此對象的 notify() 方法或 notifyAll() 方法,或者超過指定的時間量”,當前線程被喚醒(進入“就緒狀態”)。
wait(long timeout, int nanos) – 讓當前線程處於“等待(阻塞)狀態”,“直到其他線程調用此對象的 notify() 方法或 notifyAll() 方法,或者其他某個線程中斷當前線程,或者已超過某個實際時間量”,當前線程被喚醒(進入“就緒狀態”)

/**
 * wait()、notify()、notifyAll()
 * 這些方法都是用在同步中,因爲要對持有監視器(鎖)的線程操作,同步纔有鎖
 * 這些方法在操作同步線程時,都必須要標識他們所操作的線程持有的鎖,在同一個鎖上的被等待線程,
 * 可以被同一鎖上notify喚醒,不可以對不同鎖操作,等待、喚醒必須爲同一鎖
 * 鎖可以是任意對象,所以定義在Object類中
 */

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

class Input implements Runnable{
	//保證資源唯一 單例模式也可以
	private Res res;
	Input(Res res){
		this.res = res;
	}
	@Override
	public void run() {
		int x = 0;
		while(true) {
			
			synchronized (res) {
				try {
					if(res.flag)//如果標記爲true 則不再賦值 等待outPut線程取值打印
						res.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				if(x == 0) {
					res.name = "tom";
					res.age = "18";
				} else {
					res.name = "傑克";
					res.age = "二十";
				}
				//得到 0、1
				x = (x+1)%2;
				res.flag = true;
				res.notify();
			}
		}
	}
}

class Output implements Runnable{
	//保證資源唯一 單例模式也可以
	private Res res;
	Output(Res res){
		this.res = res;
	}
	@Override
	public void run() {
		while(true) {
			synchronized (res) {
				try {
					if(!res.flag)
					res.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("name:"+res.name+",age:"+res.age);
				res.flag = false;
				res.notify();
			}
		}
	}
}

public class InOutRunnable {
	public static void main(String[] args) {
		Res res = new Res();
		
		Input in = new Input(res);
		Output out = new Output(res);
		
		Thread thread1 = new Thread(in);
		Thread thread2 = new Thread(out);
		
		thread1.start();
		thread2.start();
	}
}

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