我的網站:chenmingyu.top/concurrent-…java
進程:操做系統在運行一個程序的時候就會爲其建立一個進程(好比一個java程序),進程是資源分配的最小單位,一個進程包含多個線程編程
線程:線程是cpu調度的最小單位,每一個線程擁有各自的計數器,對戰和局部變量等屬性,而且能過訪問共享的內存變量安全
java線程的生命週期總共包括6個階段:多線程
start()
方法synchronized
修飾的代碼塊或方法notify()
或notifyAll()
進行喚醒在某一時刻,線程只能處於其中的一個狀態併發
線程初始化後,調用start()
方法變爲運行狀態,調用wait()
,join()
等方法,線程由運行狀態變爲等待狀態,調用notify()
或notifyAll()
等方法,線程由等待狀態變成運行狀態,超時等待狀態就是在等待狀態基礎上加了時間限制,超過規定時間,自動更改成運行狀態,當須要執行同步方法時,若是沒有得到鎖,這時線程狀態就變爲阻塞狀態,直到獲取到鎖,變爲運行狀態,當執行完線程的run()
方法後,線程變爲終止狀態ide
建立線程有三種方式網站
Thread
類Runnable
接口Callable
接口繼承Thread
類this
/** * @author: chenmingyu * @date: 2019/4/8 15:13 * @description: 繼承Thread類 */
public class ThreadTest extends Thread{
@Override
public void run() {
IntStream.range(0,10).forEach(i->{
System.out.println(this.getName()+":"+i);
});
}
public static void main(String[] args) {
Thread thread = new ThreadTest();
thread.start();
}
}
複製代碼
Runnable
接口/** * @author: chenmingyu * @date: 2019/4/8 15:18 * @description: 實現Runnable接口 */
public class RunnableTest implements Runnable {
@Override
public void run() {
IntStream.range(0,10).forEach(i->{
System.out.println(Thread.currentThread().getName()+":"+i);
});
}
public static void main(String[] args) {
Runnable runnable = new RunnableTest();
new Thread(runnable,"RunnableTest").start();
}
}
複製代碼
Callable
接口/** * @author: chenmingyu * @date: 2019/4/8 15:23 * @description: 實現Callable接口 */
public class CallableTest implements Callable<Integer> {
@Override
public Integer call() throws Exception {
IntStream.range(0,10).forEach(i->{
System.out.println(Thread.currentThread().getName()+":"+i);
});
return -1;
}
public static void main(String[] args) throws Exception {
Callable callable = new CallableTest();
FutureTask futureTask = new FutureTask(callable);
new Thread(futureTask,"future").start();
System.out.println("result:"+futureTask.get());
}
}
複製代碼
Thread
提供的過時方法能夠實現對線程進行暫停suspend()
,恢復resume()
,中止stop()
的操做spa
例:建立一個線程,run()
中循環輸出當前時間,在main()
方法中對新建線程進行暫停,恢復,中止的操做操作系統
/** * @author: chenmingyu * @date: 2019/4/8 15:51 * @description: 線程的暫停,恢復,中止 */
public class OperationThread implements Runnable{
@Override
public void run() {
while (true){
try {
TimeUnit.SECONDS.sleep(1L);
System.out.println(Thread.currentThread().getName()+"運行中:"+LocalTime.now());
}catch (InterruptedException e){
System.err.println(e.getMessage());
}
}
}
public static void main(String[] args) throws Exception{
Runnable runnable = new OperationThread();
Thread thread = new Thread(runnable,"operationThread");
/** * 啓動,輸出當前時間 */
thread.start();
TimeUnit.SECONDS.sleep(3L);
/** * 線程暫停,不在輸出當前時間 */
System.out.println("此處暫停:"+LocalTime.now());
thread.suspend();
TimeUnit.SECONDS.sleep(3L);
/** * 線程恢復,繼續輸出當前時間 */
System.out.println("此處恢復:"+LocalTime.now());
thread.resume();
TimeUnit.SECONDS.sleep(3L);
/** * 線程中止,不在輸出當前時間 */
thread.stop();
System.out.println("此處中止:"+LocalTime.now());
TimeUnit.SECONDS.sleep(3L);
}
}
複製代碼
輸出
由於是過時方法,因此不推薦使用,使用suspend()
方法後,線程不會釋放已經佔有的資源,就進入睡眠狀態,容易引起死鎖問題,而使用stop()
方法終結一個線程是不會保證線程的資源正常釋放的,可能會致使程序異常
線程安全的暫停,恢復操做可使用等待/通知機制代替
安全的線程暫停,恢復(等待/通知機制)
相關方法:
方法名 | 描述 |
---|---|
notify() | 通知一個在對象上等待的線程,使其重wait()方法中返回,前提是該線程得到了對象的鎖 |
notifyAll() | 通知全部等待在該對象上的線程 |
wait() | 調用該方法線程進入等待狀態,只有等待另外線程的通知或被中斷纔會返回,調用該方法會釋放對象的鎖 |
wait(long) | 超時等待一段時間(毫秒),若是超過期間就返回 |
wait(long,int) | 對於超時時間耕細粒度的控制,能夠達到納秒 |
例:建立一個名爲waitThread
的線程,在run()
方法,使用中使用synchronized
進行加鎖,以變量flag
爲條件進行while
循環,在循環中調用LOCK.wait()
方法,此時會釋放對象鎖,由main()
方法得到鎖,調用LOCK.notify()
方法通知LOCK
對象上等待的waitThread
線程,將其置爲阻塞狀態,並將變量flag
置爲true
,當waitThread
線程再次獲取對象鎖以後繼續執行餘下代碼
/** * @author: chenmingyu * @date: 2019/4/8 20:00 * @description: wait/notify */
public class WaitNotifyTest {
private static Object LOCK = new Object();
private static Boolean FLAG = Boolean.TRUE;
public static void main(String[] args) throws InterruptedException{
Runnable r = new WaitThread();
new Thread(r,"waitThread").start();
TimeUnit.SECONDS.sleep(1L);
synchronized (LOCK){
System.out.println(Thread.currentThread().getName()+"喚醒waitThread線程:"+LocalTime.now());
/** * 線程狀態由等待狀態變爲阻塞狀態 */
LOCK.notify();
/** * 只有當前線程釋放對象鎖,waitThread獲取到LOCK對象的鎖以後纔會從wait()方法中返回 */
TimeUnit.SECONDS.sleep(2L);
FLAG = Boolean.FALSE;
}
}
public static class WaitThread implements Runnable {
@Override
public void run() {
/** * 加鎖 */
synchronized (LOCK){
while (FLAG){
try {
System.out.println(Thread.currentThread().getName()+"運行中:"+LocalTime.now());
/** * 線程狀態變爲等待狀態 */
LOCK.wait();
/** * 再次得到對象鎖以後,纔會執行 */
System.out.println(Thread.currentThread().getName()+"被喚醒:"+LocalTime.now());
}catch (InterruptedException e){
System.err.println(e.getMessage());
}
}
}
System.out.println(Thread.currentThread().getName()+"即將中止:"+LocalTime.now());
}
}
}
複製代碼
輸出
能夠看到在mian
線程調用
LOCK.notify()
方法後,沉睡了2s才釋放對象鎖,
waitThread
線程在得到對象鎖以後執行餘下代碼
線程的安全中止操做是利用線程的中斷標識來實現,線程的中斷屬性表示一個運行中的線程是否被其餘線程進行了中斷操做,其餘線程經過調用該線程的interrupt()
方法對其進行中斷操做,而該線程經過檢查自身是否被中斷來進行響應,當一個線程被中斷可使用Thread.interrupted()
方法對當前線程的中斷標識位進行復位
例:新建一個線程,run
方法中使用Thread.currentThread().isInterrupted()
是否中斷做爲判斷條件,在主線程中使用thread.interrupt()
方法對子線程進行中斷操做,用來達到終止線程的操做,這種方式會讓子線程能夠去清理資源或一些別的操做,而使用stop()
方法則會會直接終止線程
/** * @author: chenmingyu * @date: 2019/4/8 20:47 * @description: 中斷 */
public class InterruptTest {
public static void main(String[] args) throws InterruptedException {
Runnable r = new StopThread();
Thread thread = new Thread(r,"stopThread");
thread.start();
TimeUnit.SECONDS.sleep(1L);
System.out.println(Thread.currentThread().getName()+"對stopThread線程進行中斷:"+LocalTime.now());
thread.interrupt();
}
public static class StopThread implements Runnable {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName()+"運行中:"+LocalTime.now());
}
System.out.println(Thread.currentThread().getName()+"中止:"+LocalTime.now());
}
}
}
複製代碼
Thread.join()
做用是等待該線程終止
好比在主線程中新建一個子線程,調用子線程的join()
方法,那麼在子線程未執行完時,主線程的狀態是阻塞狀態,只有當子線程執行結束,主線程纔會繼續往下執行
方法名 | 做用 |
---|---|
join() | 調用A線程的join() 方法後,那麼當前線程須要等待A線程終止,才能夠繼續執行 |
join(long) | 在join() 方法的基礎上增長了時間限制(毫秒),超出時間後,不管A線程是否執行完,當前線程都進入就緒狀態,從新等待cpu調用 |
join(long,int) | 在join(long) 方法基礎上,時間控制上更加嚴謹,時間細粒度爲納秒(Long毫秒+int納秒) |
例:循環建立子線程,在main
線程中調用子線程的join()
方法,在子線程中輸出了一句日誌
/** * @author: chenmingyu * @date: 2019/4/9 20:53 * @description: thread.join(); */
public class JoinThreadTest {
public static void main(String[] args) throws InterruptedException{
IntStream.range(0, 5).forEach(i -> {
try {
Runnable runnable = new JoinThread();
Thread thread = new Thread(runnable,"joinThread");
thread.start();
thread.join();
System.out.println(Thread.currentThread().getName() + "運行中: " + LocalTime.now());
} catch (InterruptedException e) {
System.err.println(e.getMessage());
}
System.out.println("------- 分隔符 ------- ");
});
}
public static class JoinThread implements Runnable {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1L);
System.out.println(Thread.currentThread().getName() + "運行中: " + LocalTime.now());
} catch (InterruptedException e) {
System.err.println(e.getMessage());
}
}
}
}
複製代碼
輸出
每次循環都是主線程等待子線程終止,在子線程執行完以後主線程纔會繼續執行
thread.join()源碼
調用方線程(調用join方法的線程)執行等待操做,直到被調用的線程(join方法所屬的線程)結束,再被喚醒
public final void join() throws InterruptedException {
join(0);
}
複製代碼
join(0)
方法
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
複製代碼
join()
方法實現等待實際上是調用了wait()
方法,isAlive()
方法的做用是監測子線程是否終止,若是終止或者超過了指定時間,代碼就繼續往下執行,不然就繼續等待,知道條件知足
ThreadLocal
,叫線程變量,是一個以ThreadLocal
對象爲鍵,任意對象爲值的存儲結構,ThreadLocal
類型的變量在每一個線程中是獨立的,在多線程環境下不會相互影響
/** * @author: chenmingyu * @date: 2019/4/10 17:56 * @description: ThreadLocal */
public class ThreadLocalTest {
private static String STATE;
private static ThreadLocal<String> STRING_THREAD_LOCAL = new InheritableThreadLocal<>();
public static void main(String[] args) throws InterruptedException{
STATE = "未重置";
STRING_THREAD_LOCAL.set("未重置");
Thread thread = new Thread(() ->
{
STATE = "已重置";
STRING_THREAD_LOCAL.set("已重置");
System.out.println(Thread.currentThread().getName() + " : 變量已重置");
});
thread.start();
thread.join();
System.out.println(Thread.currentThread().getName() + "STATE : " + STATE);
System.out.println(Thread.currentThread().getName() + "STRING_THREAD_LOCAL : " + STRING_THREAD_LOCAL.get());
}
}
複製代碼
輸出
ThreadLocal<String>
類型的變量STRING_THREAD_LOCAL
未被子線程修改
參考:java併發編程的藝術