文章目錄
- 一、基本概念
- 1.1 程序
-
- 1.2 線程
- 1.3 單任務
- 1.4 多任務
- 二、實作線程方法
-
- 2.1 繼承Thread類
- 2.2 實作Runnable接口
- 2.3 實作callable接口
- 2.4 start()方法工作過程
- 三、多線程執行同一任務
-
- 3.1 任務
- 3.2 多線程執行
- 3.3 i--操作解析
- 3.4 非線程安全
- 3.5 i-- 與 println()方法會發生異常
- 四、常用方法
-
- 4.1 currentThread()方法
- 4.2 isAlive()方法
- 4.3 sleep()方法
- 4.4 getId()方法
- 4.5 suspend()方法
- 4.6 resume()方法
- 4.6 yield()方法
- 五、停止線程
-
- 5.1 stop()方法
- 5.1.1 stop方法的壞處
-
- 5.1.2 參考
- 5.2 interrupt()方法
- 5.3 判斷線程是否停止
-
- 5.3.1 this.interrupted()
- 5.3.2 this.isInterrupted()
- 5.4 異常法停止線程
- 5.5 在sleep狀态下停止線程
- 5.6 return方法停止線程
- 六、線程的優先級
-
- 6.1 設定線程優先級的作用
- 6.2 使用的方法
- 6.3 設定優先級常用的常量
- 6.4 線程優先級的繼承特性
- 6.5 優先級具有規則性
- 七、線程的分類
-
- 7.1 使用者線程
- 7.2 守護線程
- 7.3 規則
- 7.4 參考
一、基本概念
1.1 程序
程序是作業系統結構的基礎,是一次程式的執行,是一個程式及其資料在處理機上順序執行時所發生的活動,是程式在一個資料集合上運作的過程,他是系統進行資源配置設定和排程的機關。
作業系統中正在運作的exe就是一個程序。
1.2 線程
在程序中獨立運作的子任務。
1.3 單任務
需要排隊執行,也就是同步,是以cpu使用率大幅降低
1.4 多任務
cpu可以在不同任務之間來回切換,這樣一個任務就不必花費很長時間等到上個任務結束再執行,而是通過cpu切換任務,很快就可以執行。系統的運作效率大大提升,這就是多線程技術的優點。他是異步執行任務的。
二、實作線程方法
2.1 繼承Thread類
public class myThread extends Thread{
@Override
public void run() {
System.out.println("你好");
}
}
2.2 實作Runnable接口
Thread類有兩個構造方法傳遞Runnable接口
- Thread(Runnable target)
- Thread(Runnable tartget, String name)
public class myThread implements Runnable{
@Override
public void run() {
System.out.println("你好");
}
}
執行:
public class test {
public static void main(String[] args) {
myThread myThread = new myThread();
Thread thread = new Thread(myThread);
thread.start();
}
}
2.3 實作callable接口
public class myThread implements Callable {
@Override
public Object call() throws Exception {
int j = 0;
for (int i = 0; i < 10; i++) {
j++;
}
return j;
}
}
2.4 start()方法工作過程
start()方法通知"線程規劃器"此線程已經準備就緒,等待調用線程對象的Run()方法,這個過程其實就是讓系統安排一個時間來調用Thread的run()方法,啟動線程,具有異步執行的效果,如果直接調用run()方法,就是同步調用。
三、多線程執行同一任務
3.1 任務
public class myThread implements Runnable {
int i = 100;
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "還剩下的數量" + i --);
}
}
3.2 多線程執行
public class test {
public static void main(String[] args) {
myThread myThread = new myThread();
Thread A = new Thread(myThread);
Thread B = new Thread(myThread);
Thread C = new Thread(myThread);
Thread D = new Thread(myThread);
Thread E = new Thread(myThread);
A.start();
B.start();
C.start();
D.start();
E.start();
}
}
3.3 i–操作解析
i–分三步完成
- 取得原有i值
- 計算i-1
- 指派給i
3.4 非線程安全
多個線程對同一個對象中的同一個執行個體變量進行操作會出現值被更改,值不同步的現象。
舉例:
兩個靜态變量name和password和一個靜态方法。
public class Unsafe {
public static String Username;
public static String Userpassword;
public static synchronized void update(String name, String password){
Username = name;
try {
if(Username.equals("a")){
Thread.sleep(5000);
}
Userpassword = password;
System.out.println("姓名: " + Username + "密碼: " + Userpassword);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
線程A:
public class threadA extends Thread {
@Override
public void run() {
Unsafe.update("a","123");
}
}
線程B:
public class threadB extends Thread {
@Override
public void run() {
Unsafe.update("b","456");
}
}
public class test {
public static void main(String[] args) {
new threadA().start();
new threadB().start();
}
}
主方法:
public class test {
public static void main(String[] args) {
new threadA().start();
new threadB().start();
}
}
結果:
線程不安全:
姓名: a密碼: 456
姓名: a密碼: 123
加上鎖:
public static synchronized void update(String name, String password){
Username = name;
try {
if(Username.equals("a")){
Thread.sleep(5000);
}
Userpassword = password;
System.out.println("姓名: " + Username + "密碼: " + Userpassword);
} catch (Exception e) {
e.printStackTrace();
}
}
結果:
姓名: b密碼: 456
姓名: a密碼: 123
3.5 i-- 與 println()方法會發生異常
- 雖然在println()方法在内部是同步的,但i–操作确是在進入println之前發生的。
四、常用方法
4.1 currentThread()方法
- 傳回代碼段正在被哪個線程調用的資訊。
4.2 isAlive()方法
- 判斷目前線程是否處于活動狀态,活動狀态就是線程已經啟動且尚未終止,線程處于正在運作或者準備開始運作的狀态。
4.3 sleep()方法
- 讓目前正在執行的線程休眠一段時間,這個正在執行的線程是this.currentThread()傳回的線程。
- 主要是為了暫停目前線程,把cpu片段讓出給其他線程,減緩目前線程的執行。
- 參考:Thread之sleep方法
4.4 getId()方法
- 取得線程的唯一辨別
4.5 suspend()方法
- 暫停線程
- 使用不當,容易造成公共的同步對象的獨占,使其他線程無法通路公共同步對象
- 使用不當,造成因為線程暫停而資料不同步的狀況。
4.6 resume()方法
- 恢複暫停的線程
- 使用不當,容易造成公共的同步對象的獨占,使其他線程無法通路公共同步對象
4.6 yield()方法
-
放棄目前的cpu資源,将他讓給其他的任務去占用cpu執行時間,但放棄的時間不确定。
Thread類:
public class threadA extends Thread {
@Override
public void run() {
int j = 0;
long l = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
// Thread.yield();
System.out.println(i);
}
long l1 = System.currentTimeMillis();
System.out.println(l1 - l + "毫秒");
}
}
測試:
public class test {
public static void main(String[] args) {
threadA threadA = new threadA();
threadA.start();
}
}
五、停止線程
5.1 stop()方法
- 不安全
- 已經被棄用廢棄
線程A:
public class threadA extends Thread {
@Override
public void run() {
for (int i = 0; i < 1000000; i++) {
System.out.println(i);
}
}
}
測試:
public class test {
public static void main(String[] args) {
try {
threadA threadA = new threadA();
threadA.start();
threadA.sleep(2000);
threadA.stop();
System.out.println(threadA.isInterrupted());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
結論:stop()方法直接使線程暴力終止。
-
會抛出java.lang.ThreadDeath異常,通常情況下,不需要顯示的捕捉。
ThreadA:
public class threadA extends Thread {
@Override
public void run() {
try{this.stop();
}catch (ThreadDeath e){
System.out.println("線程被暴力終止");
}
}
}
測試:
public class test {
public static void main(String[] args) {
try {
threadA threadA = new threadA();
threadA.start();
threadA.sleep(2000);
threadA.stop();
System.out.println(threadA.isInterrupted());
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
結果:
線程被暴力終止
false
5.1.1 stop方法的壞處
stop()方法強制停止線程會讓一些清理性工作得不到完成,另外一個情況就是對鎖定的對象進行了解鎖,導緻資料得不到同步處理。比如說同步的方法得不到同步的效果。
- 使下面線程的方法還未執行完,就強行停止線程,就會産生安全方面的問題。
public class stopThread extends Thread {
public static boolean flag = false;
@Override
public void run() {
for (int i = 0; i < 4; i++) {
try {
Thread.sleep(1000);
System.out.println("輸出:" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("正常結束");
flag = true;
}
public static void main(String[] args) {
try {
stopThread stopThread = new stopThread();
Thread thread = new Thread(stopThread);
thread.start();
thread.join(2000);
thread.stop();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "已經停止了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
輸出:
輸出:0
main已經停止了
5.1.2 參考
為什麼Thread.stop不推薦使用? 如何正确停止一個線程
5.2 interrupt()方法
- 中斷線程
- 并不是停止線程
5.3 判斷線程是否停止
5.3.1 this.interrupted()
- 測試目前線程是否已經發生中斷
-
具有清除中斷狀态的功能跟
測試:
public class test {
public static void main(String[] args) {
try {
threadA threadA = new threadA();
threadA.start();
threadA.sleep(1000);
threadA.interrupt();
System.out.println(threadA.interrupted());
System.out.println(threadA.interrupted());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
輸出為false,說明目前運作的線程是main
測試2:
public class test {
public static void main(String[] args) {
try {
threadA threadA = new threadA();
threadA.start();
threadA.sleep(1000);
Thread.currentThread().interrupt();
System.out.println(threadA.interrupted());
System.out.println(threadA.interrupted());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
使main線程中斷,第一次輸出為true,說明main線程中斷,第二次為false,是因為interrupted()方法更改了main線程中斷的狀态。
5.3.2 this.isInterrupted()
- 測試線程是否已經中斷
- 不具有清除中斷狀态的功能
- 測試資料要足夠多,否則還沒開始中斷已經結束了。
線程A:
public class threadA extends Thread {
@Override
public void run() {
for (int i = 0; i < 200000; i++) {
if(this.isInterrupted()){
System.out.println("線程中斷,我要退出了");
break;
}
System.out.println(i);
}
}
}
測試類:
public class test {
public static void main(String[] args) {
try {
threadA threadA = new threadA();
threadA.start();
threadA.sleep(1000);
threadA.interrupt();
System.out.println(threadA.isInterrupted());
System.out.println(threadA.isInterrupted());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
結果:
...
90780
90781
90782
true
true
線程中斷,我要退出了
5.4 異常法停止線程
線程A:
public class threadA extends Thread {
@Override
public void run() {
for (int i = 0; i < 2000000; i++) {
if(this.isInterrupted()){
System.out.println("線程中斷,我要退出了");
break;
}
System.out.println(i);
}
System.out.println("線程雖然中斷了,但是我還可以輸出");
}
}
測試:
public class test {
public static void main(String[] args) {
try {
threadA threadA = new threadA();
threadA.sleep(10000);
threadA.start();
threadA.interrupt();
System.out.println(threadA.isInterrupted());
System.out.println(threadA.isInterrupted());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
如上,線程被中斷,但是還可以執行for語句外面的語句。
使用異常方式退出,不會執行下面的語句。
public class threadA extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < 2000000; i++) {
if(this.isInterrupted()){
System.out.println("線程中斷,我要退出了");
throw new InterruptedException();
}
System.out.println(i);
}
System.out.println("線程雖然中斷了,但是我還可以輸出");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
結果:
true
true
線程中斷,我要退出了
java.lang.InterruptedException
at threadA.run(threadA.java:11)
5.5 在sleep狀态下停止線程
線程A:
public class threadA extends Thread {
@Override
public void run() {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
測試:
public class test {
public static void main(String[] args) {
try {
threadA threadA = new threadA();
threadA.start();
threadA.sleep(4000);
threadA.interrupt();
System.out.println(threadA.isInterrupted());
} catch (InterruptedException e) {
System.out.println("線程在沉睡時被中斷");
e.printStackTrace();
}
}
}
結果:
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at threadA.run(threadA.java:8)
false
線程在沉睡時被中斷
說明:在sleep狀态下停止某一線程,會進入線程的catch語句中,并會在test類中,清除中斷的狀态。
5.6 return方法停止線程
ThreadA:
public class threadA extends Thread {
@Override
public void run() {
for (int i = 0; i < 1000000; i++) {
System.out.println(i);
if (this.isInterrupted()){
System.out.println("線程中斷");
return;
}
}
System.out.println("for外面的不執行可了");
}
}
測試類:
public class test {
public static void main(String[] args) {
try {
threadA threadA = new threadA();
threadA.start();
threadA.sleep(2000);
threadA.stop();
System.out.println(threadA.isInterrupted());
}catch (InterruptedException e) {
e.printStackTrace();
}
}
結果:
184861
true
六、線程的優先級
6.1 設定線程優先級的作用
設定線程優先級有助于幫"線程規劃器"确定在下一次選擇哪一個線程優先執行。
6.2 使用的方法
-
setPriority()
設定線程優先級
-
getPriority
獲得線程優先級
6.3 設定優先級常用的常量
- MIN_PRIORITY = 1
- NORM_PRIORITY = 5
- MAX_PRIORITY = 10
6.4 線程優先級的繼承特性
-
線程的優先級具有繼承性,如A線程啟動B線程,則B線程的優先級與A線程的優先級一緻
線程A:
public class threadA extends Thread {
@Override
public void run() {
threadB threadB = new threadB();
threadB.start();
System.out.println("線程A的優先級" + this.getPriority());
}
}
線程B:
public class threadB extends Thread {
@Override
public void run() {
System.out.println("線程B");
System.out.println("線程B的優先級" + this.getPriority());
}
}
測試類:
public class test {
public static void main(String[] args) {
threadA threadA = new threadA();
//threadA.setPriority(6);
threadA.start();
}
}
結果:
線程A的優先級5
線程B
線程B的優先級5
測試類:
public class test {
public static void main(String[] args) {
threadA threadA = new threadA();
threadA.setPriority(6);
threadA.start();
}
}
結果:
線程A的優先級6
線程B
線程B的優先級6
6.5 優先級具有規則性
- 線程的優先級與代碼的執順序無關,但是CPU盡量将執行資源配置設定給優先級較高的線程。
七、線程的分類
7.1 使用者線程
7.2 守護線程
7.3 規則
- 當程序中不存在非守護線程時,則守護線程自動銷毀。典型的守護線程就是垃圾回收線程。
-
setDaemon(true)
設定線程為守護線程
- 守護線程要線上程開啟前設定
7.4 參考
Thread的setDaemon(true)方法的作用