線程命名與取得
多線程的運行狀態是不確定的,所以對於多線程操作必須有一個明確標識出線程對象的信息,這個信息往往通過名稱來描述。在Thread類中提供有如下的線程名稱方法:
class MyThread implements Runnable {
@Override
public void run() {}
}
public class TestThread {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread,"線程1");
thread.start();
System.out.println(thread.getName());
Thread thread1 = new Thread(new MyThread());
thread1.start();
thread1.setName("線程2");
System.out.println(thread1.getName());
Thread thread2 = new Thread(new MyThread());
thread2.setName("線程3");
thread2.start();
System.out.println(thread2.getName());
thread2.setName("子線程3");
System.out.println(thread2.getName());
}
}
如何證明mian方法也是一個線程?
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
public class TestThread {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.run();
}
}
Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.java.TestThread
main
Process finished with exit code 0
線程休眠(sleep方法)
線程休眠:指的是讓線程暫緩執行一下,等到了預計時間之後再恢復執行。
public static native void sleep(long millis) throws InterruptedException
線程休眠會交出CPU(變回阻塞狀態),讓CPU去執行其他的任務,睡眠結束後返回就緒狀態。但是有一點要非常注意,sleep方法不會釋放鎖,也就是說如果當前線程持有對某個對象的鎖,則即使調用sleep方法,其他線程也無法訪問這個對象。
休眠時間使用毫秒作爲單位。
class MyThread implements Runnable {
@Override
public void run() {
for(int i = 0;i < 100;i++){
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(i);
}
}
}
public class TestThread {
public static void main(String[] args) {
MyThread myThread = new MyThread();
new Thread(myThread).start();
}
}
線程讓步(yield()方法)
暫停當前正在執行的線程對象,並執行其他線程。
意思就是調用yield方法會讓當前線程交出CPU權限,讓CPU去執行其他的線程。它跟sleep方法類似,同樣不會釋放鎖。但是yield不能控制具體的交出CPU的時間,另外,yield方法只能讓擁有相同優先級的線程有獲取CPU執行時間的機會。
注意,調用yield方法並不會讓線程進入阻塞狀態,而是讓線程重回就緒狀態,它只需要等待重新獲取CPU執行時間,這一點是和sleep方法不一樣的。
class MyThread implements Runnable {
@Override
public void run() {
for(int i = 0;i < 100;i++){
Thread.yield();
System.out.println(Thread.currentThread().getName()+","+i);
}
}
}
public class TestThread {
public static void main(String[] args) {
MyThread myThread = new MyThread();
new Thread(myThread).start();
new Thread(myThread).start();
new Thread(myThread).start();
}
}
join()方法
等待該線程終止。意思就是如果在主線程中調用該方法時就會讓主線程休眠,讓調用該方法的線程run方法先執行完畢之後在開始執行主線程。
join()方法只是對Object提供的wait()做的的一層包裝而已。
join()方法變回阻塞狀態,結束後返回就緒狀態,會釋放鎖。
class MyThread implements Runnable {
@Override
public void run() {
try{
System.out.println("主線程睡眠前的時間");
TestThread.printTime();
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName());
System.out.println("睡眠結束的時間");
TestThread.printTime();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class TestThread {
public static void main(String[] args) {
Thread thread = new Thread(new MyThread(),"子線程");
thread.start();
System.out.println(Thread.currentThread().getName());
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("代碼結束");
}
public static void printTime() {
Date date=new Date();
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String time = dateFormat.format(date);
System.out.println(time);
}
}
Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.java.TestThread
main
主線程睡眠前的時間
2018-11-14 13:07:10
子線程
睡眠結束的時間
2018-11-14 13:07:12
代碼結束
Process finished with exit code 0
5線程停止
多線程中有三種方式可以停止線程。
- 設置標記位,可以是線程正常退出。
class MyThread implements Runnable {
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag) {
System.out.println(Thread.currentThread().getName() + "第" + ++i + "次執行");
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
public class TestThread {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread,"子線程");
thread.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myThread.setFlag(false);
System.out.println("代碼結束");
}
}
Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.java.TestThread
子線程第1次執行
子線程第2次執行
子線程第3次執行
子線程第4次執行
子線程第5次執行
代碼結束
Process finished with exit code 0
- 使用stop方法強制使線程退出,但是該方法不太安全所以已經被廢棄了。
爲什麼說不安全呢?因爲stop會解除由線程獲取的所有鎖定,當在一個線程對象上調用stop()方法時,這個線程對象所運行的線程就會立即停止,假如一個線程正在執行:synchronized void { x = 3; y = 4;}
由於方法是同步的,多個線程訪問時總能保證x,y被同時賦值,而如果一個線程正在執行到x = 3;
時,被調用了 stop()方法,即使在同步塊中, 它也會馬上stop了,這樣就產生了不完整的殘廢數據。
class MyThread implements Runnable {
@Override
public void run() {
int i = 0;
while (true) {
System.out.println(Thread.currentThread().getName() + "第" + ++i + "次執行");
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public class TestThread {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread,"子線程");
thread.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.stop();
System.out.println("代碼結束");
}
}
- 使用Thread類中的一個interrupt() 可以中斷線程。
調用Thread類的interrupt()方法,只是將線程狀態置爲中斷狀態而已,他不會中斷一個正在運行的線程。此方法只是給受阻塞的線程傳遞一箇中斷信號,程序可以根據此信號來判斷是否需要終止。
具體而言,這個方法只會給線程設置一個爲true的中斷標誌(中斷標誌只是一個布爾類型的變量),而設置之後,則根據線程當前的狀態進行不同的後續操作。如果,線程的當前狀態處於非阻塞狀態,那麼僅僅是線程的中斷標誌被修改爲true而已;如果線程的當前狀態處於阻塞狀態,那麼在將中斷標誌設置爲true後,還會有如下三種情況之一的操作:
當線程中使用了sleep、wait、join()導致此線程阻塞,則interrupt()會在線程中拋出InterruptException
,並且將線程的中斷狀態由true置爲false。
如果在中斷時,線程正處於非阻塞狀態,則將中斷標誌修改爲true,而在此基礎上,一旦進入阻塞狀態,則按照阻塞狀態的情況來進行處理。
通過上面的分析,我們可以總結,調用線程類的interrupted方法,其本質只是設置該線程的中斷標誌,將中斷標誌 設置爲true,並根據線程狀態決定是否拋出異常。因此,通過interrupted方法真正實現線程的中斷原理是:開發人員根據中斷標誌的具體值,來決定如何退出線程。
class MyThread implements Runnable {
@Override
public void run() {
int i = 0;
while (true) {
try {
/**
* 這裏阻塞之後,線程被調用了interrupte()方法,
* 清除中斷標誌,就會拋出一個異常
* java.lang.InterruptedException */
Thread.sleep(1000);
boolean bool = Thread.currentThread().isInterrupted();
if(bool){
System.out.println("非阻塞狀態下執該操作...線程狀態:"+bool);
break;
}
System.out.println("第"+(++i)+"次執行,"+bool+",線程名稱爲:"+Thread.currentThread().getName());
} catch (Exception e) {
System.out.println("退出了");
/**
* 這裏退出阻塞狀態,且中斷標誌被系統會自動清除
* 並且重新設置爲false,所以此處bool爲false
*/
boolean bool = Thread.currentThread().isInterrupted();
System.out.println(bool);
//退出run方法,中斷進程
return;
}
}
}
}
public class TestThread {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread,"子線程");
thread.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();
System.out.println("代碼結束");
}
}
Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.java.TestThread
第1次執行,false線程名稱爲:子線程
第2次執行,false線程名稱爲:子線程
第3次執行,false線程名稱爲:子線程
第4次執行,false線程名稱爲:子線程
代碼結束
退出了
false
Process finished with exit code 0
線程優先級
線程的優先級指的是,線程的優先級越高越有可能先執行,但僅僅是有可能而已。
- 設置優先級
public final void SetPriority(int newPriority)
- 取得優先級
public final int getPriority()
對於優先級設置的內容可以通過Thread類的幾個常量來決定
- 高優先級:public final static int MAX_PRIORITY = 10;
- 中等優先級:public final static int NORM_PRIORITY = 5;
- 低優先級:public final static int MIN_PRIORITY = 1;
public class TestThread {
public static void main(String[] args) {
Runnable runnable = () -> System.out.println(Thread.currentThread().getName());
Thread thread1 = new Thread(runnable,"子線程1");
Thread thread2 = new Thread(runnable,"子線程2");
Thread thread3 = new Thread(runnable,"子線程3");
thread1.setPriority(Thread.MIN_PRIORITY);
thread2.setPriority(Thread.MAX_PRIORITY);
thread3.setPriority(Thread.NORM_PRIORITY);
thread1.start();
thread2.start();
thread3.start();
}
}
主方法是一個線程,那麼主線程的優先級是什麼呢?
主方法只是一箇中等優先級,爲5
public class TestThread {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getPriority()); //5
}
}
線程具有繼承性
只是繼承優先級而已。從A線程啓動B線程,則B和A的線程優先級是一樣的
class A implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+","+Thread.currentThread().getPriority());
new Thread(new B(),"線程B").start();
}
}
class B implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+","+Thread.currentThread().getPriority());
}
}
public class TestThread {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+","+Thread.currentThread().getPriority());
Thread thread = new Thread(new A(),"線程A");
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
}
}
Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.java.TestThread
main,5
線程A,10
線程B,10
守護線程
守護線程是一種特殊的線程,它屬於是一種陪伴線程。簡單點說 java 中有兩種線程:用戶線程和守護線程。可以通過isDaemon()
方法來區別它們:如果返回false,則說明該線程是“用戶線程”;否則就是“守護線程”。典型的守護線程就是垃圾回收線程。注意:主線程main是用戶線程。
只要當前JVM進程中存在任何一個非守護線程沒有結束,守護線程就在工作;只有當後一 個非守護線程結束時,守護線程纔會隨着JVM一同停止工作。
Thread類提供setDaemon()將用戶線程設置爲守護線程
class MyThread implements Runnable {
@Override
public void run() {
while(true){
System.out.println(Thread.currentThread().getName()+"是否爲守護線程"+Thread.currentThread().isDaemon());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "中斷線程了");
return;
}
}
}
}
public class TestThread {
public static void main(String[] args) throws InterruptedException{
Thread thread1 = new Thread(new MyThread(),"線程1");
thread1.setDaemon(true);
Thread thread2 = new Thread(new MyThread(),"線程2");
thread1.start();
thread2.start();
Thread.sleep(3000);
thread2.interrupt();
Thread.sleep(5000);
System.out.println("代碼結束");
}
}
Files\Java\jdk1.8.0_181\jre\lib\rt.jar;E:\Java\code\out\production\code" www.bit.java.TestThread
線程1是否爲守護線程true
線程2是否爲守護線程false
線程1是否爲守護線程true
線程2是否爲守護線程false
線程2是否爲守護線程false
線程1是否爲守護線程true
線程2中斷線程了
線程1是否爲守護線程true
線程1是否爲守護線程true
線程1是否爲守護線程true
線程1是否爲守護線程true
線程1是否爲守護線程true
代碼結束
Process finished with exit code 0
從上面的代碼可以看出來,B是用戶線程當它中斷了之後守護線程還沒有結束,是因爲主線程(用戶線程)還沒有結束,所以說明是所有的用戶線程結束之後守護線程纔會結束。