多線程

描述

問題描述:有4個線程和1個公共的字符數組。線程1的功能就是向數組輸出A,線程2的功能就是向字符輸出B,線程3的功能就是向數組輸出C,線程4的功能就是向數組輸出D。要求按順序向數組賦值ABCDABCDABCD,ABCD的個數由線程函數1的參數指定。[注:C語言選手可使用WINDOWS SDK庫函數]
接口說明:
void init();  //初始化函數
void Release(); //資源釋放函數
unsignedint__stdcall ThreadFun1(PVOID pM)  ; //線程函數1,傳入一個int類型的指針[取值範圍:1 – 250,測試用例保證],用於初始化輸出A次數,資源需要線程釋放
unsignedint__stdcall ThreadFun2(PVOID pM)  ;//線程函數2,無參數傳入
unsignedint__stdcall ThreadFun3(PVOID pM)  ;//線程函數3,無參數傳入
Unsigned int __stdcall ThreadFunc4(PVOID pM);//線程函數4,無參數傳入
char  g_write[1032]; //線程1,2,3,4按順序向該數組賦值。不用考慮數組是否越界,測試用例保證

知識點 字符串,循環,鏈表,隊列,棧,查找,搜索,排序,樹,圖,數組,函數,指針,枚舉,位運算,結構體,聯合體,文件操作,遞歸
運行時間限制 10M
內存限制 128
輸入

輸入一個int整數

輸出

輸出多個ABCD

樣例輸入 10
樣例輸出 ABCDABCDABCDABCDABCDABCDABCDABCDABCDABCD
由於以前對線程使用的比較少,所以這幾天總結了一下,然後接觸這道題的!

import java.util.Scanner;

import java.io.IOException;   

public class Main
{        
	public static int N;
	public static void main(String[] args) throws IOException 
	{
		Scanner sca = new Scanner(System.in);
		N = sca.nextInt();
		sca.close();
		
		final Test obj = new Test();            
		Thread tA = new Thread()
		{             
			public void run() 
			{                 
				obj.m1(N);              
			}          
		};
		tA.start();
		Thread tB = new Thread()
		{             
			public void run()
			{                 
				obj.m2(N);              
			}          
		};
		tB.start();
		Thread tC = new Thread() 
		{              
			public void run() 
			{                  
				obj.m3(N); 
			}          
		};
		tC.start();
		Thread tD = new Thread()
		{
			public void run()
			{
				obj.m4(N);
			}
		};
		tD.start();
		try {
			tA.join();
			tB.join();
			tC.join();
			tD.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(obj.ch);
	}    
}    
class Test 
{     
	static int count;      
	
	volatile int target = 1; 
	
	public static String ch = "";
	
	synchronized void m1(int n)
	{         
		for (int i = 0; i < n; i++)
		{              
			while (target != 1)
			{                 
				try
				{
					wait();
				}
				catch (InterruptedException e)
				{
					e.printStackTrace(); 
				}
			}
			ch += "A";
			target = 2;              
			notifyAll();         
		}      
	}        
	synchronized void m2(int n)
	{
		for (int i = 0; i < n; i++)
		{
			while (target != 2)
			{
				try
				{
					wait();
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();             
				}
			}
			ch += "B";
			target = 3;
			notifyAll();
		}
	}
	synchronized void m3(int n)
	{
		for (int i = 0; i < n; i++)
		{
			while (target != 3)
			{
				try
				{
					wait();
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
			ch += "C";
			target = 4;
			notifyAll();
		}     
	} 
	synchronized void m4(int n)
	{
		for (int i = 0; i < n; i++)
		{
			while (target != 4)
			{
				try
				{
					wait();
				}
				catch (InterruptedException e)
				{
					e.printStackTrace();
				}
			}
			ch += "D";
			target = 1;
			notifyAll();
		}     
	} 
}
我承認這個不是很符合題意,但是大體上來說還是達到了題中所說的,提交正確以後,我看了一下別人的正確代碼,我失望了,都是直接輸出,除了main線程意外沒有其他線程,也就是騙了機器……

好了,下面就我所理解的java線程總結一下:

一、關於線程的一些概念:

首先,進程與線程是不同的,一般來說,一個程序有一個進程,而有多個線程。在單核CPU中,這些線程在宏觀上講是並行的,而在微觀上來說每一時刻都只有一個線程在運行。在多核CPU中同一時刻會有多個線程運行,起數量取決於CPU數量。
其次,來說一下線程的生命週期:
線程的生命週期總的來說可以分爲新建狀態、就緒狀態、運行狀態、等待/阻塞狀態和消亡狀態(也有的書上會說有新建狀態、就緒狀態、運行狀態、等待狀態、休眠狀態、阻塞狀態和消亡狀態),叫法會有不同,但實際所指的是一樣的。
生命週期狀態圖:

二、線程的定義方式:

第一種:

public class Main extends Thread
{
	public void run()
	{
		System.out.println("繼承Thread定義線程!");
	}
	public static void main(String[] args)
	{
		Main main = new Main();
		main.start();
	}
}
run()函數爲Thread的內部函數,重寫的run()函數的功能就是希望讓線程完成的任務。
第二種:

public class Main implements Runnable
{
	public void run()
	{
		System.out.println("實現接口Runnable定義線程!");
	}
	public static void main(String[] args)
	{
		Main test = new Main();
		Thread t = new Thread(test);
		t.start();
	}
}
run()函數同上,這種方法比較靈活,首先可以在已經有繼承類的情況下使用線程,而且可以創建多個線程來完成任務。

注意,其實main也是一個線程,它的啓動的動作由JVM來控制,其他線程必須由自己來控制。

三、線程操作方法:

線程的休眠:

try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

sleep()方法的基本語法格式是:

public static void sleep(long millis) throws InterruptedException;
public static void sleep(long millis ,int nanos) throws InterruptedException;
millis表示線程睡眠的毫秒數,nanos表示線程睡眠的納秒數。同時注意sleep方法是靜態方法,其使用位置比較隨意,在線程內調用就是當前線程進入睡眠狀態。

線程的加入:

try {
			t.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
在一個線程中調用其他線程的join方法意味着先執行其他線程,而當前線程進入就緒狀態,在其他線程執行完畢後再執行當前線程。
看一下這個例子:
public class Main
{
	public static void main(String[] args)
	{
		Thread t1 = new Thread(new Runnable(){
			public void run()
			{
				for(int i = 0; i < 20; i++)
				{
					System.out.println(Thread.currentThread().getName()+" "+i);
				}
			}
		},"t1");
				
		Thread t2 = new Thread(new Runnable(){
			public void run()
			{
				for(int i = 0; i < 20; i++)
				{
					System.out.println(Thread.currentThread().getName()+" "+i);
					if(i == 10)
					{
						try {
							t1.join();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
			}
		},"t2");
		
		t1.start();
		t2.start();
	}
}
它的運行結果爲:
t2 0
t1 0
t2 1
t1 1
t2 2
t1 2
t2 3
t1 3
t2 4
t1 4
t2 5
t2 6
t1 5
t1 6
t1 7
t1 8
t2 7
t1 9
t1 10
t1 11
t1 12
t2 8
t2 9
t1 13
t2 10
t1 14
t1 15
t1 16
t1 17
t1 18
t1 19
t2 11
t2 12
t2 13
t2 14
t2 15
t2 16
t2 17
t2 18
t2 19

可以看出,在t2線程運行到i==10的時候,調用了t1的join方法,從這時候開始就先執行t1,知道t1執行完畢才執行t2線程。

線程的中斷:

以前可以使用stop()方法來中斷線程,但是stop()方法是直接中斷,線程來不及回收資源等行爲,所以現在不建議使用stop()方法。現在中斷可以使用標記變量來中斷:
public class Main implements Runnable
{
	private static boolean symbol = true;
	public void run()
	{
		while(symbol)
		{
			System.out.println(0);
		}
	}
	public static void main(String[] args)
	{
		Main test = new Main();
		Thread t1 = new Thread(test);
		t1.start();
		symbol = false;
	}
}
當需要中斷的時候直接賦值給symbol爲false,t1線程就會中斷(t1線程運行完畢)。

線程的禮讓:

線程禮讓使用yield()方法,這也是一個靜態方法,所以可以Thread.yield()直接使用,功能就是讓當前線程進入就緒狀態,CPU重新選擇具有同樣優先級的線程進入運行狀態。yield()方法只是一種暗示,當前線程是否進入就緒狀態還是由CPU決定。

線程的優先級

在線程調用start()方法之前可以設置線程優先級,使用setPriority()方法設置,參數爲1-10(main線程的優先級是Thread.NORM_PRIORITY(5)),系統始終會選擇就緒狀態下優先級較高的進入運行狀態,高優先級的運行機率比較大,低優先級的運行機率比較小。

線程同步

首先比較經典的是賣火車票:
public class Main implements Runnable
{
	int count = 10;//火車票數量
	public void run()
	{
		while(true)
		{
			if(count > 0)
			{
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"賣出"+count--);
			}
		}
	}
	public static void main(String[] args)
	{
		Main test = new Main();
		Thread t1 = new Thread(test,"窗口1");
		Thread t2 = new Thread(test,"窗口2");
		Thread t3 = new Thread(test,"窗口3");
		t1.start();
		t2.start();
		t3.start();
	}
}
運行結果是:
窗口2賣出10
窗口1賣出9
窗口3賣出9
窗口1賣出8
窗口2賣出7
窗口3賣出6
窗口1賣出4
窗口2賣出5
窗口3賣出3
窗口2賣出2
窗口1賣出1
窗口3賣出0
窗口2賣出-1
可以看到最後兩行的票號爲0和負數,這是因爲count變成1的時候,三個線程都對其有存儲功能,當線程1執行run()方法時,還沒有來得及遞減count,就指定它調用sleep()方法進入就緒狀態,這時其他線程進入run()方法,發現count仍然大於0,而線程1休眠時間又到了,將count遞減,同時其他線程也對count完成遞減操作,所以就出現了負值。
此時需要對共享資源上鎖,當一個線程進入run()方法時禁止其他線程進入run()方法,其他線程阻塞,當線程釋放共享資源以後其他線程才能進入就緒狀態,由CPU選擇一個線程進入run()方法,並上鎖,其它線程再進入阻塞狀態。
修改以後爲:
<pre name="code" class="java">public class Main implements Runnable
{
	volatile int count = 10;//火車票數量
	public void run()
	{
		while(true)
		{
			synchronized("")
			{
				if(count > 0)
				{
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName()+"賣出"+count--);
				}
			}
			
		}
	}
	public static void main(String[] args)
	{
		Main test = new Main();
		Thread t1 = new Thread(test,"窗口1");
		Thread t2 = new Thread(test,"窗口2");
		Thread t3 = new Thread(test,"窗口3");
		t1.start();
		t2.start();
		t3.start();
	}
}

使用了synchronized關鍵字,執行結果是:
窗口1賣出10
窗口1賣出9
窗口3賣出8
窗口3賣出7
窗口2賣出6
窗口2賣出5
窗口2賣出4
窗口2賣出3
窗口3賣出2
窗口1賣出1
synchronized關鍵字也可以放在方法前面,比如說:
public synchronized void doit(){……}
其效果與上面的例子相同。


最後,以上的內容都是根據我的理解總結而來,如果有什麼不對的地方歡迎大家指出,萬分感謝!有什麼需要討論的可以在評論中留言,博主會盡快回復:)
發佈了38 篇原創文章 · 獲贊 6 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章