1、線程的實現方式 線程有兩種實現方式,分別為繼承Thread、實現Runnable介面。差異性:實現方式避免了類的繼承單一性,且對於多個線程同時訪問同一個資源時更便捷。 (1)繼承Thread class TestThread extends Thread { @Override public v ...
1、線程的實現方式
線程有兩種實現方式,分別為繼承Thread、實現Runnable介面。差異性:實現方式避免了類的繼承單一性,且對於多個線程同時訪問同一個資源時更便捷。
(1)繼承Thread
class TestThread extends Thread { @Override public void run() { for (int i = 0; i <= 100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } } public class TestThreadMethod { public static void main(String[] args) { TestThread th1 = new TestThread(); th1.setName("testThread"); th1.start(); } }View Code
(2)實現Runnable介面
class TestRunnable implements Runnable{ @Override public void run() { for (int i=1; i <= 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } } public class TestMain { public static void main(String[] args) { TestRunnable tr = new TestRunnable(); Thread th1 = new Thread(tr, "th1"); Thread th2 = new Thread(tr, "th2"); th1.start(); th2.start(); } }View Code
(3)線程的生命周期
2、線程的常用方法
start():線程的開啟。同一個線程在結束時只能開啟一次,否則報錯。
setName():設置當前線程的名稱。
getName():獲取當前線程的名稱。
currentThread():獲取當前線程的對象。
priority():線程的級別。靜態值有,0/5(預設)/10。值越高,代表爭奪CPU的使用權的幾率越大,但不是絕對。
yield():線程讓步。當前線程A調用yield()方法時,釋放CPU的使用權,重新與其他線程B爭奪CPU的使用權。
join():線程加入。在A線程中,調用B線程的join方法時,A線程的程式等待B線程的程式執行完才能繼續執行A線程。
sleep():線程睡眠。當線程執行該方法時,進入設置的睡眠時間後繼續執行程式。
isAlive():線程是否在活動,返回值為boolean。
wait():線程等待。必須在同步方法中使用。
notify()/notifyAll():線程喚醒,與wait()結合使用,用於線程通信。
註意:(1)線程在同步時,wait()會釋放鎖,而join()與sleep()方法不會釋放鎖。
(2)wait()、notify()、notifyAll()嚴格來說不算線程中的方法,這三個方法是定義在Object對象中。
class TestThread extends Thread { @Override public void run() { for (int i = 0; i <= 100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } } public class TestThreadMethod { public static void main(String[] args) { TestThread th1 = new TestThread(); th1.setName("testThread"); th1.setPriority(Thread.MAX_PRIORITY); th1.start(); Thread.currentThread().setName("=======mainThread"); for (int i = 0; i <= 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); if (i % 10 == 0) { Thread.yield(); } if (i == 20) { try { th1.join(); System.out.println(th1.isAlive()); th1.start(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }View Code
3、線程的同步
線程的同步分為兩種:同步代碼塊和同步方法塊。
class TestThread extends Thread { static int ticket = 100; static Object obj = new Object(); @Override public void run() { /*1、同步代碼塊時,只能同步需要的代碼塊,其餘無關代碼塊不行,會影響同步; * 2、同步監視器用this,代表當前對象,此時繼承方式,對象是多個,所以無法同步; * 此兩種情況出現重覆輸出情況,如下的this情況 */ /*synchronized (this) { while(true) { try { Thread.currentThread().sleep(100); } catch (InterruptedException e) { // TODaO Auto-generated catch block e.printStackTrace(); } if (ticket <= 0) { break; } System.out.println(Thread.currentThread().getName() + "視窗:" +ticket--); } }*/ while(true) { synchronized (obj) {//obj充當鎖時必須是靜態的,否則多個對象代表的鎖是各自的。 try { Thread.currentThread().sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } if (ticket <= 0) { break; } System.out.println(Thread.currentThread().getName() + "視窗:" +ticket--); } } } } public class TestMain { public static void main(String[] args) { TestThread th1 = new TestThread(); TestThread th2 = new TestThread(); th1.setName("th1"); th2.setName("th2"); th1.start(); th2.start(); } }View Code
class TestThread implements Runnable { int ticket = 100; @Override public void run() { while(true) { importNum(); } } /** * 同步方法塊時,同步鎖為當前對象.所以方法的類必須是同一個對象 */ private synchronized void importNum() { if (ticket <= 0) { return; } System.out.println(Thread.currentThread().getName() + "視窗:" + ticket--); } } public class TestMain { public static void main(String[] args) { TestThread th = new TestThread(); Thread th1 = new Thread(th, "th1"); Thread th2 = new Thread(th, "th2"); th1.start(); th2.start(); } }View Code
註:同步在Thread與Runnable兩種方式的實現中有很大的區別,詳見上例。
拓展示例:
class SingleTon { private SingleTon() { } public static SingleTon instance = null; public static SingleTon getSingleTon() { /** * 原程式直接判斷,創建實例,返回。在多線程中,存線上程安全問題,會創建多個SingleTon; * 開發中為避免這情況發生,採用下麵同步的實現方式。 */ /** if (instance == null) { instance = new SingleTon(); } return instance; */ if (instance == null) {//提高同步效率,後面訪問方法的對象,無需再次等待 synchronized(SingleTon.class) {//採用類本身的對象來充當鎖 if (instance == null) { instance = new SingleTon(); } } } return instance; } } public class TestMain { public static void main(String[] args) { SingleTon st1 = SingleTon.getSingleTon(); SingleTon st2 = SingleTon.getSingleTon(); } }View Code
4、線程的死鎖
死鎖:當兩個線程同時需要對方手中的鎖時,各自都在等對方放棄手中的鎖,而使程式無限期的等待下去,稱為死鎖。
public class TestMain { public static void main(String[] args) { StringBuffer sb1 = new StringBuffer(); StringBuffer sb2 = new StringBuffer(); new Thread(){ public void run() { synchronized(sb1) { sb1.append("A"); try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(sb2) { sb2.append("B"); System.out.println("sb1:" + sb1.toString()); System.out.println("sb2:" + sb2.toString()); } } }; }.start(); new Thread(){ public void run() { synchronized(sb2) { sb2.append("C"); try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } synchronized(sb1) { sb1.append("D"); System.out.println("sb1:" + sb1.toString()); System.out.println("sb2:" + sb2.toString()); } } }; }.start(); } }View Code
上述例子中,第一個線程執行第一個同步塊代碼後,進行睡眠,握住sb1的鎖;此時第二個線程可能已經開啟運行,獲取sb2的鎖,進行同步代碼塊方法,進行睡眠。之後第一個線程睡眠時間過後要進行第二個同步代碼塊的執行,這時sb2的鎖握在了第二個線程,而第二個線程在醒來後要獲取第二塊同步方法的鎖時,這時的鎖在第一個線程中。兩個線程同時在等待對方釋放手中的鎖,導致了死鎖的出現。
5、線程的通信
/** * 生產者 --> 店員(數量最多存放20) --> 消費者 * */ class Clerk { int num; public synchronized void addProduct() { if (num >= 20) { try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } notifyAll(); num++; System.out.println(Thread.currentThread().getName() + ":" + num); } public synchronized void consume() { if (num == 0) { try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } notifyAll(); System.out.println(Thread.currentThread().getName() + ":" + num); num--; } } class Customer implements Runnable { Clerk clerk; public Customer(Clerk clerk) { this.clerk = clerk; } public void run() { while(true) { try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } clerk.consume(); } } } class Producer implements Runnable { Clerk clerk; public Producer(Clerk clerk) { this.clerk = clerk; } public void run() { while(true) { try { Thread.currentThread().sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } clerk.addProduct(); } } } public class TestMain { public static void main(String[] args) { Clerk clerk = new Clerk(); Customer c1 = new Customer(clerk); Producer p1 = new Producer(clerk); Thread th2 = new Thread(p1, "生產者"); Thread th1 = new Thread(c1, "消費者"); th2.start(); th1.start(); } }View Code