Java多線程(一)
一、程序、進程、線程基本概念
程序:是爲完成特定任務,用某種語言編寫的一組指令的集合,即指一段靜態的代碼,靜態對象。
進程:是程序的一次執行過程,或是正在運行的一個程序,是一個動態的過程,有它自身的產生,存在和消亡的過程。
線程:線程是操作系統能夠進⾏運算調度的最⼩單位,它被包含在進程之中,是進程中的實際運作單位,可以使⽤多線程對
進⾏運算提速。
二、併發、並行基本概念
並行:多個CPU同時執行多個任務。多個cpu實例或者多臺機器同時執行一段處理邏輯。
併發:一個CPU(採用時間片)同時執行多個任務。通過cpu調度算法,讓用戶看上去同時執行,實際上從cpu操作層面不是真正的同時。
三、Java多線程基本狀態及生命週期
多線程有五種基本狀態,分別是新建狀態、就緒狀態,運行狀態,死亡狀態,阻塞狀態。
線程的基本狀態:
- 新建 :從新建一個線程對象到程序start() 這個線程之間的狀態,都是新建狀態;
- 就緒 :線程對象調用start()方法後,就處於就緒狀態,等到JVM裏的線程調度器的調度;
- 運行 :就緒狀態下的線程在獲取CPU資源後就可以執行run(),此時的線程便處於運行狀態,運行狀態的線程可變爲就緒、阻塞及死亡三種狀態。
- 等待/阻塞/睡眠 :在一個線程執行了sleep(睡眠)、suspend(掛起)等方法後會失去所佔有的資源,從而進入阻塞狀態,在睡眠結束後可重新進入就緒狀態。
- 終止 :run()方法完成後或發生其他終止條件時就會切換到終止狀態。
四、Java多線程常用方法
- start():1.啓動當前線程2.調用線程中的run方法
- run():通常需要重寫Thread類中的此方法,將創建的線程要執行的操作聲明在此方法中
- currentThread():靜態方法,返回執行當前代碼的線程
- getName():獲取當前線程的名字
- setName():設置當前線程的名字
- yield():主動釋放當前線程的執行權
- join():在線程中插入執行另一個線程,該線程被阻塞,直到插入執行的線程完全執行完畢以後,該線程才繼續執行下去
- stop():過時方法。當執行此方法時,強制結束當前線程。
- sleep(long millitime):線程休眠一段時間
- isAlive():判斷當前線程是否存活
五、多線程創建方式
(一)繼承Thread類:
- 創建一個集成於Thread類的子類
- 重寫Thread類的run( )方法
- 創建Thread子類的對象
- 通過此對象調用start( )方法
start( )方法與run( )方法的區別:
start( )方法:
用start方法來啓動線程,真正實現了多線程運行,這時無需等待run方法體代碼執行完畢而直接繼續執行下面的代碼。通過調用Thread類的start()方法來啓動一個線程,這時此線程處於就緒(可運行)狀態,並沒有運行,一旦得到cpu時間片,就開始執行run()方法,這裏方法run()稱爲線程體,它包含了要執行的這個線程的內容,run方法運行結束,此線程隨即終止。
Java源碼start( )方法:
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
run( )方法:
run()方法只是類的一個普通方法而已,如果直接調用Run方法,程序中依然只有主線程這一個線程,其程序執行路徑還是隻有一條,還是要順序執行,還是要等待run方法體執行完畢後纔可繼續執行下面的代碼,這樣就沒有達到寫線程的目的。
Java源碼run( )方法:
@Override
public void run() {
if (target != null) {
target.run();
}
}
Thread創建線程實例:
public class Main {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
//使用lambda表達式簡化
new Thread(()->{
int i = 0;
while(true){
System.out.println(Thread.currentThread().getName()+"線程"+i++);
}
}).start();
}
}
class MyThread extends Thread{
int i = 0;
@Override
public void run() {
while(true){
System.out.println(Thread.currentThread().getName()+"線程"+i++);
}
}
}
(二)實現Runable接口:
- 創建一個實現了Runable接口的類
- 實現類去實現Runnable中的抽象方法:run()
- 創建實現類的對象
- 將此對象作爲參數傳遞到Thread類中的構造器中,創建Thread類的對象
- 通過Thread類的對象調用start()
實現Runable接口實例:
public class RunnableTest {
public static void main(String[] args) {
MyRunnable m1 = new MyRunnable();
Thread t1 = new Thread(m1);
Thread t2 = new Thread(m1);
t1.start();
t2.start();
//局部內部類
class Test2 implements Runnable{
@Override
public void run() {
for(int i=0;i<5;i++) {
System.out.println("局部內部類");
}
System.out.println("-------------");
}
}
new Thread(new Test2()).start();
//匿名內部類
new Thread(new Runnable(){
@Override
public void run() {
for(int i=0;i<5;i++) {
System.out.println("匿名內部類");
}
System.out.println("-------------");
}
}).start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+"線程"+i++);
}
}
}
(三)實現callable接口:
與實現Runable接口相比:
- runnable重寫的run方法不如callaalbe的call方法強大,call方法可以有返回值
- 方法可以拋出異常
- 支持泛型的返回值
- 需要藉助FutureTask類,比如獲取返回結果
使用Callable創建線程:
- 創建一個實現callable的實現類
- 實現call方法,將此線程需要執行的操作聲明在call()中
- 創建callable實現類的對象
- 將callable接口實現類的對象作爲傳遞到FutureTask的構造器中,創建FutureTask的對象
- 將FutureTask的對象作爲參數傳遞到Thread類的構造器中,創建Thread對象,並調用start方法啓動(通過FutureTask的對象調用方法get獲取線程中的call的返回值)
實現callable接口實例:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableTest {
public static void main(String[] args){
//new一個實現callable接口的對象
NumThread numThread = new NumThread();
//通過futureTask對象的get方法來接收futureTask的值
FutureTask futureTask = new FutureTask(numThread);
Thread t1 = new Thread(futureTask);
t1.setName("線程1");
t1.start();
try {
//get返回值即爲FutureTask構造器參數callable實現類重寫的call的返回值
Object sum = futureTask.get();
System.out.println(Thread.currentThread().getName()+":"+sum);
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class NumThread implements Callable {
private int sum=0;//
@Override
public Object call() throws Exception {
for(int i=0;i<5;i++){
sum += i;
System.out.println(Thread.currentThread().getName()+"線程"+i++);
}
return sum;
}
}