東哥說java併發 第一集

實現多線程的方法

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方法就是一個簡單的運行方法,它運行主線程。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章