基於Java回想之多線程詳解。本站提示廣大學習愛好者:(基於Java回想之多線程詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是基於Java回想之多線程詳解正文
線程是操作體系運轉的根本單元,它被封裝在過程中,一個過程可以包括多個線程。即便我們不手動發明線程,過程也會有一個默許的線程在運轉。
關於JVM來講,當我們編寫一個單線程的法式去運轉時,JVM中也是有至多兩個線程在運轉,一個是我們創立的法式,一個是渣滓收受接管。
線程根本信息
我們可以經由過程Thread.currentThread()辦法獲得以後線程的一些信息,並對其停止修正。
我們來看以下代碼:
檢查並修正以後線程的屬性
String name = Thread.currentThread().getName();
int priority = Thread.currentThread().getPriority();
String groupName = Thread.currentThread().getThreadGroup().getName();
boolean isDaemon = Thread.currentThread().isDaemon();
System.out.println("Thread Name:" + name);
System.out.println("Priority:" + priority);
System.out.println("Group Name:" + groupName);
System.out.println("IsDaemon:" + isDaemon);
Thread.currentThread().setName("Test");
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
name = Thread.currentThread().getName();
priority = Thread.currentThread().getPriority();
groupName = Thread.currentThread().getThreadGroup().getName();
isDaemon = Thread.currentThread().isDaemon();
System.out.println("Thread Name:" + name);
System.out.println("Priority:" + priority);
個中列出的屬性解釋以下:
GroupName,每一個線程都邑默許在一個線程組裡,我們也能夠顯式的創立線程組,一個線程組中也能夠包括子線程組,如許線程和線程組,就組成了一個樹狀構造。
Name,每一個線程都邑有一個名字,假如不顯式指定,那末名字的規矩是“Thread-xxx”。
Priority,每一個線程都邑有本身的優先級,JVM對優先級的處置方法是“搶占式”的。當JVM發明優先級高的線程時,立時運轉該線程;關於多個優先級相等的線程,JVM對其停止輪詢處置。Java的線程優先級從1到10,默許是5,Thread類界說了2個常量:MIN_PRIORITY和MAX_PRIORITY來表現最高和最低優先級。
我們可以看上面的代碼,它界說了兩個分歧優先級的線程:
線程優先級示例
public static void priorityTest()
{
Thread thread1 = new Thread("low")
{
public void run()
{
for (int i = 0; i < 5; i++)
{
System.out.println("Thread 1 is running.");
}
}
};
Thread thread2 = new Thread("high")
{
public void run()
{
for (int i = 0; i < 5; i++)
{
System.out.println("Thread 2 is running.");
}
}
};
thread1.setPriority(Thread.MIN_PRIORITY);
thread2.setPriority(Thread.MAX_PRIORITY);
thread1.start();
thread2.start();
}
從運轉成果可以看出,是高優先級線程運轉完成後,低優先級線程才運轉。
isDaemon,這個屬性用來掌握父子線程的關系,假如設置為true,當父線程停止後,其下一切子線程也停止,反之,子線程的性命周期不受父線程影響。
我們來看上面的例子:
IsDaemon 示例
public static void daemonTest()
{
Thread thread1 = new Thread("daemon")
{
public void run()
{
Thread subThread = new Thread("sub")
{
public void run()
{
for(int i = 0; i < 100; i++)
{
System.out.println("Sub Thread Running " + i);
}
}
};
subThread.setDaemon(true);
subThread.start();
System.out.println("Main Thread end.");
}
};
thread1.start();
}
下面代碼的運轉成果,在和刪除subThread.setDaemon(true);後比較,可以發明後者運轉進程中子線程會完成履行後再停止,而前者中,子線程很快就停止了。
若何創立線程
下面的內容,都是演示默許線程中的一些信息,那末應當若何創立線程呢?在Java中,我們有3種方法可以用來創立線程。
Java中的線程要末繼續Thread類,要末完成Runnable接口,我們逐個道來。
應用外部類來創立線程
我們可使用外部類的方法來創立線程,進程是聲明一個Thread類型的變量,偏重寫run辦法。示例代碼以下:
應用外部類創立線程
public static void createThreadByNestClass()
{
Thread thread = new Thread()
{
public void run()
{
for (int i =0; i < 5; i++)
{
System.out.println("Thread " + Thread.currentThread().getName() + " is running.");
}
System.out.println("Thread " + Thread.currentThread().getName() + " is finished.");
}
};
thread.start();
}
繼續Thread以創立線程
我們可以從Thread中派生一個類,重寫其run辦法,這類方法和下面類似。示例代碼以下:
派生Thread類以創立線程
class MyThread extends Thread
{
public void run()
{
for (int i =0; i < 5; i++)
{
System.out.println("Thread " + Thread.currentThread().getName() + " is running.");
}
System.out.println("Thread " + Thread.currentThread().getName() + " is finished.");
}
}
public static void createThreadBySubClass()
{
MyThread thread = new MyThread();
thread.start();
}
完成Runnable接口以創立線程
我們可以界說一個類,使其完成Runnable接口,然後將該類的實例作為構建Thread變量結構函數的參數。示例代碼以下:
完成Runnable接口以創立線程
class MyRunnable implements Runnable
{
public void run()
{
for (int i =0; i < 5; i++)
{
System.out.println("Thread " + Thread.currentThread().getName() + " is running.");
}
System.out.println("Thread " + Thread.currentThread().getName() + " is finished.");
}
}
public static void createThreadByRunnable()
{
MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
上述3種方法都可以創立線程,並且從示例代碼上看,線程履行的功效是一樣的,那末這三種創立方法有甚麼分歧呢?
這觸及到Java中多線程的運轉形式,關於Java來講,多線程在運轉時,有“多對象多線程”和“單對象多線程”的差別:
多對象多線程,法式在運轉進程中創立多個線程對象,每一個對象上運轉一個線程。
單對象多線程,法式在運轉進程中創立一個線程對象,在其上運轉多個線程。
明顯,從線程同步和調劑的角度來看,多對象多線程要簡略一些。上述3種線程創立方法,前兩種都屬於“多對象多線程”,第三種既可使用“多對象多線程”,也能夠應用“單對象單線程”。
我們來看上面的示例代碼,外面會用到Object.notify辦法,這個辦法會叫醒對象上的一個線程;而Object.notifyAll辦法,則會叫醒對象上的一切線程。
notify示例
public class NotifySample {
public static void main(String[] args) throws InterruptedException
{
notifyTest();
notifyTest2();
notifyTest3();
}
private static void notifyTest() throws InterruptedException
{
MyThread[] arrThreads = new MyThread[3];
for (int i = 0; i < arrThreads.length; i++)
{
arrThreads[i] = new MyThread();
arrThreads[i].id = i;
arrThreads[i].setDaemon(true);
arrThreads[i].start();
}
Thread.sleep(500);
for (int i = 0; i < arrThreads.length; i++)
{
synchronized(arrThreads[i])
{
arrThreads[i].notify();
}
}
}
private static void notifyTest2() throws InterruptedException
{
MyRunner[] arrMyRunners = new MyRunner[3];
Thread[] arrThreads = new Thread[3];
for (int i = 0; i < arrThreads.length; i++)
{
arrMyRunners[i] = new MyRunner();
arrMyRunners[i].id = i;
arrThreads[i] = new Thread(arrMyRunners[i]);
arrThreads[i].setDaemon(true);
arrThreads[i].start();
}
Thread.sleep(500);
for (int i = 0; i < arrMyRunners.length; i++)
{
synchronized(arrMyRunners[i])
{
arrMyRunners[i].notify();
}
}
}
private static void notifyTest3() throws InterruptedException
{
MyRunner runner = new MyRunner();
Thread[] arrThreads = new Thread[3];
for (int i = 0; i < arrThreads.length; i++)
{
arrThreads[i] = new Thread(runner);
arrThreads[i].setDaemon(true);
arrThreads[i].start();
}
Thread.sleep(500);
synchronized(runner)
{
runner.notifyAll();
}
}
}
class MyThread extends Thread
{
public int id = 0;
public void run()
{
System.out.println("第" + id + "個線程預備休眠5分鐘。");
try
{
synchronized(this)
{
this.wait(5*60*1000);
}
}
catch(InterruptedException ex)
{
ex.printStackTrace();
}
System.out.println("第" + id + "個線程被叫醒。");
}
}
class MyRunner implements Runnable
{
public int id = 0;
public void run()
{
System.out.println("第" + id + "個線程預備休眠5分鐘。");
try
{
synchronized(this)
{
this.wait(5*60*1000);
}
}
catch(InterruptedException ex)
{
ex.printStackTrace();
}
System.out.println("第" + id + "個線程被叫醒。");
}
}
示例代碼中,notifyTest()和notifyTest2()是“多對象多線程”,雖然notifyTest2()中的線程完成了Runnable接口,然則它外面界說Thread數組時,每一個元素都應用了一個新的Runnable實例。notifyTest3()屬於“單對象多線程”,由於我們只界說了一個Runnable實例,一切的線程都邑應用這個實例。
notifyAll辦法實用於“單對象多線程”的情形,由於notify辦法只會隨機叫醒對象上的一個線程。
線程的狀況切換
關於線程來說,從我們創立它一向到線程運轉停止,在這個進程中,線程的狀況能夠是如許的:
創立:曾經有Thread實例了, 然則CPU還無為其分派資本和時光片。
停當:線程曾經取得了運轉所需的一切資本,只等CPU停止時光調劑。
運轉:線程位於以後CPU時光片中,正在履行相干邏輯。
休眠:普通是挪用Thread.sleep後的狀況,這時候線程仍然持有運轉所需的各類資本,然則不會被CPU調劑。
掛起:普通是挪用Thread.suspend後的狀況,和休眠相似,CPU不會調劑該線程,分歧的是,這類狀況下,線程會釋放一切資本。
逝世亡:線程運轉停止或許挪用了Thread.stop辦法。
上面我們來演示若何停止線程狀況切換,起首我們會用到上面辦法:
Thread()或許Thread(Runnable):結構線程。
Thread.start:啟動線程。
Thread.sleep:將線程切換至休眠狀況。
Thread.interrupt:中止線程的履行。
Thread.join:期待某線程停止。
Thread.yield:褫奪線程在CPU上的履行時光片,期待下一次調劑。
Object.wait:將Object上一切線程鎖定,直到notify辦法才持續運轉。
Object.notify:隨機叫醒Object上的1個線程。
Object.notifyAll:叫醒Object上的一切線程。
上面,就是演示時光啦!!!
線程期待與叫醒
這裡重要應用Object.wait和Object.notify辦法,請拜見下面的notify實例。須要留意的是,wait和notify都必需針對統一個對象,當我們應用完成Runnable接口的方法來創立線程時,應當是在Runnable對象而非Thread對象上應用這兩個辦法。
線程的休眠與叫醒
Thread.sleep實例
public class SleepSample {
public static void main(String[] args) throws InterruptedException
{
sleepTest();
}
private static void sleepTest() throws InterruptedException
{
Thread thread = new Thread()
{
public void run()
{
System.out.println("線程 " + Thread.currentThread().getName() + "將要休眠5分鐘。");
try
{
Thread.sleep(5*60*1000);
}
catch(InterruptedException ex)
{
System.out.println("線程 " + Thread.currentThread().getName() + "休眠被中止。");
}
System.out.println("線程 " + Thread.currentThread().getName() + "休眠停止。");
}
};
thread.setDaemon(true);
thread.start();
Thread.sleep(500);
thread.interrupt();
}
}
線程在休眠進程中,我們可使用Thread.interrupt將其叫醒,這時候線程會拋出InterruptedException。
線程的終止
固然有Thread.stop辦法,但該辦法是不被推舉應用的,我們可以應用下面休眠與叫醒的機制,讓線程在處置IterruptedException時,停止線程。
Thread.interrupt示例
public class StopThreadSample {
public static void main(String[] args) throws InterruptedException
{
stopTest();
}
private static void stopTest() throws InterruptedException
{
Thread thread = new Thread()
{
public void run()
{
System.out.println("線程運轉中。");
try
{
Thread.sleep(1*60*1000);
}
catch(InterruptedException ex)
{
System.out.println("線程中止,停止線程");
return;
}
System.out.println("線程正常停止。");
}
};
thread.start();
Thread.sleep(500);
thread.interrupt();
}
}
線程的同步期待
當我們在主線程中創立了10個子線程,然後我們希冀10個子線程全體停止後,主線程在履行接上去的邏輯,這時候,就該Thread.join退場了。
Thread.join示例
public class JoinSample {
public static void main(String[] args) throws InterruptedException
{
joinTest();
}
private static void joinTest() throws InterruptedException
{
Thread thread = new Thread()
{
public void run()
{
try
{
for(int i = 0; i < 5; i++)
{
System.out.println("線程在運轉。");
Thread.sleep(1000);
}
}
catch(InterruptedException ex)
{
ex.printStackTrace();
}
}
};
thread.setDaemon(true);
thread.start();
Thread.sleep(1000);
thread.join();
System.out.println("主線程正常停止。");
}
}
我們可以試著將thread.join();正文或許刪除,再次運轉法式,便可以發明分歧了。
線程間通訊
我們曉得,一個過程上面的一切線程是同享內存空間的,那末我們若何在分歧的線程之間傳遞新聞呢?在回想 Java I/O時,我們談到了PipedStream和PipedReader,這裡,就是它們施展感化的處所了。
上面的兩個示例,功效完整一樣,分歧的是一個應用Stream,一個應用Reader/Writer。
PipeInputStream/PipedOutpueStream 示例
public static void communicationTest() throws IOException, InterruptedException
{
final PipedOutputStream pos = new PipedOutputStream();
final PipedInputStream pis = new PipedInputStream(pos);
Thread thread1 = new Thread()
{
public void run()
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try
{
while(true)
{
String message = br.readLine();
pos.write(message.getBytes());
if (message.equals("end")) break;
}
br.close();
pos.close();
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
};
Thread thread2 = new Thread()
{
public void run()
{
byte[] buffer = new byte[1024];
int bytesRead = 0;
try
{
while((bytesRead = pis.read(buffer, 0, buffer.length)) != -1)
{
System.out.println(new String(buffer));
if (new String(buffer).equals("end")) break;
buffer = null;
buffer = new byte[1024];
}
pis.close();
buffer = null;
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
};
thread1.setDaemon(true);
thread2.setDaemon(true);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
PipedReader/PipedWriter 示例
private static void communicationTest2() throws InterruptedException, IOException
{
final PipedWriter pw = new PipedWriter();
final PipedReader pr = new PipedReader(pw);
final BufferedWriter bw = new BufferedWriter(pw);
final BufferedReader br = new BufferedReader(pr);
Thread thread1 = new Thread()
{
public void run()
{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
try
{
while(true)
{
String message = br.readLine();
bw.write(message);
bw.newLine();
bw.flush();
if (message.equals("end")) break;
}
br.close();
pw.close();
bw.close();
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
};
Thread thread2 = new Thread()
{
public void run()
{
String line = null;
try
{
while((line = br.readLine()) != null)
{
System.out.println(line);
if (line.equals("end")) break;
}
br.close();
pr.close();
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
};
thread1.setDaemon(true);
thread2.setDaemon(true);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}