實現多線程的方法
package threadcoreknowledge.createthreads;
/**
* @author gaston
* 用runnable方式創建線程
*/
public class RunnableStyle implements Runnable{
@Override
public void run() {
System.out.println("用Runnable方法實現線程");
}
public static void main(String[] args) {
Thread thread = new Thread(new RunnableStyle());
thread.start();
}
}
------------------------------------------------------------------------
package threadcoreknowledge.createthreads;
/**
* @author gaston
* 用Thread方式實現線程
*/
public class ThreadStyle extends Thread{
public static void main(String[] args) {
new ThreadStyle().start();
}
@Override
public void run(){
System.out.println("用Thread方法實現線程");
}
}
以上兩種方式對比,使用Runnable比較好:
1.Thread方式的run主要邏輯方法和Thread創建,thread類應該是解耦的,不能把兩個事情混爲一談
2.使用Thread方式每次開啓一個任務就要啓動一個線程,而新建一個獨立的線程這樣的損耗是比較大的,他需要創建,執行和銷燬,runnable可以使用線程池工具
3.繼承了Thread後不能繼承其他類,不能多繼承
查看一下run方法的源碼:
package threadcoreknowledge.createthreads;
/**
* @author gaston
* 測試同時使用Runnable和Tread啓動
*/
public class BothRunnableThread {
public static void main(String[] args) {
new Thread(new Runnable(){
@Override
public void run() {
System.out.println("我是Runnable啓動的");
}
}){
@Override
public void run() {
System.out.println("我是Thread啓動的");
}
}.start();
}
}
/**
運行結果:
我是Thread啓動的
說明:在main方法首先new一個thread匿名內部類,並且重寫Thread的run方法,同時創建Runnable匿名內部類並重寫run方法,這裏Runnable也是作爲了Thread的參數,參考上邊源碼run方法要麼執行target.run()要麼就是run()方法被重寫,在這裏是被Thread的run重寫了方法裏邊沒有源碼原始的邏輯了也就不可能會是target.run()也就是不可能是Runnable線程輸出的內容
**/
總結:如果要問啓動線程的方法有幾種,最精準的描述?
答:1.通常我們可以分爲兩類,根據oracle官方說明就是這樣的,繼承Thread類和實現Runnable接口
2.準確的說,創建線程只有一種方式就是構造Thread類(源碼追蹤也是這樣的最終都會定位到Thread類下的run方法),而實現線程的執行單元有兩種方式:
方法一,實現Runnable接口重寫run方法,並把Runnable實例傳給Thread類;
方法二,繼承Thread類,重寫Thread的run方法。
package threadcoreknowledge.createthreads.wrongways;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author gaston
* 驗證錯誤說法,使用線程池啓動線程
*/
public class ThreadPool5 {
public static void main(String[] args) {
ExecutorService executorService =
Executors.newCachedThreadPool();
for(int i =0; i < 20; i++){
executorService.submit(new Task(){
});
}
}
}
class Task implements Runnable{
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}
結果:
pool-1-thread-13
pool-1-thread-4
pool-1-thread-17
pool-1-thread-16
pool-1-thread-6
pool-1-thread-18
pool-1-thread-12
pool-1-thread-19
pool-1-thread-2
pool-1-thread-14
pool-1-thread-5
pool-1-thread-7
pool-1-thread-8
pool-1-thread-1
pool-1-thread-11
pool-1-thread-9
pool-1-thread-10
pool-1-thread-3
pool-1-thread-15
pool-1-thread-20
線程池源碼追蹤:
錯誤說法:通過Callable和FutureTask創建線程,也算是一種新建線程的方式,錯誤原因是它們最終也是通過Thread或Runnable啓動的線程,如圖
package threadcoreknowledge.createthreads.wrongways;
/**
* @author gaston
* 演示java8新特性 lambda啓動線程
*/
public class Lambda {
public static void main(String[] args) {
new Thread(() -> System.out.println(Thread.currentThread().getName())).start();
}
}
package threadcoreknowledge.createthreads.wrongways;
/**
* @author gaston
* 對比start和run啓動方式
*/
public class StartAndRunMethod {
public static void main(String[] args) {
Runnable runnable = () -> {
System.out.println(Thread.currentThread().getName());
};
runnable.run();
new Thread(runnable).start();
}
}
結果:
main
Thread-0
run方法執行的是主線程,而不是像start方法那樣新創建一個線程
start()方法啓動一個新線程,但並不是立即運行這個線程,等待jvm和線程調度器來安排,他可以讓主線程和新建的線程運行,父線程主線程啓動後再去啓動新線程,不能兩次調用start方法。
檢查線程狀態,加入線程組,調用start0()
針對於run方法源碼就是那個target.run()代碼,沒有參數的run方法就是一個簡單的運行方法,它運行主線程。