前言
以前大家寫的都是單線程的程序,全是在main函數中調用方法,可以清楚的看到它的效率是特別低的,就像python中使用單線程取爬一個網站,可以說能讓你等的吐血,因爲數據量實在太大了,今天我們就來看看java的併發編程多線程的學習
創建線程
創建一個線程可以有多種方法,比如繼承Thread類,實現Runnable接口......下面我們來詳細的看看創建的方法
繼承Thread
爲什麼繼承
Thread
可以直接調用start()
方法啓動線程呢,因爲start()
本身就是Thread的方法,也就是繼承了Thread的start()方法,因此這個類的對象可以調用start()啓動線程
//繼承Threadpublic class MyThread extends Thread { public void run() { for (int i = 0; i < 10; i++) { System.out.println(this.getName()+"正在跑"); } } }public class Test{ public static void main(String[] args) { Mythread t1=new MyThread(); //創建對象 t1.start(); //啓動線程 } }
注意: 繼承
Thread
類的創建方法一個對象只能創建一個線程,並不能多個線程共用一個對象,只能一個線程對應一個對象,因此我們來看看實現Runnable
接口的類來實現多個線程共享同一個對象
實現Runnable接口
//實現Runnable接口public class Demo implements Runnable { @Override public void run() { for(int i=0;i<10;i++) { System.out.println(Thread.currentThread().getName()+"正在跑"); } } }//測試類public class Test{ public static void main(String[] args) { Demo d=new Demo(); //創建對象 Thread thread1=new Thread(d); //爲對象創建一個線程 Thread thread2=new Thread(d); //創建另外一個線程 //同時啓動兩個線程 thread1.start(); thread2.start(); } }
從上面可以清楚的看到實現
Runnable
接口的類一個對象可以供多個線程共享,並不像繼承Thread類只爲一個線程使用
簡便的創建方法
直接在
main
方法中創建,如果創建的普通類的對象在外面,那麼必須是final修飾,可以實現多個線程同時共享一個對象,這個和實現Runnable
接口一樣,這時候就要控制同步條件了,如果在run方法中定義對象,那麼,就是一個線程對應一個對象,這個就和繼承Thread類一樣的效果。所以可以根據條件自由選擇
//普通的一個類public class Simple { public void display() { for(int i=0;i<10;i++) { System.out.println(Thread.currentThread().getName()+"正在跑"); } } }//線程測試類public class Test { public static void main(String[] args) { //如果在外面必須使用final,當然也可以直寫在run方法中,不過寫在外面可以實現多個線程共享一個對象 //寫在run方法中當前對象只能爲一個線程使用,和繼承Thread類一樣的效果 final Simple simple=new Simple(); //下面創建使用同一個對象創建同兩個線程,實現多個線程共享一個對象,和實現Runnable接口一樣的效果 Thread t1=new Thread(){ public void run() { simple.display(); }; }; Thread t2=new Thread(){ public void run() { simple.display(); }; }; //啓動這兩個線程 t1.start(); t2.start(); }}
常用的方法
static void sleep(long mils)
使正在運行的線程休眠mils毫秒,但是這裏需要注意的是如果線程加了鎖,那麼使線程休眠並不會釋放鎖
String getName()
得到線程的名稱,上面的程序中已經使用了這個方法
void setName(String name)
設置正在運行的線程的名字爲name
start()
啓動線程,線程的創建並不意味着線程的啓動,只有調用start()方法線程纔是真正的開始運行
long getId()
返回線程的標識符
run()
線程執行的代碼都放在run()方法中,在run方法中的調用是有序的,都是按照程序運行的順序開始執行
使用
下面使用上面的方法創建一個實例
//線程的類,繼承Threadpublic class MyThread1 extends Thread { public void run() { // 重載run方法,並且在其中寫線程執行的代碼塊 for (int i = 0; i < 10; i++) { // 獲取線程的id和name System.out.println("Thread-Name: " + this.getName() + " Thread-id: " + this.getId()); try { this.sleep(1000); // 線程休眠1秒 } catch (InterruptedException e) { e.printStackTrace(); } } } }//線程測試的類public class Test { public static void main(String[] args) { MyThread1 t1 = new MyThread1(); // 創建線程 t1.setName("第一個線程"); // 設置線程的名字 MyThread1 t2 = new MyThread1(); t2.setName("第二個線程"); t1.start(); // 啓動線程,開始運行 t2.start(); } }
void join()
等待該線程終止才能運行其他的線程
void join(long mils)
等待該線程的時間爲mils毫秒,一旦過了這個時間其他線程正常執行
使用
//線程類public class MyThread1 extends Thread { public void run() { // 重載run方法,並且在其中寫線程執行的代碼塊 for (int i = 0; i < 10; i++) { // 獲取線程的id和name System.out.println("Thread-Name: " + this.getName() + " Thread-id: " + this.getId()); try { this.sleep(1000); // 線程休眠1秒 } catch (InterruptedException e) { e.printStackTrace(); } } } }//測試類public class Test { public static void main(String[] args) { MyThread1 t1 = new MyThread1(); // 創建線程 t1.setName("第一個線程"); // 設置線程的名字 t1.start(); // 啓動線程,開始運行 try { t1.join(); //阻塞其他線程,只有當這個線程運行完之後纔開始運行其他的線程 } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 10; i++) { System.out.println("主線程正在運行"); } } }//輸出結果/*Thread-Name: 第一個線程 Thread-id: 9Thread-Name: 第一個線程 Thread-id: 9Thread-Name: 第一個線程 Thread-id: 9Thread-Name: 第一個線程 Thread-id: 9Thread-Name: 第一個線程 Thread-id: 9Thread-Name: 第一個線程 Thread-id: 9Thread-Name: 第一個線程 Thread-id: 9Thread-Name: 第一個線程 Thread-id: 9Thread-Name: 第一個線程 Thread-id: 9Thread-Name: 第一個線程 Thread-id: 9主線程正在運行主線程正在運行主線程正在運行主線程正在運行主線程正在運行主線程正在運行主線程正在運行主線程正在運行主線程正在運行主線程正在運行 */
getPriority()
得到當前線程優先級
setPriority(int num)
更改線程的優先級(0-10)默認的是5,優先級越高獲得cpu資源的機率就會越高
使用
//線程類public class MyThread1 extends Thread { public void run() { // 重載run方法,並且在其中寫線程執行的代碼塊 for (int i = 0; i < 10; i++) { // 獲取線程的id和name System.out.println("Thread-Name: " + this.getName() + " Thread-id: " + this.getId()); try { this.sleep(1000); // 線程休眠1秒 } catch (InterruptedException e) { e.printStackTrace(); } } } }//測試類public class Test { public static void main(String[] args) { MyThread1 t1 = new MyThread1(); // 創建線程 t1.setName("第一個線程"); // 設置線程的名字 MyThread1 t2 = new MyThread1(); t2.setName("第二個線程"); t2.setPriority(8); //設置第二個線程的優先級爲8,第一個線程的優先級爲5(是默認的) t1.start(); t2.start(); } }/* * 從上面的運行結果可以看出大部分的第二個線程都是在第一個線程之前開始執行的,也就是說優先級越高獲得cpu執行的機率就越大 * /
setDaemon(boolean)
是否設置爲守護線程,如果設置爲守護線程,那麼主線程銷燬守護線程也會隨之銷燬
isDaemon()
判斷是否爲守護線程
使用
//測試類public class MyThread1 extends Thread { public void run() { // 重載run方法,並且在其中寫線程執行的代碼塊 for (int i = 0; i < 10; i++) { // 獲取線程的id和name System.out.println("Thread-Name: " + this.getName() + " Thread-id: " + this.getId()); try { Thread.sleep(1000); //休眠一秒,方便主線程運行結束 } catch (InterruptedException e) { e.printStackTrace(); } } } }public class Test { public static void main(String[] args) { MyThread1 t1 = new MyThread1(); // 創建線程 t1.setName("第一個線程"); // 設置線程的名字 t1.setDaemon(true); t1.start(); for (int i = 0; i < 1; i++) { System.out.println(i); } } }//結果:/* 0123456789Thread-Name: 第一個線程 Thread-id: 9*//* * 從上面的結果可以看出,一旦主線程結束,那麼守護線程就會自動的結束 * /
前言
以前大家寫的都是單線程的程序,全是在main函數中調用方法,可以清楚的看到它的效率是特別低的,就像python中使用單線程取爬一個網站,可以說能讓你等的吐血,因爲數據量實在太大了,今天我們就來看看java的併發編程多線程的學習
創建線程
創建一個線程可以有多種方法,比如繼承Thread類,實現Runnable接口......下面我們來詳細的看看創建的方法
繼承Thread
爲什麼繼承
Thread
可以直接調用start()
方法啓動線程呢,因爲start()
本身就是Thread的方法,也就是繼承了Thread的start()方法,因此這個類的對象可以調用start()啓動線程
//繼承Threadpublic class MyThread extends Thread { public void run() { for (int i = 0; i < 10; i++) { System.out.println(this.getName()+"正在跑"); } } }public class Test{ public static void main(String[] args) { Mythread t1=new MyThread(); //創建對象 t1.start(); //啓動線程 } }
注意: 繼承
Thread
類的創建方法一個對象只能創建一個線程,並不能多個線程共用一個對象,只能一個線程對應一個對象,因此我們來看看實現Runnable
接口的類來實現多個線程共享同一個對象
實現Runnable接口
//實現Runnable接口public class Demo implements Runnable { @Override public void run() { for(int i=0;i<10;i++) { System.out.println(Thread.currentThread().getName()+"正在跑"); } } }//測試類public class Test{ public static void main(String[] args) { Demo d=new Demo(); //創建對象 Thread thread1=new Thread(d); //爲對象創建一個線程 Thread thread2=new Thread(d); //創建另外一個線程 //同時啓動兩個線程 thread1.start(); thread2.start(); } }
從上面可以清楚的看到實現
Runnable
接口的類一個對象可以供多個線程共享,並不像繼承Thread類只爲一個線程使用
簡便的創建方法
直接在
main
方法中創建,如果創建的普通類的對象在外面,那麼必須是final修飾,可以實現多個線程同時共享一個對象,這個和實現Runnable
接口一樣,這時候就要控制同步條件了,如果在run方法中定義對象,那麼,就是一個線程對應一個對象,這個就和繼承Thread類一樣的效果。所以可以根據條件自由選擇
//普通的一個類public class Simple { public void display() { for(int i=0;i<10;i++) { System.out.println(Thread.currentThread().getName()+"正在跑"); } } }//線程測試類public class Test { public static void main(String[] args) { //如果在外面必須使用final,當然也可以直寫在run方法中,不過寫在外面可以實現多個線程共享一個對象 //寫在run方法中當前對象只能爲一個線程使用,和繼承Thread類一樣的效果 final Simple simple=new Simple(); //下面創建使用同一個對象創建同兩個線程,實現多個線程共享一個對象,和實現Runnable接口一樣的效果 Thread t1=new Thread(){ public void run() { simple.display(); }; }; Thread t2=new Thread(){ public void run() { simple.display(); }; }; //啓動這兩個線程 t1.start(); t2.start(); }}
常用的方法
static void sleep(long mils)
使正在運行的線程休眠mils毫秒,但是這裏需要注意的是如果線程加了鎖,那麼使線程休眠並不會釋放鎖
String getName()
得到線程的名稱,上面的程序中已經使用了這個方法
void setName(String name)
設置正在運行的線程的名字爲name
start()
啓動線程,線程的創建並不意味着線程的啓動,只有調用start()方法線程纔是真正的開始運行
long getId()
返回線程的標識符
run()
線程執行的代碼都放在run()方法中,在run方法中的調用是有序的,都是按照程序運行的順序開始執行
使用
下面使用上面的方法創建一個實例
//線程的類,繼承Threadpublic class MyThread1 extends Thread { public void run() { // 重載run方法,並且在其中寫線程執行的代碼塊 for (int i = 0; i < 10; i++) { // 獲取線程的id和name System.out.println("Thread-Name: " + this.getName() + " Thread-id: " + this.getId()); try { this.sleep(1000); // 線程休眠1秒 } catch (InterruptedException e) { e.printStackTrace(); } } } }//線程測試的類public class Test { public static void main(String[] args) { MyThread1 t1 = new MyThread1(); // 創建線程 t1.setName("第一個線程"); // 設置線程的名字 MyThread1 t2 = new MyThread1(); t2.setName("第二個線程"); t1.start(); // 啓動線程,開始運行 t2.start(); } }
void join()
等待該線程終止才能運行其他的線程
void join(long mils)
等待該線程的時間爲mils毫秒,一旦過了這個時間其他線程正常執行
使用
//線程類public class MyThread1 extends Thread { public void run() { // 重載run方法,並且在其中寫線程執行的代碼塊 for (int i = 0; i < 10; i++) { // 獲取線程的id和name System.out.println("Thread-Name: " + this.getName() + " Thread-id: " + this.getId()); try { this.sleep(1000); // 線程休眠1秒 } catch (InterruptedException e) { e.printStackTrace(); } } } }//測試類public class Test { public static void main(String[] args) { MyThread1 t1 = new MyThread1(); // 創建線程 t1.setName("第一個線程"); // 設置線程的名字 t1.start(); // 啓動線程,開始運行 try { t1.join(); //阻塞其他線程,只有當這個線程運行完之後纔開始運行其他的線程 } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 10; i++) { System.out.println("主線程正在運行"); } } }//輸出結果/*Thread-Name: 第一個線程 Thread-id: 9Thread-Name: 第一個線程 Thread-id: 9Thread-Name: 第一個線程 Thread-id: 9Thread-Name: 第一個線程 Thread-id: 9Thread-Name: 第一個線程 Thread-id: 9Thread-Name: 第一個線程 Thread-id: 9Thread-Name: 第一個線程 Thread-id: 9Thread-Name: 第一個線程 Thread-id: 9Thread-Name: 第一個線程 Thread-id: 9Thread-Name: 第一個線程 Thread-id: 9主線程正在運行主線程正在運行主線程正在運行主線程正在運行主線程正在運行主線程正在運行主線程正在運行主線程正在運行主線程正在運行主線程正在運行 */
getPriority()
得到當前線程優先級
setPriority(int num)
更改線程的優先級(0-10)默認的是5,優先級越高獲得cpu資源的機率就會越高
使用
//線程類public class MyThread1 extends Thread { public void run() { // 重載run方法,並且在其中寫線程執行的代碼塊 for (int i = 0; i < 10; i++) { // 獲取線程的id和name System.out.println("Thread-Name: " + this.getName() + " Thread-id: " + this.getId()); try { this.sleep(1000); // 線程休眠1秒 } catch (InterruptedException e) { e.printStackTrace(); } } } }//測試類public class Test { public static void main(String[] args) { MyThread1 t1 = new MyThread1(); // 創建線程 t1.setName("第一個線程"); // 設置線程的名字 MyThread1 t2 = new MyThread1(); t2.setName("第二個線程"); t2.setPriority(8); //設置第二個線程的優先級爲8,第一個線程的優先級爲5(是默認的) t1.start(); t2.start(); } }/* * 從上面的運行結果可以看出大部分的第二個線程都是在第一個線程之前開始執行的,也就是說優先級越高獲得cpu執行的機率就越大 * /
setDaemon(boolean)
是否設置爲守護線程,如果設置爲守護線程,那麼主線程銷燬守護線程也會隨之銷燬
isDaemon()
判斷是否爲守護線程
使用
//測試類public class MyThread1 extends Thread { public void run() { // 重載run方法,並且在其中寫線程執行的代碼塊 for (int i = 0; i < 10; i++) { // 獲取線程的id和name System.out.println("Thread-Name: " + this.getName() + " Thread-id: " + this.getId()); try { Thread.sleep(1000); //休眠一秒,方便主線程運行結束 } catch (InterruptedException e) { e.printStackTrace(); } } } }public class Test { public static void main(String[] args) { MyThread1 t1 = new MyThread1(); // 創建線程 t1.setName("第一個線程"); // 設置線程的名字 t1.setDaemon(true); t1.start(); for (int i = 0; i < 1; i++) { System.out.println(i); } } }//結果:/* 0123456789Thread-Name: 第一個線程 Thread-id: 9*//* * 從上面的結果可以看出,一旦主線程結束,那麼守護線程就會自動的結束 * /