java 多線程基本操作總結

java能夠支持多線程,多線程是實現併發機制的一種有效手段,和進程一樣,線程也是實現併發的一個基本單位,但是線程是比進程更小的單位,線程是在進程的基礎上的進一步劃分。所謂所線程就是說一個進程在運行的過程中可以產生多個線程,這些線程可以同時存在,同時運行,一個進程可能包含有多個同時執行的線程。

首先,線程的實現:直接繼承Thread類和實現Runnable接口。這兩種方式都能夠實現多線程先給出例子:

繼承Thread類:

class ThreadT extends Thread{
	
	public ThreadT(String name) {
		super(name);
	}

	@Override
	public void run() {
		for(int i=0;i<20;i++)
			System.out.println(this.getName()+"running"+i);
	}
}
public class ThreadTest {
	public static void main(String[] args) {
		new ThreadT("Thread線程一").start();
		new ThreadT("Thread線程二").start();
	}
}
/**
 * 結果:
 * 
 * Thread線程一running0
Thread線程一running1
Thread線程一running2
Thread線程二running0
Thread線程一running3
Thread線程二running1
Thread線程一running4
Thread線程二running2
Thread線程一running5
Thread線程二running3
Thread線程二running4
Thread線程二running5
Thread線程二running6
Thread線程二running7
 */

啓動線程不能直接調用run()方法,必須調用start()方法,因爲線程的執行需要cpu的調度,同時每個線程都只能調用一次start()方法

實現Runnable接口的多線程:兩個線程一個打印“-”一個打印“|”同時打印:

public class ThreadTest01 {
	public static void main(String[] args) {
//		new Thread(new PrintHeng()).start();
//		new Thread(new PrintShu()).start();
		RunnableTest runnable1=new RunnableTest();
		new Thread(runnable1,"thread1").start();
		new Thread(runnable1,"thread2").start();
		new Thread(runnable1,"thread3").start();
		new Thread(runnable1,"thread4").start();
	}
}

class PrintHeng implements Runnable{  // 打印橫線的線程
	public void run() {
		for(int i=0;i<20;i++)
		{
			System.out.print("-");
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	
}

class PrintShu implements Runnable{  // 打印豎線的線程
	public void run() {
		for(int i=0;i<20;i++)
		{
			System.out.println("|");
			try {
				Thread.sleep(300);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
運行能同時打印橫線和豎線,但是注意到線程的start方法調用還是通過Thread類來實現的,這一點下面會給出解釋

既然通過Thread類和Runnable接口都可以實現多線程,那麼這兩種方法有什麼區別呢,首先給出Thread類的定義:

public class Thread implements Runnable {
	//...     中間很多變量省略
	           
	
	//...
	/* What will be run. */
    private Runnable target;         // 將目標線程作爲自己的參數
    
  //...     中間部分省略
    
	
  	//...
    
    public void run() {
    	if (target != null) {      // 實際上就是調用了目標線程的run方法
    	    target.run();
    	}
        }


}
從定義中可以看出,Thread類也實現了Runnable 的接口,如果我們自己實現的線程也是實現Runnable接口方式的話就有了下面的關係:

可見當我們Thread類就相當於是我們目標線程(MyThread)的一個代理,通過target的start()方法來調用MyThread中的run方法,而不是直接運行我們Mythread中的run(),因爲直接調用run()的話並不需要cpu的調度相當於執行了一個普通方法。

除此之外兩種方式在共享變量時也是有所區別的,使用一個簡單的例子:

package edu.hue.jk.thread;

class MyThread extends Thread{
	private int i=0;
	public void run(){
		System.out.println("Thread繼承方式:"+"----"+i++);
	}
	public MyThread() {
		super();
	}
}

public class ThreadTest02 {
	public static void main(String[] args) {
		for(int i=0;i<10;i++)
			new Thread(new MyThread()).start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		MyRunnable runnable=new MyRunnable();
		for(int i=0;i<10;i++)
			new Thread(runnable).start();  // 構造同一實例的多個線程
	}
}
class MyRunnable implements Runnable{
	private int i=0;
	public void run() {
		System.out.println("Runnable實現方式:"+"----"+i++);
	}
	public MyRunnable() {
		super();
	}
}

實驗結果:

Thread繼承方式:----0
Thread繼承方式:----0
Thread繼承方式:----0
Thread繼承方式:----0
Thread繼承方式:----0
Thread繼承方式:----0
Thread繼承方式:----0
Thread繼承方式:----0
Thread繼承方式:----0
Thread繼承方式:----0
Runnable實現方式:----0
Runnable實現方式:----1
Runnable實現方式:----2
Runnable實現方式:----3
Runnable實現方式:----4
Runnable實現方式:----5
Runnable實現方式:----6
Runnable實現方式:----7
Runnable實現方式:----8
Runnable實現方式:----9

說明使用Runnable方式實現多線程時候,可以實現多個線程共享同一個實例(資源共享的效果)。



三個靜態方法用來控制進程的優先級:

static int MAX_PRIORITY
          線程可以具有的最高優先級。
static int MIN_PRIORITY
          線程可以具有的最低優先級。
static int NORM_PRIORITY
          分配給線程的默認優先級。
 
構造方法摘要
Thread(Runnable target)
          分配新的 Thread 對象。
Thread(String name)
          分配新的 Thread 對象。
 
 
其它方法
static Thread currentThread()
          返回對當前正在執行的線程對象的引用。
 ClassLoader getContextClassLoader()
          返回該線程的上下文 ClassLoader。
 long getId()
          返回該線程的標識符。
 String getName()
          返回該線程的名稱。
 int getPriority()
          返回線程的優先級。
 Thread.State getState()
          返回該線程的狀態。
 ThreadGroup getThreadGroup()
          返回該線程所屬的線程組。
static boolean holdsLock(Object obj)
          當且僅當當前線程在指定的對象上保持監視器鎖時,才返回 true。
 void interrupt()
          中斷線程。
static boolean interrupted()
          測試當前線程是否已經中斷。
 boolean isAlive()
          測試線程是否處於活動狀態。
 boolean isDaemon()
          測試該線程是否爲守護線程。
 boolean isInterrupted()
          測試線程是否已經中斷。
 void join()
          等待該線程終止。
 void join(long millis)
          等待該線程終止的時間最長爲 millis 毫秒。
 void join(long millis, int nanos)
          等待該線程終止的時間最長爲 millis 毫秒 + nanos 納秒。
 void resume()
          已過時。 該方法只與 suspend() 一起使用,但 suspend() 已經遭到反對,因爲它具有死鎖傾向。有關更多信息,請參閱爲何 Thread.stop、Thread.suspend 和 Thread.resume 遭到反對?。
 void run()
          如果該線程是使用獨立的 Runnable 運行對象構造的,則調用該 Runnable 對象的 run 方法;否則,該方法不執行任何操作並返回。
 void setContextClassLoader(ClassLoader cl)
          設置該線程的上下文 ClassLoader。
 void setDaemon(boolean on)
          將該線程標記爲守護線程或用戶線程。
static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
          設置當線程由於未捕獲到異常而突然終止,並且沒有爲該線程定義其他處理程序時所調用的默認處理程序。
 void setName(String name)
          改變線程名稱,使之與參數 name 相同。
 void setPriority(int newPriority)
          更改線程的優先級。
 void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
          設置該線程由於未捕獲到異常而突然終止時調用的處理程序。
static void sleep(long millis)
          在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行)。
static void sleep(long millis, int nanos)
          在指定的毫秒數加指定的納秒數內讓當前正在執行的線程休眠(暫停執行)。
 void start()
          使該線程開始執行;Java 虛擬機調用該線程的 run 方法。
 void stop()  停止線程
 void stop(Throwable object)
 void suspend()  線程掛起
 String toString()
          返回該線程的字符串表示形式,包括線程名稱、優先級和線程組。
static void yield()
         線程讓步, 暫停當前正在執行的線程對象,並執行其他線程。

除此之外還可以使用object中的三個方法來對進程進行調度:

  this.notify();    // 喚醒另外一個進程
  this.notifyAll(); // 喚醒所有進程
  this.wait();      // 等待
  this.wait(timeout);  // 等待多長時間

使用進程同步:用一個例子來說明同步的重要性

package edu.hue.jk.thread;


//要理解進程同步首先要搞清楚進程同步的定義:
/**
 * 在操作系統中進程同步是指,系統中多個進程中發生的事件存在某種時序關係,需要相互協作,共同完成任務。具體的說一個線程運行在某一點時
 * 需要另外一個線程爲它提供消息,在未獲得消息之前一直處在等待狀態,獲得消息之後該進程被喚醒處於就緒狀態。而且當多個線程訪問同一份資源(貢獻資源)
 * 的時候,可能會引起衝突,所以要加入同步機制來控制多個線程之間的訪問順序,一遍造成讀寫不一致的情況。
 * 現在假設一個網上售票系統,有三個點可以售票,假設總共有五張票,程序如下:
 * @author dell
 *
 */
public class WhySynchronized {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		TiketThread tiket=new TiketThread();
		new Thread(tiket,"tiket1").start();
		new Thread(tiket,"tiket2").start();
		new Thread(tiket,"tiket3").start();
	}

}
class TiketThread implements Runnable{  //  充分說明了進程同步的重要性
	static int tiket=5;
	public void run() {
		//synchronized(this){    // 不使用同步
		while(true){
			if(tiket>0)
			{
				try {
					Thread.sleep(200);   //  加入延時方便查看期待的結果
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"selling tikets"+tiket);
				tiket--;
			}
			else
				break;
		}
		//}
	}
	
}

可見沒有使用同步,使得多線程的使用得不到我們需要的效果,將使用同步的註釋去掉就可以得到正確的結果。

爲了跟進一步的理解進程的同步使用方法,以及如何去操作進程,引入生產者和消費者的兩個例子:

<span style="color:#000000;">package edu.hue.jk.thread;

/**
 * 用來描述生產者消費者問題          生產者不斷的生產產品,消費者不斷的取出產品;生產者生產一個產品,消費者就要取出一個產品,生產者不能連續生產,消費者也不能連續取產品
 * 對於生產者和消費者來說,產品就是共享資源,而且不能同時去控制產品,所以使用同步操作
 * 使用信號量來控制生產者和消費者的協調工作:
 * s1=1 表示生產者可以生產,s1=0表示不能生產  初始值爲1
 * s1=0表示消費者可以取產品  s1=1 表示不能取  
**/
public class ProduceAndConsumer {
	public static void main(String[] args) {
		Product pro=new Product();
		pro.setS1(1);
		new Thread(new Produce(pro)).start();
		new Thread(new Consumer(pro)).start();  // 這裏要怎樣修改
	}  
}
class Product{
	private String productName;
	int s1=1;  // 用來協調生產者和消費者的信號量
	
	public int getS1() {
		return s1;
	}
	public void setS1(int s1) {
		this.s1 = s1;
	}
	public String getProductName() {
		return productName;
	}
	public void setProductName(String productName) {
		this.productName = productName;
	}
}

class Produce implements Runnable{
	Product product;
	
	public Produce(Product product) {
		super();
		this.product = product;
		
	}
	public void run() {
		while(true){  // 生產者不斷的生產產品
			synchronized(product){    //使用同步塊
				if(product.getS1()==1)      // 能夠生產
				{
					product.setProductName("productName");
					System.out.println("生產產品:"+product.getProductName());
					try {
						Thread.sleep(300);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					product.setS1(0);   // 生產者生產之後消費者能夠取出產品
					product.notify();  // 喚醒消費者
				}
				else  // 不能夠生產
				{
					
					System.out.println("不能生產產品:");
					try {
						Thread.sleep(300);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					
					try {
						product.wait();  // 等待
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
		}
	}
	
	}
}
class Consumer implements Runnable{
	Product product;
	public Consumer(Product product) {
		super();
		this.product = product;
	}
	public void run() {
		while(true){
			synchronized(product){
				if(product.getS1()==0)
				{
					System.out.println("消費者取出產品:"+product.getProductName());
					try {
						Thread.sleep(300);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					product.setS1(1);
					product.notify();  // 喚醒生產者
				}
				else
				{
					
					System.out.println("不能取出產品:");
					try {
						Thread.sleep(300);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					
					try {
						product.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
		
			}
		}
	}
	
}</span>

得到的結果

生產產品:productName
不能生產產品:
消費者取出產品:productName
不能取出產品:
生產產品:productName
不能生產產品:
消費者取出產品:productName
不能取出產品:
生產產品:productName
不能生產產品:
消費者取出產品:productName
不能取出產品:
生產產品:productName

生產者消費者問題變形:

<span style="color:#000000;">package edu.hue.jk.thread;
import java.util.LinkedList;
import java.util.List;

/**
 *<span style="color:#cc0000;"> 生產者和消費者問題變形: 現在有一個容量固定的緩衝區,緩衝區滿時生產者停止生產,緩衝區空時消費者停止消費
 * 前邊使用的是同步塊,下面使用同步方法來實現
 * @author dell
</span> *
 */
public class ProduceAndConsumer02 {
	public static void main(String[] args) {
		ProductList list=new ProductList(5);
		new Thread(new Produce02(list)).start();
		new Thread(new Consumer02(list)).start();
	}

}
class Product02 {
	String productName;
	public String getProductName() {
		return productName;
	}
	public void setProductName(String productName) {
		this.productName = productName;
	}
	public Product02(String productName) {
		super();
		this.productName = productName;
	}
	
}
// 表示緩衝區
class ProductList{
	int length ;// 表示緩衝區的容量
	int i=1;  // 表示剷平的序號
	List<Product02> productList;
	public ProductList(int length) {
		super();
		this.length = length;
		productList=new LinkedList<Product02>();
	}
	public ProductList() {
		super();
		productList=new LinkedList<Product02>();
	}
	// 使用同步方法生產產品
	public synchronized  void ProduceAProduct(){
		while(true){
			try {
				Thread.sleep(300);
			} catch (InterruptedException e1) {   //爲了觀察效果而設置的延遲操作
				e1.printStackTrace();
			}  //  
			if(length>productList.size()){   // 可以繼續生產產品
				Product02 pro=new Product02("product"+i);
				productList.add(pro);  // 加入緩衝區
				System.out.println("生產產品:"+pro.getProductName());
				i++;
				notify();
			}
			else{
				try {
					System.out.println("生產者等待。。。。。。");
					wait();
				} catch (InterruptedException e) {
					
					e.printStackTrace();
				}
			}
		}
		
	}
	public synchronized  void getAProduct(){
		while(true){
			try {
				Thread.sleep(300);
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}  //  
			if(productList.size()>0){   // 可以繼續生產產品
				Product02 pro=productList.get(0); // 始終取第一個產品
				productList.remove(0);   // 從緩衝區取出
				System.out.println("消費者取出:"+pro.getProductName());
				notify();
			}
			else{
				try {
					System.out.println("消費者等待。。。。。。");
					wait();
				} catch (InterruptedException e) {
					
					
					e.printStackTrace();
				}
			}
		}
		
	}
}
class Produce02 implements Runnable{
	ProductList list;
	public void run() {
		list.ProduceAProduct();
	}
	public Produce02(ProductList list) {
		super();
		this.list = list;
	}
}
class Consumer02 implements Runnable{
	ProductList list;
	public void run() {
		list.getAProduct();
	}
	public Consumer02(ProductList list) {
		super();
		this.list = list;
	}
}</span>


運行結果:

生產產品:product1
生產產品:product2
生產產品:product3
生產產品:product4
生產產品:product5
生產者等待。。。。。。
消費者取出:product1
消費者取出:product2
消費者取出:product3
消費者取出:product4
消費者取出:product5
消費者等待。。。。。。
生產產品:product6
生產產品:product7
生產產品:product8
生產產品:product9
生產產品:product10
生產者等待。。。。。。
消費者取出:product6


 


 

發佈了47 篇原創文章 · 獲贊 4 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章