在上一篇 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多線程的複習總結就這樣,還是說結合實際生活來學習聯想。接下來,我們會繼續……