Java高併發——多執行緒基礎
在上一篇 JAVA高併發——瞭解並行世界 中我們回顧了一些多執行緒的概念知識。這裡先舉個例子,來看現實生活中的多執行緒例子:一個家庭中有爸爸、媽媽、兒子,家中有電視、洗衣機、書桌等,如果媽媽領著兒子出去了,爸爸就可以想幹什麼幹什麼(單執行緒);如果三人都在家,媽媽可以洗衣服、爸爸在書桌上工作、兒子看動畫片(三個執行緒使用不同的資源互不影響);如果兒子看動畫片,爸爸想看NBA視訊,就得等著兒子看完了(多執行緒公用資源,出現阻塞)……看見了吧,其實我們就是把程式編寫的如何像現實生活一樣,多個任務同時進行,有共享資源,有各自資源,但是大家能夠有條不紊的進行著……
好,這篇我們來複習一下Java多執行緒的一些基礎知識,來看下這張思維導圖:
一,首先需要了解執行緒的狀態有哪些,怎麼扭轉,其實也就是生命週期(現實生活做事也是可以這樣抽象)。我先看下Java,Thread類中對於state的定義:
public enum State { /** * Thread state for a thread which has not yet started. */ NEW, /** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */ RUNNABLE, /** * Thread state for a thread blocked waiting for a monitor lock. * A thread in the blocked state is waiting for a monitor lock * to enter a synchronized block/method or * reenter a synchronized block/method after calling * {@link Object#wait() Object.wait}. */ BLOCKED, /** * Thread state for a waiting thread. * A thread is in the waiting state due to calling one of the * following methods: * <ul> * <li>{@link Object#wait() Object.wait} with no timeout</li> * <li>{@link #join() Thread.join} with no timeout</li> * <li>{@link LockSupport#park() LockSupport.park}</li> * </ul> * * <p>A thread in the waiting state is waiting for another thread to * perform a particular action. * * For example, a thread that has called <tt>Object.wait()</tt> * on an object is waiting for another thread to call * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on * that object. A thread that has called <tt>Thread.join()</tt> * is waiting for a specified thread to terminate. */ WAITING, /** * Thread state for a waiting thread with a specified waiting time. * A thread is in the timed waiting state due to calling one of * the following methods with a specified positive waiting time: * <ul> * <li>{@link #sleep Thread.sleep}</li> * <li>{@link Object#wait(long) Object.wait} with timeout</li> * <li>{@link #join(long) Thread.join} with timeout</li> * <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> * <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> * </ul> */ TIMED_WAITING, /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED; }
好,看下這幾種狀態的相互轉化吧:
二,接下來看下執行緒的基本操作:
1,新建執行緒:這個不用說了,通過繼承Thread或者實現Runnable介面兩種方式。
2,終止執行緒:一般來說,執行緒執行完畢就會結束,無須自動關閉。但是對於一些while true的執行緒如果想關閉呢?Thread類裡有stop()方法,但是已經不建議使用了。因為它是強制停止執行緒,無論執行緒處於什麼狀態,很容易出現執行緒正在處理一半資料被停止的情況,這樣非常容易造成資料不一致問題。所以慎用stop()(最好不用),通過下邊這種方式來停止哪些無限迴圈的執行緒:
public class StopThread extends Thread {
volatile boolean stopme = false;
//停止方法
public void stopMe() {
stopme = true;
}
@Override
public void run() {
while (true) {
//死迴圈中如果出現停止標識,則直接跳出
if (stopme) {
System.out.println("exit by stop me");
break;
}
System.out.println(System.currentTimeMillis());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3,執行緒中斷:是一種執行緒協作機制。是告訴目標執行緒一箇中斷通知,至於接到通知後如何處理,則有執行緒自己決定處理。
public class InterruptThread {
/**
* public void interrupt() 中斷執行緒
* public static boolean interrupted() 判斷是否被中斷,並清除當前中斷標識
* public boolean isInterrupted() 判斷是否被中斷
*/
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
while (true) {
//判斷如果有中斷標識,則直接跳出
if (Thread.currentThread().isInterrupted()) {
System.out.println("exit by interrupted");
break;
}
System.out.println(System.currentTimeMillis());
Thread.yield();
}
}
};
t1.start();
Thread.sleep(2000);
//打上中斷標識
t1.interrupt();
}
}
4,等待(wait)和通知(notify),也是為了支援執行緒之間的協作。方法都是在Object上定義的,例如執行緒A呼叫了obj.wiat()方法,那麼執行緒A就會停止執行,等到其它執行緒呼叫obj.notify()方法為止。
public class WaitNotifyThread {
final static Object ob = new Object();
public static class T1 extends Thread {
@Override
public void run() {
synchronized (ob) {
System.out.println(System.currentTimeMillis() + "t1 is start");
try {
System.out.println(System.currentTimeMillis() + "t1 is wait");
ob.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + "t1 is end");
}
}
}
public static class T2 extends Thread {
@Override
public void run() {
synchronized (ob) {
System.out.println(System.currentTimeMillis() + "t2 is start");
System.out.println(System.currentTimeMillis() + "t2 is notify start");
ob.notify();
System.out.println(System.currentTimeMillis() + "t2 is notify end");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + "t2 is end");
}
}
}
public static void main(String[] args) {
Thread t1 = new T1();
Thread t2 = new T2();
t1.start();
t2.start();
}
}
5,掛起(suspend)和繼續執行(resume):也是一對相對的操作,但是JDK也已經標示為廢棄方法,不推薦使用。應為使用suspend()去掛起導致執行緒暫停的同時,並不釋放資源,會阻塞其它執行緒,如果處理不當(例如resume()處理在suspend()之前了,就會導致一直阻塞),所以慎用。我們可以通過wait和notify來像實現stopMe()一樣,實現自己一個掛起和繼續執行操作。這裡不再演示。
6,等待執行緒結束(join)和謙讓(yield):類似現實生活中,我做這件事得等著它做完那件事,我為他讓讓步等。很容易理解的:
public class JoinThread {
volatile static int i = 0;
public static class AddThread extends Thread{
@Override
public void run() {
for(i=0;i<10000;i++);
}
}
public static void main(String[] args) throws InterruptedException {
AddThread addThread = new AddThread();
addThread.start();
//主執行緒等待addTread執行完畢
addThread.join();
System.out.println(i);
}
}
三,volatile:非常重要的關鍵字,如果用volatile修飾,虛擬機器就會特殊處理,不能隨意變動優化目標指令;並且修改後,各個執行緒都能看見它的改動,保證變數的可見性。在那個中共享變數是“一寫多讀”的情況下非常使用,保證修改及時可見。
四,執行緒組:非常容易理解的概念,如果執行緒過多,為了方便管理,有了執行緒組,簡單看下使用:
public class ThreadGroupTest implements Runnable {
public void run() {
String groupName = Thread.currentThread().getThreadGroup().getName() + "--" + Thread.currentThread().getName();
while (true){
System.out.println(" i am " + groupName);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
ThreadGroup threadGroup = new ThreadGroup("testGroup");
Thread t1 = new Thread(threadGroup,new ThreadGroupTest(),"T1");
Thread t2 = new Thread(threadGroup,new ThreadGroupTest(),"T2");
t1.start();
t2.start();
System.out.println(threadGroup.activeCount());
threadGroup.list();
}
}
五,守護執行緒(Daemon):是一種特殊的執行緒,聽名字猜想這種執行緒是系統的守護者。例如jvm的垃圾回收執行緒等。當一個Java應用中只有守護執行緒時,也就會自然退出:
public class DaemonDome {
public static class T1 extends Thread {
@Override
public void run() {
while (true) {
System.out.println(" i am live");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t = new T1();
t.setDaemon(true);
t.start();
Thread.sleep(2000);
}
}
六,執行緒優先順序,這個比較好理解,優先順序從1到10數字越大優先順序越高,只是說概率更高而已,而並非一定。Tread提供三個靜態變數:
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
七,執行緒安全和synchronized:執行緒安全就是針對共享資源能夠高效有序的使用,不要出現同時修改,最終導致資料不一致的問題。而共享資源的同步使用即可解決。synchronized的作用就是實現執行緒間的同步。前邊已經用到,我們不在看例子,總結一下它的用法:1,指定加鎖物件:給物件加鎖,進入同步程式碼前要獲得給定物件的鎖:synchronized(obj);2,直接作用於例項方法:相當於對當前例項加鎖,進入同步程式碼前要獲得當前例項的鎖:synchronized(this);3,直接作用於靜態方法:相當於當前類加鎖,進入同步程式碼前要獲得當前類的鎖。
好,高併發——Java多執行緒的複習總結就這樣,還是說結合實際生活來學習聯想。接下來,我們會繼續……