1、繼承Thread
其實也是實現了runnable接口的。
public class Threadextend extends Thread{
@Override
public void run() {
for(int i=0;i<=100;i++){
System.out.print("A"+i+ "\t");
}
}
}
public class Main {
public static void main(String[] args) {
Threadextend trea=new Threadextend("小強");
//start 表示開啓線程
trea.start();
for(int i=0 ;i <100;i++){
System.out.print("B"+i +"\t");
}
System.out.println(trea.getName());
}
}
結果如下,隨機打印
2、實現runnable接口
實現Runnable接口比繼承Thread類所具有的優勢:
1. 適合多個相同的程序代碼的線程去共享同一個資源。 (,注意我說的是適合,這裏我從網上看到了一些人的回答,都說thread不能資源共享,可能是斷章取義吧,Thread可以實現資源共享的,只不過是現實開發中它不適合資源共享,因爲它如果想資源共享的話可以將共享的資源設置成靜態的,因爲靜態資源的生命週期是和類綁在一起的,和對象沒有關係。但是這樣做的壞處就是,如果此時有兩個不一樣的任務的話,同時都調用這個類,那麼就不能實現了,因爲靜態變量不能區分這兩個任務了)
2、 可以避免java中的單繼承的侷限性。 (就是說接口的好處了,實現完接口後還可以繼承)
3、增加程序的健壯性,實現解耦操作,代碼可以被多個線程共享,代碼和線程獨立。
(從代碼架構角度:具體的任務(run方法)應該和“創建和運行線程的機制(Thread類)”解耦)
4、線程池只能放入實現Runable或Callable類線程,不能直接放入繼承Thread的類。
class ThreadRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<=100;i++){
System.out.print("A"+i+ "\t");
}
}
}
public static void main(String[] args) {
ThreadRunnable threadRunnable=new ThreadRunnable();
Thread thread = new Thread(threadRunnable, "小強");
thread.start();
for(int i=0 ;i <100;i++){
System.out.print("B"+i +"\t");
}
System.out.println(thread.getName());
}
結果同上
3、jdk1.5後,實現callable接口
和以上相比,callable更加強大一些
1:相比run()方法,可以有返回值
2:方法可以拋出異常
3:支持泛型的返回值
4:需要藉助FutureTask類,比如獲取返回結果
class ThreadCallable implements Callable<Integer>{
int result=0;
@Override
public Integer call() throws Exception {
for(int i=0;i<=100;i++){
System.out.println(i);
result +=i;
}
return result;
}
}
public static void main(String[] args) {
ThreadCallable threadCallable = new ThreadCallable();
FutureTask<Integer> result= new FutureTask<>(threadCallable);
new Thread(result).start();
try {
System.out.println(result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
4、jdk1.5後,線程池
提前創建好多個線程,放入線程池中,使用時直接獲取,使用完放回池中。可以避免頻繁創建銷燬、實現重複利用。提高響應速度 (減少了創建新線程的時間) 降低資源消耗(重複利用線程池中線程,不需要每次都創建) 便於線程管理。
4.1體系結構
如上圖,頂級接口爲Executor,真正的線程池接口是ExecutorService,下面是對上圖的說明
4.2 工具類:Executors
Executors.newCachedThreadPool():創建一個可根據需要創建新線程的線程池
Executors.newFixedThreadPool(n); 創建一個可重用固定線程數的線程池
Executors.newSingleThreadExecutor() :創建一個只有一個線程的線程池
Executors.newScheduledThreadPool(n):創建一個線程池,它可安排在給定延遲後運
行命令或者定期地執行。 返回值類型爲ScheduleThreadPoolExeccutor。
class ThreadRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<=100;i++){
System.out.print(Thread.currentThread().getName()+ "\t"+ "A"+i+ "\t");
}
}
}
線程池與Runnable的使用方式
public static void main(String[] args) {
// 創建線程池
ExecutorService pool = Executors.newFixedThreadPool(5);
ThreadRunnable trb=new ThreadRunnable();
//爲線程池分配任務
for(int i=0;i<10;i++){
pool.submit(trb);
}
//關閉線程池,shutdown只是將線程池的狀態設置爲SHUTWDOWN狀態,
//正在執行的任務會繼續執行下去,沒有被執行的則中斷。而shutdownNow
//則是將線程池的狀態設置爲STOP,正在執行的任務則被停止,沒被執行任務的則返回。
pool.shutdown();
}
線程池與Callable的使用方式, 帶有返回值Future
// 創建線程池
ExecutorService pool = Executors.newFixedThreadPool(5);
List<Future<Integer>> list = new ArrayList<>();
//爲線程池分配任務
for(int i=0;i<10;i++){
Future<Integer> future = pool.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum=0;
for (int j = 0; j <100 ; j++) {
sum+=j;
}
return sum;
}
});
list.add(future);
}
//關閉線程池
pool.shutdown();
for (Future<Integer> future : list) {
System.out.println(future.get());
}
5、延申:start() 和run()有什麼區別呢?
run():只是調用了一個普通方法,並沒有啓動另一個線程,程序還是會按照順序執行相應的代碼。線程被調度的時候,執行的操作
start():重新開啓一個線程,此線程進入到就緒狀態,不必等待其他線程運行完,只要得到cup資源就可以運行該線程,如下圖,這個圖在後面的博文中會有詳細的講解