1、繼承Thread類實現多線程
繼承Thread類的方法盡管被我列為一種多線程實現方式,但Thread本質上也是實現了Runnable接口的一個實例,它代表一個線程的實例,並且,啟動線程的唯一方法就是通過Thread類的start()實例方法。start()方法是一個native方法,它將啟動一個新線程,並執行run()方法。這種方式實現多線程很簡單,通過自己的類直接extend Thread,並復寫run()方法,就可以啟動新線程並執行自己定義的run()方法。例如:
package com.multithread.learning; class Thread1 extends Thread{ private String name; public Thread1(String name) { this.name=name; } public void run() { for (int i = 0; i < 5; i++) { System.out.println(name + "運行 : " + i); try { sleep((int) Math.random() * 10); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class Main { public static void main(String[] args) { Thread1 mTh1=new Thread1("A"); Thread1 mTh2=new Thread1("B"); mTh1.start(); mTh2.start(); } }
運行結果:
A運行:0
B運行:0
A運行:1
B運行:1
A運行:2
B運行:2
A運行:3
B運行:3
A運行:4
B運行:4
但是start方法重復調用的話,會出現java.lang.IllegalThreadStateException異常。
二、實現java.lang.Runnable接口
采用Runnable也是非常常見的一種,我們只需要重寫run方法即可。下面也來看個實例。
package com.thread; /** * Created by HJS on 2017/8/11. */ class Thread2 implements Runnable { private String name; public Thread2(String name){ this.name=name; } @Override public void run(){ for (int i=0;i<5;i++){ System.out.println(name+"運行:"+i); try{ Thread.sleep((int)Math.random()*10); }catch (InterruptedException e){ e.printStackTrace(); } } } }
package com.thread; /** * Created by HJS on 2017/8/11. */ public class Main2 { public static void main(String[] args){ new Thread(new Thread2("C")).start(); new Thread(new Thread2("D")).start(); } }
結果1:
D運行:0
C運行:0
D運行:1
C運行:1
D運行:2
C運行:2
D運行:3
C運行:3
C運行:4
D運行:4
結果2:
D運行:0
C運行:0
D運行:1
D運行:2
C運行:1
C運行:2
C運行:3
C運行:4
D運行:3
D運行:4
總結:
實現Runnable接口比繼承Thread類所具有的優勢:
1):適合多個相同的程序代碼的線程去處理同一個資源
2):可以避免java中的單繼承的限制
3):增加程序的健壯性,代碼可以被多個線程共享,代碼和數據獨立
4):線程池只能放入實現Runable或callable類線程,不能直接放入繼承Thread的類
提醒:main方法其實也是一個線程。在java中所以的線程都是同時啟動的,至於什么時候,哪個先執行,完全看誰先得到CPU的資源。
在java中,每次程序運行至少啟動2個線程。一個是main線程,一個是垃圾收集線程。因為每當使用java命令執行一個類的時候,實際上都會啟動一個JVM,每一個JVM實際就是在操作系統中啟動了一個進程。
四、多線程售票
public class TestTicket { public static void main(String[] args) { Runnable st = new SellTicket(new Tick()); new Thread(st, "A").start(); new Thread(st, "B").start(); new Thread(st, "C").start(); new Thread(st, "D").start(); } public static class SellTicket implements Runnable { public Tick tick; Object mutex = new Object(); public SellTicket(Tick tick) { this.tick = tick; } public void run() { while (tick.getCount() > 0) { synchronized(mutex) { //需要有一個鎖變量 if(tick.getCount() <=0) break; //synchronized之前沒鎖住其他線程(有可能進入到while等待,當進入后需要重新判斷count值是大於0,不然就會變成0或負數) int temp = tick.getCount(); System.out.println(Thread.currentThread().getName() + "-----sale" + temp--); tick.setCount(temp); } } } } public static class Tick { private int count = 10; private Tick() { } private static final class lazyhodler { public static final Tick INSTANCE = new Tick(); } public static final Tick getInstance() { return lazyhodler.INSTANCE; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } } }
運行結果:
A-----sale10
A-----sale9
B-----sale8
C-----sale7
C-----sale6
C-----sale5
C-----sale4
C-----sale3
C-----sale2
C-----sale1
這里使用到synchronized關鍵字來添加內置鎖,具體關於synchronized的學習請移步下一篇文章:http://www.cnblogs.com/jiansen/p/7351872.html