一、線程概述
線程是在程序中獨立并發(fā)的執(zhí)行流,相比于進程,線程具有更高的性能,多個線程可以共享一個進程虛擬空間,線程之間共享內存非常方便,可以擁有很高的并發(fā)。
二、線程的創(chuàng)建和啟動
2.1 繼承Thread類創(chuàng)建線程類
通過繼承Thread類來創(chuàng)建啟動多線程的步驟如下:
(1)定義Thread的子類,重寫run方法
(2)創(chuàng)建Thread子類的實例
(3)通過start來啟動線程
Thread.currentThread()可以返回當前正在執(zhí)行的線程對象
getName()可以返回線程的名字
public class FirstThread extends Thread{
public void run()
{...}
}
new FirstThread().start();
2.2 實現(xiàn)Runnable接口創(chuàng)建線程類
通過Runnable接口來創(chuàng)建并啟動多線程的步驟如下:
(1)定義Runnable的實現(xiàn)類,并且重寫run方法
(2)創(chuàng)建Runnable實現(xiàn)類的實例,并且以此實例作為Thread的target來創(chuàng)建Thread對象
public class SecondThread implements Runnable
{
public void run()
{...}
}
new Thread(st,'name').start();
采用Runnable接口的方式來創(chuàng)建的多條線程可以共享線程類的實例屬性。
采用Runnable接口方式來創(chuàng)建的線程只能通過Thread.currentThread()來獲取當前線程,而不能通過this
3 線程的生命周期
- 新建狀態(tài):new了一個線程之后
- 就緒狀態(tài):調用了start之后(永遠不要直接調用run方法)
- 運行狀態(tài):線程獲得CPU,開始運行run方法之后
- 阻塞狀態(tài):調用sleep,調用阻塞式IO方法,等待通知,程序調用了suspend方法等,都會使線程進入阻塞狀態(tài)。
- 線程死亡:run()執(zhí)行完畢,拋出未捕獲的Exception和Error,或者調用該程序的stop()方法來結束該線程。
可以通過線程的isAlive方法來判斷線程是否死亡
不要試圖對一個已經死亡的線程調用start方法
4 控制線程
4.1 join線程
某個線程調用另一個線程的join方法以后,該線程將被阻塞,直到被join的線程執(zhí)行完畢以后為止。
- join()
- join(long millis)
- join(long millis,int nanos)
4.2 后臺線程
后臺線程有個特征:如果前臺線程都死亡,則后臺線程會自動死亡。
通過調用Thread的setDaemon(true)的時候,就可以將其設為后臺線程。
可以通過isDaemon()來判斷是否是后臺線程
4.3 線程睡眠Sleep
- Thread.sleep(long millis)
- Thread.sleep(long millis, int nanos)
4.4 線程讓步y(tǒng)ield
yield方法不會阻塞該線程,而是轉入就緒狀態(tài),讓線程調度器重新調度一次。
- Thread.yield
4.5 改變線程優(yōu)先級
- Thread.setPriority(int newPriority)
優(yōu)先級值在1~10之間,也可以使用MAX_PRIORITY(10),MIN_PRIORITY(1),NORM_PRIORITY(5)
5 線程的同步
5.1同步代碼塊
synchronized(obj){ 同步代碼塊 }
5.2同步方法
public synchronized void draw(double drawAmount)
5.3釋放同步監(jiān)視器的鎖定
以下情況會釋放:
- 代碼塊執(zhí)行完畢
- break return
- 拋出錯誤或者異常
- 程序執(zhí)行了同步監(jiān)視器對象的wait()方法
5.4 同步鎖
常用的有可重用鎖ReentrantLock,可重用的意思是說,對已經加鎖的ReentrantLock可以再次上鎖,該對象會維持一個計數器來記錄。
public class Accout
{
private final ReetrantLock lock = new ReentrantLock();
public void draw(double drawAmout)
{
lock.lock();
try
{
//臨界區(qū)
}
finally
{
lock.unlock();
}
}
}
5.5 死鎖
當兩個線程相互等待對方釋放同步監(jiān)視器就會發(fā)生死鎖。
6 線程通信
6.1 線程的協(xié)調運行
Object類提供的wait(), notify(),notifyAll()三個方法,必須由同步監(jiān)視器來調用:
- 對于使用synchronized修飾的同步方法,該類的默認實例this就是同步監(jiān)視器
- 對于使用synchronized修飾的代碼塊,后面括號里的對象就是同步監(jiān)視器
6.2 使用條件變量進行控制協(xié)調
如果程序不使用synchronized關鍵字來保持同步,而是直接使用Lock對象來保持同步,則系統(tǒng)中不存在隱式的同步監(jiān)視器對象。Java提供了一個Condition類來保持協(xié)調。
- await()
- signal()
- signalAll()
public class Account
{
private final Lock lock = new ReentrantLock();
private final Condition cond = lock.newCondition();
####6.3 使用管道流
如果兩條線程之間需要更多的信息交互,則可以考慮使用管道流。
PipedWriter pw = null;
PipedReader pr = null;
try
{
pw = new PipedWriter();
pr = new PipedReader();
pw.connect(pr);
new WriterThread(pw).start();
new ReaderThread(pr).start();
}
###7 Callable和Future
Callable看起來像是Runnable的接口和增強版,call方法是其線程執(zhí)行體,但是比run()方法更強大:可以有返回值,可以拋出異常
class RtnThread implements Callable<Integer>
{
public Integer call(){
return i
}
}
public class CallableTest
{
public static void main(String[] args)
{
RtnThread rt = new RtnThread();
FutrueTask<Integer> task = new FutureTask<Integer>(rt);
new Thread(task,'name').start();
System.out.println(task.get());
}
}
###8 線程池
略過
###9 線程相關類
####9.1 ThreadLocal類
ThreadLocal類是線程局部變量的意思,就是為每一個使用該變量的線程都提供一個變量值的副本。從而避免沖突。
ThreadLocal提供了三個public方法:
* T get()
* void remove()
* void set(T value)
>如果需要進行多個線程之間的資源共享,就需要使用同步機制,如果只是需要隔離多個線程之間的共享沖突,則可以使用ThreadLocal
####9.2 線程包裝不安全的集合
Java集合中介紹的ArrayList,LinkedList, HashSet, TreeSet, HashMap都是線性不安全的。可以使用Collections的方法來將其變成線程安全:
SynchronizedCollection/List/Map/Set/SortedMap/SortedSet
####9.3 線程安全的集合類
JDK1.5開始,java.util.concurrent包下提供了ConcurrentHashMap, ConcurrentLinkedQueue。