JAVA多線程管理

之前,我們討論了多線程的創建、實現與信息共享通過static變量和同一個繼承Runnable接口的類的成員變量,實現了由粗粒度到細粒度之間的轉變,加強了線程之間的交流,但這種同步的意義不高。
線程狀態:

  • NEW剛創建(new)
  • RUNNABLE 就緒態(start)
  • RUNNING 運行中(run)
  • BLOCK 阻塞(sleep)
  • TERMINATED 結束
    線程阻塞和喚醒的方法:
  • sleep,時間一到,自己會醒來
  • wait/notify/ notifyAll 等待,需要別人來喚醒
  • join,等待另一個線程結束
  • interrupt,向另外一個線程發送中斷信號,該線程收到信息,會觸發InterruptException(可接觸阻塞),並進行下一步處理。
    下面,先來看一個例子,生產者和消費者問題。
package product;
/*
 * 經典生產者與消費者問題
 * 生產者不斷的往倉庫中存放產品,消費者從倉庫中消費產品
 * 其中生產者和消費者都可以有若干個
 * 倉庫規則:容量有限,庫滿時不能存放,庫空時不能取產品
 */
public class ProductTest 
{

	public static void main(String[] args) 
	{
		// TODO 自動生成的方法存根
		Storage storage = new Storage();
		
		Thread consumer1 = new Thread(new Consumer(storage));
		consumer1.setName("消費者1");
		Thread consumer2 = new Thread(new Consumer(storage));
		consumer2.setName("消費者2");
		Thread producer1 = new Thread(new Producer(storage));
		producer1.setName("生產者1");
		Thread producer2 = new Thread(new Producer(storage));
		producer2.setName("生產者2");
		
		producer1.start();
		producer2.start();
		try
		{
			Thread.sleep(1000);	
		}catch(Exception e)
		{
			e.printStackTrace();
		}
		consumer1.start();
		consumer2.start();
	}
}
package product;

public class Storage 
{
	private Product[] products = new Product[10];
	private int top = 0;
	
	public synchronized void push (Product product)
	{
		while(top == products.length)
		{
			try
			{
				System.out.println("producer wait");
				wait();
			}catch(Exception e)
			{
				e.printStackTrace();
			}
		}
		
		products[top++] = product;
		System.out.println(Thread.currentThread().getName()+" 生產了產品 "+product);
		System.out.println("producer notifyAll");
		notifyAll();
	}
	
	public synchronized Product pop()
	{
		while(top == 0)
		{
			try
			{
				System.out.println("consumer wait");
				wait();
			}catch (Exception e)
			{
				e.printStackTrace();
			}
		}
		
		top--;
		Product p = new Product(products[top].getId(),products[top].getName());
		products[top] = null;
		System.out.println(Thread.currentThread().getName()+" 消費了產品 "+p);
		System.out.println("comsumer notifyAll");
		notifyAll();
		return p;
	}
}
package product;

public class Product 
{
	private int id;
	private String name;
	
	public Product(int id,String name)
	{
		this.id = id;
		this.name = name;
	}
	
	public String toString()
	{
		return "(產品ID:"+this.id+"產品名稱:"+this.name+")";
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}
package product;

import java.util.Random;

public class Producer implements Runnable
{
	private Storage storage;
	
	public Producer(Storage storage)
	{
		this.storage = storage;
	}
	@Override
	public void run()
	{
		int i=0;
		Random r = new Random();
		while(i<10)
		{
			i++;
			Product product = new Product(i,"電話"+r.nextInt(100));
			storage.push(product);
		}
	}
}
package product;

public class Consumer implements Runnable
{
	private Storage storage;
	
	public Consumer(Storage storage)
	{
		this.storage = storage;
	}
	
	public void run()
	{
		int i=0;
		while(i<10)
		{
			i++;
			storage.pop();
			try
			{
				Thread.sleep(1000);
			}catch(Exception e)
			{
				e.printStackTrace();
			}
		}
	}
}

從該例子可以看出,當某個線程被動的暫停和終止時,只能依靠別的線程類拯救自己當前的狀態,這樣很危險,比如被暫停的線程正打開某個文件,還未關閉等。下面通過一個例子,來看一下被動的終止和主動的終止的區別。

public class InterruptTest
{

	public static void main(String[] args) throws InterruptedException 
	{
		// TODO 自動生成的方法存根
		TestThread1 t1 = new TestThread1();
		TestThread2 t2 = new TestThread2();
		
		t1.start();
		t2.start();
		
		Thread.sleep(2000);
		t1.interrupt();	//讓t1中斷
		t2.flag = false;
		System.out.println("main thread is exiting");
	}
}


class TestThread1 extends Thread
{
	public void run()
	{
		while(!interrupted())
		{
			System.out.println("test thread1 is running");
			try
			{
				Thread.sleep(1000);
			}catch(Exception e)
			{
				e.printStackTrace();
				break;
			}
		}
		System.out.println("test thread1 is exiting");
	}
}

class TestThread2 extends Thread
{
	public volatile boolean flag = true;
	public void run()
	{
		while(flag)
		{
			System.out.println("test thread2 is running");
			try
			{
				Thread.sleep(1000);
			}catch(Exception e)
			{
				e.printStackTrace();
			}
		}	
		System.out.println("test thread2 is exiting");
	}
}

當interrupted時,若該線程處於sleep狀態時,此時終止將會爆發異常。

線程的死鎖

public class ThreadDemo5 
{
	public static Integer r1 = 1;
	public static Integer r2 = 2;
	public static void main(String[] args) 
	{
		// TODO 自動生成的方法存根
		TestThread51 t1 = new TestThread51();
		t1.start();
		TestThread52 t2 = new TestThread52();
		t2.start();
	}
}

class TestThread51 extends Thread
{
	public void run()
	{
		synchronized (ThreadDemo5.r1)
		{
			System.out.println("TestThread51 already r1");
			try
			{
				Thread.sleep(1000);
			}catch(Exception e)
			{
				e.printStackTrace();
			}
			synchronized(ThreadDemo5.r2)
			{
				System.out.println("TestThread51 is running");
			}
		}
	}
}

class TestThread52 extends Thread
{
	public void run()
	{
		synchronized (ThreadDemo5.r2)
		{
			System.out.println("TestThread52 already r2");
			try
			{
				Thread.sleep(1000);
			}catch(Exception e)
			{
				e.printStackTrace();
			}
			synchronized(ThreadDemo5.r1)
			{
				System.out.println("TestThread52 is running");
			}
		}
	}
}

守護(後臺)線程
普通線程的結束,是run方法運行結束。
守護線程的結束,是run方法運行結束,或main函數結束

public class ThreadDemo4 
{

	public static void main(String[] args) throws InterruptedException 
	{
		// TODO 自動生成的方法存根
		TestThread4 t = new TestThread4();
		t.setDaemon(true);//設置爲守護線程
		t.start();
		Thread.sleep(2000);
		System.out.println("main thread is exiting");
	}

}

class TestThread4 extends Thread
{
	public void run()
	{
		while(true)
		{
			System.out.println(Thread.currentThread().getName()+" is running ");
			try
			{
				Thread.sleep(1000);
			}catch(Exception e)
			{
				e.printStackTrace();
			}
		}
	}
}

main函數結束後,子線程也結束了。可以看出,守護線程和main線程同呼吸,共命運。注意:不要在守護線程中打開文件或者連接數據庫。

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