線程是java的編程中的重中之重,弄清概念是一個程序員的基本功。下面介紹下線程相關的基本概念和實現。java
計算機中的程序關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位(指一個內存中運行的應用程序,每一個進程都有本身獨立的一塊內存空間,一個進程中能夠啓動多個線程)
程序員
有時被稱爲輕量級進程(Lightweight Process,LWP),是程序執行流的最小單元(cpu運行)。一個標準的線程由線程ID,當前指令指針(PC),寄存器集合和堆棧組成。另外,線程是進程中的一個實體,是被系統獨立調度和分派的基本單位,線程本身不擁有系統資源,只擁有一點兒在運行中必不可少的資源,但它可與同屬一個進程的其它線程共享進程所擁有的所有資源。編程
tips:這裏形容下,一個應用程序至關於一個倉庫,而一個線程至關於一個取貨單。有一個管理員(cpu),每一隻能取一個商品(計算結果)。管理員不會一次把一個單子的商品取完,而是隨機按着一個單子的順序取一個商品,而後隨機按另一個單子取貨。
安全
java中的實現:
ide
a) 新生態:new Thread()對象,這個對象封裝了JVM啓動一個新線程的方式,當start()後將控制權交給程序計數器,生成新的線程。
this
b) 就緒狀態「在程序計數器列表中、等待CPU的使用權<start(),notify(),nitify all(),I/O完成>spa
d) 運行狀態:佔用cpu時間,進行邏輯運算。<run(),等待調度器調用>線程
e) 阻塞狀態:處於不正常的運行和等待中。<jion(),wait(),sleep(),suspend(),I/O請求>指針
f) 死亡狀態: 運行完成、強行中止。<stop()、destory()>code
1. 只有run()狀態能夠直接獲取數據的使用權
2. 線程的研究主要是在運行狀態和阻塞狀態。
3. 線程雖然有優先級,可是JVM並非100%安裝優先級來調用。隨機錯亂的調用方式
4. 阻塞的方式:一種是sleep(),整個線程讓調度器中止調度。一種是線程在對象鎖的等待序列上。
5. 抽象一個圖,便於記憶:線程會在紅色部分中止調度,只有在在可執行序列纔會被調度。
每個線程都會有一個線程名。
a)主線程名:程序的啓動線程接---線程名:main
b)默認線程名: thread-N(N是數字)
c)自定義線程名
/** * @see 線程名 * * @author ssHss * */ public class ThreadName implements Runnable { public static void main(String[] args) { System.out.println(Thread.currentThread().getName());// main ThreadName tn = new ThreadName(); Thread tr = new Thread(tn);//線程名:thread-0 Thread tr2 = new Thread(tn, "tr2");//線程名:tr2 tr.start(); tr2.start(); } @Override public void run() { System.out.println(Thread.currentThread().getName()); } }
a)Thread.yield():讓步,建議具備相同優先級的其它線程能夠運行了。
/** * @see yield:將當前線程從運行態放入到可運行態、將CPU交給線程優先級高的線程 * @author ssHss * */ public class ThreadYield implements Runnable { public static void main(String[] args) { System.out.println(Thread.currentThread().getName() + " start"); ThreadYield ty = new ThreadYield(); // 線程1 Thread t1 = new Thread(ty, "t--1"); t1.setPriority(8);// 默認的優先級爲5 // 線程2 Thread t2 = new Thread(ty, "t--2"); // t1.setPriority(8); t1.start(); t2.start(); System.out.println(Thread.currentThread().getName() + " end"); } @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + " :獲取了 : " + i); if (i % 3 == 0) { Thread.yield(); } } } }
b)Thread.sleep(x毫秒):讓當前線程阻塞x毫秒。
c) join():一個線程在其餘線程上調用join()方法,其效果將其它線程排在本線程後面。
/** * @see t.JOIN:將當前線程加入到t線程的後面 * */ public class ThreadJoin { public static void main(String[] args) throws InterruptedException { Sleeper sleepy = new Sleeper("Sleeper", 1500); Sleeper grumpy = new Sleeper("Grumpy", 1500); Joiner dopey = new Joiner("Dopey", sleepy); Joiner doc = new Joiner("Doc", grumpy); grumpy.interrupt(); } } class Sleeper extends Thread { private int duration; public Sleeper(String name, int sleepTime) { super(name); duration = sleepTime; this.start(); } public void run() { try { Thread.sleep(duration); } catch (Exception e) { System.out.println(this.getName() + " was interrupted " + this.isInterrupted()); } System.out.println(this.getName() + " has awakened"); } } class Joiner extends Thread { private Sleeper sleeper; public Joiner(String name, Sleeper sleeper) { super(name); this.sleeper = sleeper; this.start(); } public void run() { try { sleeper.join(); } catch (Exception e) { System.out.println("Ineterrupted"); } System.out.println(this.getName() + " join completed"); } }
public class Executor { /** * @see Executor:線程池,管理線程的建立和銷燬 * @param args */ public static void main(String[] args) { //ExecutorService es = Executors.newCachedThreadPool();//建立與所需線程相同數量的線程,回收舊線程時中止新建線程 //ExecutorService es = Executors.newFixedThreadPool(5);//建立固定數量的線程。 ExecutorService es = Executors.newSingleThreadExecutor();//建立單個線程,而後排隊執行 for (int i = 0; i < 5; i++) { es.execute(new TestClass()); } es.shutdown(); } } class TestClass implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + " i-->" + i); } } }
3.4 synchronized: 對象鎖
a)每一個對象都有一個對象鎖,保證線程在操做數據時的,數據一致性。
c)獲取了這個鎖,就獲取了這個的資源控制權。其它線程只有去對象的對象鎖列表(阻塞狀態),等待鎖釋放。在釋放鎖的時候,會把全部對象鎖列表中的線程都移動到可運行列表中。
b)釋放鎖的方式:(理解記憶)
1.自動釋放鎖(運行完):將對象鎖列表中的線程都移動到可運行狀態列表,包括本身,而後釋放鎖。全部線程都是可執行狀態,而後搶佔鎖的使用權。
2.wait釋放鎖 :把本身加入到對象鎖列表,並釋放鎖。可是不清理對象鎖列表。
Tips:1.當獲取對象鎖的時候(synchronzied),會同將鎖裏面調用的對象一塊兒鎖定(當前對象的嵌套、其它對象的調用)。
c)因爲對象鎖synchronized使用:
非靜態方法:synchronized(this) 鎖的是堆中的地址空間。
靜態方法:synchronized(xxxx.class) 鎖的是class 文件存放在永久區的地址空間。通常靜態變量最好是放在靜態方法中執行,保證安全行。
/** * @see 生存者、消費者模式 * @author ssHss * */ public class ThreadSynChron implements Runnable { Test tc = null; public static void main(String[] args) { System.out.println(Thread.currentThread().getName() + " START"); Test test = new Test(); ThreadSynChron tsc = new ThreadSynChron(test); Thread t1 = new Thread(tsc); Thread t2 = new Thread(tsc); t1.start(); t2.start(); } public ThreadSynChron(Test test) { this.tc = test; } public void A() { synchronized (this) { System.out.println(Thread.currentThread().getName() + " A "); this.B(); } } public void B() { synchronized (this) { System.out.println(Thread.currentThread().getName() + " B "); tc.TestC(); } } @Override public void run() { synchronized (this) { System.out.println(Thread.currentThread().getName() + " run "); this.A(); } } } class Test { public void TestC() { synchronized (this) { System.out.println(Thread.currentThread().getName() + " TestC "); this.TestD(); } } public void TestD() { synchronized (this) { System.out.println(Thread.currentThread().getName() + " TestD "); } } public void TestE() { synchronized (this) { System.out.println(Thread.currentThread().getName() + " TestE "); } } public void TestF() { synchronized (this) { System.out.println(Thread.currentThread().getName() + " TestF "); } } public void TestG() { synchronized (this) { System.out.println(Thread.currentThread().getName() + " TestG "); } } } 。
理解記憶:
a)wait:把本身加入到對象鎖列表
b)nitify:先將對象鎖列表中的線程移動到可執行序列,告訴JVM調度器,本對象的使用權即將可用。請作好準備,請在對象鎖釋放後調用。
package thread; /** * @see 消費這生產者 * @author ssHss * */ public class CustomerProduce { public static void main(String[] args) { Store st = new Store(); Customer2 ctom = new Customer2(st); Producer pdc = new Producer(st); Thread t1 = new Thread(ctom); Thread t2 = new Thread(pdc); t1.start(); t2.start(); } } // 消費者 class Customer2 implements Runnable { public Store store; public Customer2(Store store) { this.store = store; } @Override public void run() { synchronized (store) { System.out.println(Thread.currentThread().getName() + " 開始消費 "); try { this.store.customer(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 消費結束 "); } } } // 生成者 class Producer implements Runnable { public Store store; public Producer(Store store) { this.store = store; } @Override public void run() { synchronized (store) { System.out.println(Thread.currentThread().getName() + " 開始生產 "); try { this.store.produce(); } catch (Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 生存結束 "); } } } // 倉庫 class Store { private int breadNumber; // 總共 private int afterNumber; // 剩餘 // 生成 public void produce() throws InterruptedException { synchronized (this) { for (int i = 0; i < 20; i++) { // 若是還有剩餘大於0,則喚醒 if (afterNumber > 0) this.notify(); // 若是大於5,則等待 if (afterNumber > 5) this.wait(); breadNumber++; afterNumber++; System.out.println(Thread.currentThread().getName() + " 生產了麪包: " + breadNumber + " 剩餘 " + afterNumber); } this.notify(); } } // 消費 public void customer() throws InterruptedException { synchronized (this) { for (int i = 0; i < 15; i++) { if (afterNumber <= 2) // this.notify(); // 若是還有剩餘==0,則等待 if (afterNumber == 0) this.wait(); afterNumber--; System.out.println(Thread.currentThread().getName() + " 消費了麪包: " + breadNumber + " 剩餘 " + afterNumber); } this.notify(); } } }
最開始執行狀態:
調用notify():
調用:wait()
執行結果: