多线程 java 实例_Java多线程实例学习
1. Java多線程的就緒、運行和死亡狀態(tài)
就緒狀態(tài)轉換為運行狀態(tài):當此線程得到處理器資源;
運行狀態(tài)轉換為就緒狀態(tài):當此線程主動調用yield()方法或在運行過程中失去處理器資源。
運行狀態(tài)轉換為死亡狀態(tài):當此線程線程執(zhí)行體執(zhí)行完畢或發(fā)生了異常。
此處需要特別注意的是:當調用線程的yield()方法時,線程從運行狀態(tài)轉換為就緒狀態(tài),但接下來CPU調度就緒狀態(tài)中的哪個線程具有一定的隨機性,因此,可能會出現(xiàn)A線程調用了
yield()方法后,接下來CPU仍然調度了A線程的情況。
由于實際的業(yè)務需要,常常會遇到需要在特定時機終止某一線程的運行,使其進入到死亡狀態(tài)。目前最通用的做法是設置一boolean型的變量,當條件滿足時,使線程執(zhí)行體快速執(zhí)行完畢。如:
1. 代碼
package test;
public class TestStopThread {
public static void main(String[] args) {
MyThread myThread = new MyThread("MyThread");
Thread thread = new Thread(myThread);
for(int i=0; i<100; i++){
System.out.println(Thread.currentThread().getName() + "-->" + i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(i == 10){
thread.start();
}
if(i == 30){
myThread.stopThread();
}
}
}
}
class MyThread implements Runnable{
boolean stop = false;
private int i = 50;
private String threadName;
public MyThread(String threadName){
this.threadName = threadName;
}
@Override
public void run() {
while (!stop && i>0){
System.out.println(threadName + "-->" + i--);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void stopThread(){
stop = true;
}
}
2. 運行結果:
main-->0
main-->1
main-->2
main-->3
main-->4
main-->5
main-->6
main-->7
main-->8
main-->9
main-->10
main-->11
MyThread-->50
MyThread-->49
main-->12
MyThread-->48
main-->13
MyThread-->47
main-->14
main-->15
MyThread-->46
main-->16
MyThread-->45
MyThread-->44
main-->17
MyThread-->43
main-->18
MyThread-->42
main-->19
MyThread-->41
main-->20
main-->21
MyThread-->40
main-->22
MyThread-->39
MyThread-->38
main-->23
main-->24
MyThread-->37
MyThread-->36
main-->25
main-->26
MyThread-->35
main-->27
MyThread-->34
main-->28
MyThread-->33
MyThread-->32
main-->29
main-->30
MyThread-->31
MyThread-->30
main-->31
main-->32
main-->33
main-->34
main-->35
...
2. Java多線程的阻塞狀態(tài)與線程控制
1. join()
讓一個線程等待另一個線程完成才繼續(xù)執(zhí)行。如A線程線程執(zhí)行體中調用B線程的join()方法,則A線程被阻塞,知道B線程執(zhí)行完為止,A才能得以繼續(xù)執(zhí)行。
(1)代碼實例:
package test;
public class TestJoin {
public static void main(String[] args) {
MyJoin myJoin = new MyJoin();
Thread thread = new Thread(myJoin, "MyJoin");
for(int i=0; i<30; i++){
System.out.println(Thread.currentThread().getName() + "-->" + i);
if(i == 10){
thread.start();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(i == 20){
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class MyJoin implements Runnable{
@Override
public void run() {
for(int i=0; i<20; i++){
System.out.println(Thread.currentThread().getName() + "-->" + i);
}
}
}
(2)運行結果:
main-->0
main-->1
main-->2
main-->3
main-->4
main-->5
main-->6
main-->7
main-->8
main-->9
main-->10
MyJoin-->0
MyJoin-->1
MyJoin-->2
MyJoin-->3
MyJoin-->4
MyJoin-->5
MyJoin-->6
MyJoin-->7
MyJoin-->8
MyJoin-->9
MyJoin-->10
MyJoin-->11
MyJoin-->12
MyJoin-->13
MyJoin-->14
MyJoin-->15
MyJoin-->16
MyJoin-->17
MyJoin-->18
MyJoin-->19
main-->11
main-->12
main-->13
main-->14
main-->15
main-->16
main-->17
main-->18
main-->19
main-->20
main-->21
main-->22
main-->23
main-->24
main-->25
main-->26
main-->27
main-->28
main-->29
可以看到,當main線程中join 了MyJoin線程后,會暫停main線程,直到MyJoin線程執(zhí)行完畢再執(zhí)行main,這就是join方法,說簡單點,就是A線程中調用B線程join方法,此時,A線程暫停執(zhí)行(阻塞),而B線程執(zhí)行,直到B線程執(zhí)行完畢后A線程繼續(xù)執(zhí)行
2. sleep()
sleep —— 讓當前的正在執(zhí)行的線程暫停指定的時間,并進入阻塞狀態(tài)。在其睡眠的時間段內,該線程由于不是處于就緒狀態(tài),因此不會得到執(zhí)行的機會。即使此時系統(tǒng)中沒有任何其他可執(zhí)行的線程,出于sleep()中的線程也不會執(zhí)行。因此sleep()方法常用來暫停線程執(zhí)行。
前面有講到,當調用了新建的線程的start()方法后,線程進入到就緒狀態(tài),可能會在接下來的某個時間獲取CPU時間片得以執(zhí)行,如果希望這個新線程盡快執(zhí)行,直接調用原來線程的sleep(1)即可。
(1)代碼實例:
package test;
public class TestSleep {
public static void main(String[] args) {
for(int i=0; i<20; i++){
System.out.println(Thread.currentThread().getName() + "-->" + i);
if(i == 10){
new Thread(new MySleep(), "MySleep").start();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class MySleep implements Runnable{
@Override
public void run() {
for(int i=0; i<20; i++){
System.out.println(Thread.currentThread().getName() + "-->" + i);
}
}
}
(2)運行結果:
main-->0
main-->1
main-->2
main-->3
main-->4
main-->5
main-->6
main-->7
main-->8
main-->9
main-->10
MySleep-->0
MySleep-->1
MySleep-->2
MySleep-->3
MySleep-->4
MySleep-->5
MySleep-->6
MySleep-->7
MySleep-->8
MySleep-->9
MySleep-->10
MySleep-->11
MySleep-->12
MySleep-->13
MySleep-->14
MySleep-->15
MySleep-->16
MySleep-->17
MySleep-->18
MySleep-->19
main-->11
main-->12
main-->13
main-->14
main-->15
main-->16
main-->17
main-->18
main-->19
3. 后臺線程(Daemon Thread)
后臺線程主要是為其他線程(相對可以稱之為前臺線程)提供服務,或“守護線程”。如JVM中的垃圾回收線程。
生命周期:后臺線程的生命周期與前臺線程生命周期有一定關聯(lián)。主要體現(xiàn)在:當所有的前臺線程都進入死亡狀態(tài)時,后臺線程會自動死亡(其實這個也很好理解,因為后臺線程存在的目的在于為前臺線程服務的,既然所有的前臺線程都死亡了,那它自己還留著有什么用)。
設置后臺線程:調用Thread對象的setDaemon(true)方法可以將指定的線程設置為后臺線程。
(1)代碼實例:
package test;
public class TestDeamon {
public static void main(String[] args) {
MyDeamon myDeamon = new MyDeamon();
Thread thread = new Thread(myDeamon, "MyDeamon");
for(int i=0; i<20; i++){
System.out.println(Thread.currentThread().getName() + "-->" + i);
if(i == 5){
thread.setDaemon(true);
thread.start();
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(i >= 19){
System.out.println("main線程執(zhí)行完畢");
}
}
}
}
class MyDeamon implements Runnable{
@Override
public void run() {
System.out.println("MyDeamon線程開始執(zhí)行");
for(int i=0; i<100; i++){
System.out.println(Thread.currentThread().getName() + "-->" + i);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("MyDeamon線程執(zhí)行完畢");
}
}
(2)運行結果:
main-->0
main-->1
main-->2
main-->3
main-->4
main-->5
main-->6
MyDeamon線程開始執(zhí)行
main-->7
main-->8
main-->9
main-->10
main-->11
main-->12
main-->13
main-->14
main-->15
main-->16
main-->17
main-->18
main-->19
main線程執(zhí)行完畢
MyDeamon-->0
判斷線程是否是后臺線程:調用thread對象的isDeamon()方法。
注:main線程默認是前臺線程,前臺線程創(chuàng)建中創(chuàng)建的子線程默認是前臺線程,后臺線程中創(chuàng)建的線程默認是后臺線程。調用setDeamon(true)方法將前臺線程設置為后臺線程時,需要在start()方法調用之前。前天線程都死亡后,JVM通知后臺線程死亡,但從接收指令到作出響應,需要一定的時間,前臺線程main執(zhí)行完畢后,后臺線程收到指令結束線程,由于有一定的響應時間,所以執(zhí)行了一段代碼MyDeamon-->0
4. 改變線程的優(yōu)先級/setPriority()
每個線程在執(zhí)行時都具有一定的優(yōu)先級,優(yōu)先級高的線程具有較多的執(zhí)行機會。每個線程默認的優(yōu)先級都與創(chuàng)建它的線程的優(yōu)先級相同。main線程默認具有普通優(yōu)先級。
設置線程優(yōu)先級:setPriority(int priorityLevel)。參數(shù)priorityLevel范圍在1-10之間,常用的有如下三個靜態(tài)常量值:
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5
注:具有較高線程優(yōu)先級的線程對象僅表示此線程具有較多的執(zhí)行機會,而非優(yōu)先執(zhí)行。
(1)代碼實例:
package test;
public class TestPriority {
public static void main(String[] args) {
MyPriority myPriority = new MyPriority();
Thread thread = new Thread(myPriority, "MyPriority");
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
for(int i=0; i<20; i++){
System.out.println(Thread.currentThread().getName() + "-->" + i);
}
}
}
class MyPriority implements Runnable{
@Override
public void run() {
for(int i=0; i<20; i++){
System.out.println(Thread.currentThread().getName() + "-->" + i);
}
}
}
(2)運行結果1:
MyPriority-->0
MyPriority-->1
MyPriority-->2
MyPriority-->3
MyPriority-->4
MyPriority-->5
MyPriority-->6
MyPriority-->7
MyPriority-->8
MyPriority-->9
MyPriority-->10
MyPriority-->11
MyPriority-->12
MyPriority-->13
MyPriority-->14
MyPriority-->15
MyPriority-->16
MyPriority-->17
MyPriority-->18
MyPriority-->19
main-->0
main-->1
main-->2
main-->3
main-->4
main-->5
main-->6
main-->7
main-->8
main-->9
main-->10
main-->11
main-->12
main-->13
main-->14
main-->15
main-->16
main-->17
main-->18
main-->19
運行結果2:
main-->0
MyPriority-->0
MyPriority-->1
MyPriority-->2
MyPriority-->3
MyPriority-->4
MyPriority-->5
MyPriority-->6
MyPriority-->7
MyPriority-->8
MyPriority-->9
MyPriority-->10
MyPriority-->11
MyPriority-->12
MyPriority-->13
MyPriority-->14
MyPriority-->15
MyPriority-->16
MyPriority-->17
MyPriority-->18
MyPriority-->19
main-->1
main-->2
main-->3
main-->4
main-->5
main-->6
main-->7
main-->8
main-->9
main-->10
main-->11
main-->12
main-->13
main-->14
main-->15
main-->16
main-->17
main-->18
main-->19
可以看到,當設置了線程優(yōu)先級后,優(yōu)先級高的具有較多的執(zhí)行機會,但是并不一定優(yōu)先執(zhí)行,運行結果1和2有些許不同,1中MyPriority線程先執(zhí)行,2中main線程先執(zhí)行了一段時間,后讓給優(yōu)先級高的MyPriority線程,可見,證實了"具有較高線程優(yōu)先級的線程對象僅表示此線程具有較多的執(zhí)行機會,而非優(yōu)先執(zhí)行。"
5. 線程讓步:yield()
使當前線程從執(zhí)行狀態(tài)(運行狀態(tài))變?yōu)榭蓤?zhí)行態(tài)(就緒狀態(tài))。cpu會從眾多的可執(zhí)行態(tài)里選擇,也就是說,當前也就是剛剛的那個線程還是有可能會被再次執(zhí)行到的,并不是說一定會執(zhí)行其他線程而該線程在下一次中不會執(zhí)行到了。調用yield方法后,該線程就會把CPU時間讓掉,讓其他或者自己的線程執(zhí)行(也就是誰先搶到誰執(zhí)行)
同時,yield()方法還與線程優(yōu)先級有關,當某個線程調用yiled()方法從運行狀態(tài)轉換到就緒狀態(tài)后,CPU從就緒狀態(tài)線程隊列中只會選擇與該線程優(yōu)先級相同或優(yōu)先級更高的線程去執(zhí)行。
(1)代碼實例:
package test;
public class TestYield {
public static void main(String[] args) {
MyYield1 myYield1 = new MyYield1();
MyYield2 myYield2 = new MyYield2();
Thread thread1 = new Thread(myYield1, "MyYield1");
Thread thread2 = new Thread(myYield2, "MyYield2");
thread1.setPriority(Thread.MAX_PRIORITY);
thread2.setPriority(Thread.MIN_PRIORITY);
thread1.start();
thread2.start();
for(int i=0; i<50; i++){
System.out.println(Thread.currentThread().getName() + "-->" + i);
if(i == 10){
Thread.yield();
}
}
}
}
class MyYield1 implements Runnable{
@Override
public void run() {
for(int i=0; i<30; i++){
System.out.println(Thread.currentThread().getName() + "-->" + i);
}
}
}
class MyYield2 implements Runnable{
@Override
public void run() {
for(int i=0; i<30; i++){
System.out.println(Thread.currentThread().getName() + "-->" + i);
}
}
}
(2)運行結果:
main-->0
main-->1
main-->2
MyYield1-->0
MyYield1-->1
MyYield1-->2
MyYield1-->3
MyYield1-->4
MyYield1-->5
MyYield1-->6
MyYield1-->7
MyYield1-->8
MyYield1-->9
MyYield1-->10
MyYield1-->11
MyYield1-->12
MyYield1-->13
MyYield1-->14
MyYield1-->15
MyYield1-->16
MyYield1-->17
MyYield1-->18
MyYield1-->19
MyYield1-->20
MyYield1-->21
MyYield1-->22
MyYield1-->23
MyYield1-->24
MyYield1-->25
MyYield1-->26
MyYield1-->27
MyYield1-->28
MyYield1-->29
main-->3
main-->4
main-->5
main-->6
main-->7
main-->8
main-->9
main-->10
main-->11
main-->12
main-->13
main-->14
main-->15
main-->16
main-->17
main-->18
main-->19
main-->20
main-->21
main-->22
main-->23
main-->24
main-->25
main-->26
main-->27
main-->28
main-->29
main-->30
main-->31
main-->32
main-->33
main-->34
main-->35
main-->36
main-->37
main-->38
main-->39
main-->40
main-->41
main-->42
main-->43
main-->44
main-->45
main-->46
main-->47
main-->48
main-->49
MyYield2-->0
MyYield2-->1
MyYield2-->2
MyYield2-->3
MyYield2-->4
MyYield2-->5
MyYield2-->6
MyYield2-->7
MyYield2-->8
MyYield2-->9
MyYield2-->10
MyYield2-->11
MyYield2-->12
MyYield2-->13
MyYield2-->14
MyYield2-->15
MyYield2-->16
MyYield2-->17
MyYield2-->18
MyYield2-->19
MyYield2-->20
MyYield2-->21
MyYield2-->22
MyYield2-->23
MyYield2-->24
MyYield2-->25
MyYield2-->26
MyYield2-->27
MyYield2-->28
MyYield2-->29
總結
以上是生活随笔為你收集整理的多线程 java 实例_Java多线程实例学习的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 任正非重申不造车!赛力斯回应:华为合作模
- 下一篇: 极米h2怎么手机投屏