一、多線程三種實現(xiàn)方式的對比
運行結果
優(yōu)點
缺點
繼承Thread類
無法獲取
編程比較簡單,可以直接使用
擴展性較差,不能再繼承其他的類
實現(xiàn)Runnable接口
擴展性強,實現(xiàn)該接口的同時還可以繼承其他的類
編程相對復雜,不能直接使用Thread類中的方法
實現(xiàn)Callable接口
可以獲取
二、Thread類常見成員方法
方法名稱
說明
String getName()
返回此線程的名稱
void setName(String name)
設置線程的名字(構造方法也可以設置名字)
static Thread currentThread()
獲取當前線程的對象
static void sleep(long time)
讓線程休眠指定的時間,單位為毫秒
setPriority(int newPriority)
設置線程的優(yōu)先級
final int getpriority()
獲取線程的優(yōu)先級
final void setDaemon(boolean on)
設置為守護線程
public static void yield()
出讓線程/禮讓線程
public static void join()
插入線程/插隊線程
(一)getName()和setName()
可以通過setName方法手動給線程命名,否則線程將使用默認名稱,格式為Thread-X,其中X為線程的序號,從0開始編號。另外,也可以通過Thread類的構造方法public Thread(String name)給線程命名。
(一)static currentThread()
獲取當前線程對象。當JVM虛擬機啟動之后,會自動的啟動多條線程,其中有一條線程就叫做main線程。他的作用就是去調用main方法,并執(zhí)行里面的代碼。在以前,我們寫的所有的代碼,其實都是運行在main線程當中。
注意這是一個靜態(tài)方法,通過類名調用,并不是通過Thread對象調用。因此返回的Thread對象是當前運行的線程的對象。
(二)static sleep()休眠
使當前正在執(zhí)行的線程休眠(暫時停止執(zhí)行)指定的毫秒數(shù),但受系統(tǒng)計時器和調度器的精度和準確性的限制。該線程不會失去任何監(jiān)視器的所有權。
注意這是一個靜態(tài)方法,直接通過Thread調用,表示將當前的線程睡眠一定的時間。
線程類實現(xiàn)代碼:
public class MyThread extends Thread{
public MyThread(String name){
super(name);
}
public MyThread(){
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
// 休眠1秒鐘
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 要執(zhí)行的代碼
System.out.println(getName() + (i + 1));
}
}
}
線程使用:
public class ThreadDemo4 {
public static void main(String[] args) throws InterruptedException {
/*
String getName() 返回此線程的名稱
void setName(String name) 設置線程的名字(構造方法也可以設置名
細節(jié):
1、如果我們沒有給線程設置名字,線程也是有默認的名字的
格式:Thread-X(X序號,從0開始的)
2、如果我們要給線程設置名字,可以用set方法進行設置,也可以構造方法設置
static Thread currentThread() 獲取當前線程的對象
static void sleep(long time) 讓線程休眠指定的時間,單位為毫秒
*/
// 1。創(chuàng)建線程的對象
MyThread mt1 = new MyThread("飛機");
MyThread mt2 = new MyThread("大炮");
// 設置優(yōu)先級
mt1.setPriority(10);
mt2.setPriority(1);
// 2。開啟線程
mt1.start();
mt2.start();
/*
// 3.獲取線程
Thread thread = Thread.currentThread();
String name = thread.getName();
System.out.println(name);
*/
/*
// 線程睡眠
System.out.println("111111111");
long startTime = System.currentTimeMillis();
Thread.sleep(3000);
System.out.println("222222222");
System.out.println((System.currentTimeMillis() - startTime));
*/
}
}
(三)setPriority和getPriority
更改線程優(yōu)先級,最小是1,最大是10,優(yōu)先級越大越有可能搶占到CPU時間,默認是5。
線程的調度,一般有2種:
l?搶占式調度
l?非搶占式調度
Java中采用搶占式調度的方法,隨機性高,線程的優(yōu)先級越高就越有可能搶到CPU執(zhí)行時間。因此,優(yōu)先級高的線程只是獲得CPU時間的可能性更高,但是也有可能低優(yōu)先級的線程排在高優(yōu)先級的線程前執(zhí)行。
(二)setDaemon(boolean on)
將此線程標記為守護線程或用戶線程。當運行的線程都是守護線程時,Java虛擬機退出。必須在啟動線程之前調用此方法。
當其他的非守護線程執(zhí)行完畢之后,守護線程會陸續(xù)結束,這意味著守護線程不一定會按照代碼指定的方式執(zhí)行結束,很有可能守護線程執(zhí)行到中途就會被退出。
public class ThreadDemo5 {
public static void main(String[] args) {
/*
final void setDaemon(boolean on) 設置為守護線程
細節(jié):
當其他的非守護線程執(zhí)行完畢之后,守護線程會陸續(xù)結束
通俗易懂的理解:
當女神線程結束了,那么備胎也沒有存在的必要了
*/
// 創(chuàng)建線程對象
MyThread01 t1 = new MyThread01(5);
MyThread01 t2 = new MyThread01(20);
// 命名線程
t1.setName("被偏愛的");
t2.setName("備胎");
// 把第二個線程設置為守護線程
t2.setDaemon(true);
// 開啟線程
t1.start();
t2.start();
}
}
MyThread01類的代碼:
public class MyThread01 extends Thread{
private int count = 0;
public MyThread01(int count){
this.count = count;
}
public MyThread01(){
this.count = 100;
}
@java.lang.Override
public void run() {
// 線程要執(zhí)行的代碼
for (int i = 0; i < count; i++) {
System.out.println(getName() + ": hello world." + i);
}
}
}
以上代碼中,t1線程只需要執(zhí)行5次即完成,而t2守護線程需要執(zhí)行20次,當t1執(zhí)行完畢后,t2作為守護線程會陸續(xù)退出,一般執(zhí)行不到20次即會退出。
一次運行的結果:
備胎: hello world.0
備胎: hello world.1
備胎: hello world.2
被偏愛的: hello world.0
備胎: hello world.3
被偏愛的: hello world.1
備胎: hello world.4
被偏愛的: hello world.2
備胎: hello world.5
被偏愛的: hello world.3
備胎: hello world.6
被偏愛的: hello world.4
備胎: hello world.7
備胎: hello world.8
備胎: hello world.9
備胎線程運行了9次即退出了。
(三)yield方法
yield方法表示交出當前線程的執(zhí)行權。
public class MyThread extends Thread{
public MyThread(String name){
super(name);
}
public MyThread(){
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
/*
try {
// 休眠1秒鐘
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
*/
// 要執(zhí)行的代碼
System.out.println(getName() + (i + 1));
// 表示出讓cpu的執(zhí)行權
Thread.yield();
}
}
}
執(zhí)行代碼:
public class ThreadDemo6 {
public static void main(String[] args) {
MyThread mt1 = new MyThread("飛機");
MyThread mt2 = new MyThread("大炮");
// 執(zhí)行
mt1.start();
mt2.start();
}
}
以上代碼在運行時會更加均勻的分次執(zhí)行。
(四)join方法
join方法表示把當前線程插入到某個線程之前進行執(zhí)行。
public class MyThreadJoin extends Thread{
public MyThreadJoin(String name){
super(name);
}
public MyThreadJoin(){
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
// 要執(zhí)行的代碼
System.out.println(getName() + "@" + (i + 1));
}
}
}
執(zhí)行代碼:
public class MyThreadJoinDemo {
public static void main(String[] args) throws InterruptedException {
/*
public final void join() 插入線程/插隊線程
*/
MyThreadJoin mtj = new MyThreadJoin();
mtj.setName("土豆");
mtj.start();
// 表示把mtj這個線程,插入到當前線程之前
// mtj:土豆線程
// 當前線程:即是main線程
mtj.join();
// 在main線程中執(zhí)行
for (int i = 0; i < 10; i++) {
System.out.println("main線程@" + i);
}
}
}
一、線程的生命周期
問:sleep方法會讓線程睡眠,睡眠時間到了之后,立馬就會執(zhí)行下面的代碼嗎?
答:不一定,睡眠結束后線程進入就緒狀態(tài),需要搶奪到執(zhí)行權之后才會繼續(xù)執(zhí)行。