java執行緒的創建與實作
行程與執行緒
行程可以簡單理解成一個可執行程式例如.exe,在Windows中的任務管理器中可以查看每一個行程,行程是一次程式的執行,是程式在資料集合上運行的程序,是系統資源調度的一個單位,行程主要負責向作業系統申請資源,然而一個行程中,多個執行緒可以共享行程中相同的記憶體或檔案資源,執行緒就是一個行程一個程式要完成所依賴的子任務,這些子任務便可以看作是一個執行緒,

第一種方式繼承Thread類
從java原始碼可以看出Thread類本質上實作了Runnable介面的實體類,代表了執行緒的一個執行緒的實體,啟動的執行緒唯一辦法就是通過Thread類呼叫start()方法,start()方法是需要本地作業系統的支持,它將啟動一個新的執行緒,并且執行run()方法,

繼承Thread類實作執行緒代碼如下
創建一個Thread類,物件直接呼叫run方法會出現什么問題?
package cn.thread.執行緒;
public class MyThread extends Thread{
public MyThread(String name){
super(null,null,name);
}
int piao =10;
@Override
public void run() {
while(piao>0){
System.out.println(Thread.currentThread().getName()+"......"+piao--);
}
}
public static void main(String[] args) {
MyThread mt = new MyThread("x");
mt.run();
}
}
結果:
可以發現是主執行緒執行了run方法,并不是用戶執行緒執行的run方法,此時可以得出用戶執行緒并沒有啟動,所以并不會執行run里面的方法,且執行完run方法便結束執行緒,

第二種創建執行緒的方法,實作Runnable介面
相比繼承Thread類而言,實作介面的可擴展性得到了提升,Runnable介面也必須要封裝到Thread類里面,才可以呼叫start方法,啟動執行緒,
實作代碼
package cn.thread.執行緒;
public class MyRunnable implements Runnable{
int piao = 10;
@Override
public void run() {
while(piao>0){
System.out.println(Thread.currentThread().getName()+"-----"+piao--);
}
}
public static void main(String[] args) {
Runnable r =new MyRunnable();
Thread t =new Thread(r);
t.start();
}
}
結果

第三種創建執行緒的方法實作Callable介面
Callable介面使用方法和Runnable介面的方法類似不同的一點是Callable介面具有回傳值,回傳結果并且可能拋出例外的任務,實作者定義了一個不帶任何引數的叫做 call 的方法, Callable 介面類似于 Runnable,兩者都是為那些其實體可能被另一個執行緒執行的類設計的,但是 Runnable 不會回傳結果,并且無法拋出經過檢查的例外,
實作代碼
對下列代碼進行分析
首先callable是介面不能直接創建物件,也不能創建執行緒,并且要實作call方法類似run方法的功能,call方法有回傳值,會計算結果,如果無法計算結果,則拋出一個例外,
執行callable任務之后,可以獲得一個future的物件,future基本上是主執行緒可以跟蹤進度以及獲取其他執行緒結果的一種方式,在這里Test1()方法主要利用執行緒池和future的方法,去啟動實作Callable介面的執行緒,具有回傳值,而Test2()主要是采用FutureTask類去實作創建一個實作callable介面的執行緒,futuretask實作了future介面,
package com.openlab.test;
import java.util.Random;
import java.util.concurrent.Callable;
public class CallableTest implements Callable{
@Override
public Object call() throws Exception {
Random generator = new Random();
Integer randomNumber = generator.nextInt(5);
Thread.sleep(randomNumber*1000);
return randomNumber;
}
}
綜合練習代碼:
package cn.thread.執行緒;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;
public class CallableTest implements Callable<Object> {//不能直接創建執行緒
int taskNum;
public CallableTest(int taskNum){
this.taskNum = taskNum;
}
@Override
public Object call() throws Exception {
System.out.println(">>>"+taskNum+"任務啟動");
Date dataTemp = new Date();
Thread.sleep(1000);
Date dataTemp2 = new Date();
long time = dataTemp2.getTime() - dataTemp.getTime();
System.out.println(">>>>"+taskNum+"任務終止");
return taskNum+"任務回傳運行結果"+time;
// Random generator = new Random();
// Integer randomNumber = generator.nextInt(5);
// Thread.sleep(randomNumber*1000);
// return randomNumber;
}
/*test1方法采用Executors的靜態方法newFixedThreadPool(taskSize) 創建一個可重用固定執行緒集合的
執行緒池,以共享的無界佇列方式來運行這些執行緒,獲取執行緒池,ExecutorService的submit方法提交一個
callable實體,得到一個future物件,最終將future物件存盤在list陣列中,加入執行緒池的程序中就代表
著執行緒已經開始執行,相當于一個執行緒池代理程序,就不需要采用start方法啟動執行緒,最后對future進行
列印輸出,切記一定要關閉執行緒池!*/
static void test1() throws ExecutionException, InterruptedException {
System.out.println("程式開始");
Date data1 = new Date();
int taskSize = 5;
//構建執行緒池物件
ExecutorService pool = Executors.newFixedThreadPool(taskSize);
List<Future> list =new ArrayList<Future>();
for(int i=0;i<taskSize;i++){
Callable c = new CallableTest(i);
Future f = pool.submit(c);
list.add(pool.submit(c));
}
//關閉執行緒池
pool.shutdown();
for(Future f:list){
System.out.println(">>>"+f.get().toString());
}
Date date2 = new Date();
System.out.println("程式運行結束-----"+(date2.getTime()-data1.getTime())+"毫秒");
}
/*test2方法主要是采用futuretask類,可以直接把callable作為引數來申明futuretask物件,
這里相當于把執行緒池換成了futuretask陣列,因為test1執行緒池可以對callable進行封裝,
在這里可以直接采用futuretask就行封裝,在加上futuretask又實作了runnable介面,
所以可以直接創建執行緒采用start的方式進行啟動執行緒,*/
static void test2() throws ExecutionException, InterruptedException {
System.out.println("----程式開始-----");
Date date1 =new Date();
int taskSize = 5;
FutureTask[] randNumber = new FutureTask[taskSize];
List<Future> list =new ArrayList<Future>();
for(int i=0;i<taskSize;i++){
Callable c = new CallableTest(i);
randNumber[i] = new FutureTask(c);
Thread t = new Thread(randNumber[i]);
t.start();
}
for(Future f:randNumber){
System.out.println(">>>"+f.get().toString());
}
Date date2 = new Date();
System.out.println("程式運行結束-----"+(date2.getTime()-date1.getTime())+"毫秒");
}
public static void main(String[] args) throws Exception {
// CallableTest c = new CallableTest();
// Integer i = (Integer) c.call();
test1();
test2();
}
}
執行結果

第四種實作執行緒的方法,基于執行緒池
其實在第三種的方法中就提到了兩種實作方法,一種執行緒池+future,另一種futuretask的方法,執行緒和資料庫連接這些資源都是非常寶貴的資源,那么每次需要的時候創建,不需要的時候銷毀,是非常浪費資源的,那么我們就可以使用快取的策略,也就是使用執行緒池,
// 創建執行緒池
ExecutorService threadPool = Executors.newFixedThreadPool(10);
while(true) {
threadPool.execute(new Runnable() { // 提交多個執行緒任務,并執行
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " is running ..");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
} }
總結
理論上實作執行緒的方法還有一些,本文所提及到的,基本都是一些創建執行緒常用的方法,希望本文對大家在學習執行緒的程序中有所幫助,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/292477.html
標籤:java
上一篇:Java基本程式設計結構
下一篇:爬蟲逆向學習進階路線
