日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

java main是多线程的吗_Java多线程之线程及其常用方法

發布時間:2024/10/12 java 74 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java main是多线程的吗_Java多线程之线程及其常用方法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

創建線程和常用方法

進程與線程的概念進程

進程是程序執行時的一個實例。程序運行時系統就會創建一個進程,并為它分配資源,然后把該進程放入進程就緒隊列,進程調度器選中它的時候就會為它分配CPU時間,程序開始真正運行。線程

線程是一條執行路徑,是程序執行時的最小單位,它是進程的一個執行流,是CPU調度和分派的基本單位,一個進程可以由很多個線程組成,線程間共享進程的所有資源,每個線程有自己的堆棧和局部變量。線程由CPU獨立調度執行,在多CPU環境下就允許多個線程同時(并發)運行。

一個正在運行的軟件(如迅雷)就是一個進程,一個進程可以同時運行多個任務( 迅雷軟件可以同時下載多個文件,每個下載任務就是一個線程), 可以簡單的認為進程是線程的集合。

為什么要使用多線程

多線程可以提高程序的效率。

實際生活案例:村長要求喜洋洋在一個小時內打100桶水,可以喜洋洋一個小時只能打25桶水,如果這樣就需要4個小時才能完成任務,為了在一個小時能夠完成,喜洋洋就請美洋洋、懶洋洋、沸洋洋,來幫忙,這樣4只羊同時干活,在一小時內完成了任務。原本用4個小時完成的任務現在只需要1個小時就完成了,如果把每只羊看做一個線程,多只羊即多線程可以提高程序的效率。

并發編程的概念順序編程public class Main {

// 順序編程:當吃飯吃不完的時候,是不能喝酒的,只能吃完晚才能喝酒

public static void main(String[] args) throws Exception {

// 先吃飯再喝酒

eat();

drink();

}

private static void eat() throws Exception {

System.out.println("開始吃飯...\t" + new Date());

Thread.sleep(5000);

System.out.println("結束吃飯...\t" + new Date());

}

private static void drink() throws Exception {

System.out.println("開始喝酒...\t" + new Date());

Thread.sleep(5000);

System.out.println("結束喝酒...\t" + new Date());

}

}

并發編程public class Main {

public static void main(String[] args) {

// 一邊吃飯一邊喝酒

new EatThread().start();

new DrinkThread().start();

}

}

class EatThread extends Thread{

@Override

public void run() {

System.out.println("開始吃飯?...\t" + new Date());

try {

Thread.sleep(5000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("結束吃飯?...\t" + new Date());

}

}

class DrinkThread extends Thread {

@Override

public void run() {

System.out.println("開始喝酒??...\t" + new Date());

try {

Thread.sleep(5000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("結束喝酒?...\t" + new Date());

}

}

并發編程,一邊吃飯一邊喝酒總共用時5秒,比順序編程更快,因為并發編程可以同時運行,而不必等前面的代碼運行完之后才允許后面的代碼。

同一個時刻一個CPU只能做一件事情,即同一時刻只能一個線程中的部分代碼,假如有兩個線程,Thread-0和Thread-1,剛開始CPU說Thread-0你先執行,給你3毫秒時間,Thread-0執行了3毫秒時間,但是沒有執行完,此時CPU會暫停Thread-0執行并記錄Thread-0執行到哪行代碼了,當時的變量的值是多少,然后CPU說Thread-1你可以執行了,給你2毫秒的時間,Thread-1執行了2毫秒也沒執行完,此時CPU會暫停Thread-1執行并記錄Thread-1執行到哪行代碼了,當時的變量的值是多少,此時CPU又說Thread-0又該你,這次我給你5毫秒時間,去執行吧,此時CPU就找出上次Thread-0線程執行到哪行代碼了,當時的變量值是多少,然后接著上次繼續執行,結果用了2毫秒就Thread-0就執行完了,就終止了,然后CPU說Thread-1又輪到你,這次給你4毫秒,同樣CPU也會先找出上次Thread-1線程執行到哪行代碼了,當時的變量值是多少,然后接著上次繼續開始執行,結果Thread-1在4毫秒內也執行結束了,Thread-1也結束了終止了。CPU在來回改變線程的執行機會稱之為線程上下文切換。

線程的狀態

創建(new)狀態: 準備好了一個多線程的對象,即執行了new Thread(); 創建完成后就需要為線程分配內存

就緒(runnable)狀態: 調用了start()方法, 等待CPU進行調度

運行(running)狀態: 執行run()方法

阻塞(blocked)狀態: 暫時停止執行線程,將線程掛起(sleep()、wait()、join()、沒有獲取到鎖都會使線程阻塞), 可能將資源交給其它線程使用

死亡(terminated)狀態: 線程銷毀(正常執行完畢、發生異常或者被打斷interrupt()都會導致線程終止)

創建線程的方式

1.子類繼承Thread類,重寫Thread類run方法,new Thread子類創建線程對象,調用線程對象的start()方法。public class Main {

public static void main(String[] args) {

new MyThread().start();

}

}

class MyThread extends Thread {

@Override

public void run() {

System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId());

}

}

2.實現類實現Runnable接口,重寫Runnable接口run()方法,實例化實現類并將此實例作為Thread類的target來創建線程對象,調用線程對象的start()方法。public class Main {

public static void main(String[] args) {

// 將Runnable實現類作為Thread的構造參數傳遞到Thread類中,然后啟動Thread類

MyRunnable runnable = new MyRunnable();

new Thread(runnable).start();

}

}

class MyRunnable implements Runnable {

@Override

public void run() {

System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId());

}

}

3.實現類實現Callable接口,重寫Callable帶有返回值的call()方法,用FutureTask包裝Callable實現類的實例,將FutureTask的實例作為Thread類的target來創建線程對象,調用線程對象的start()方法public class Main {

public static void main(String[] args) throws Exception {

// 將Callable包裝成FutureTask,FutureTask也是一種Runnable

MyCallable callable = new MyCallable();

FutureTask futureTask = new FutureTask<>(callable);

new Thread(futureTask).start();

// get方法會阻塞調用的線程

Integer sum = futureTask.get();

System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() + "=" + sum);

}

}

class MyCallable implements Callable {

@Override

public Integer call() throws Exception {

System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId() + "\t" + new Date() + " \tstarting...");

int sum = 0;

for (int i = 0; i <= 100000; i++) {

sum += i;

}

Thread.sleep(5000);

System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId() + "\t" + new Date() + " \tover...");

return sum;

}

}

三種方式比較:

1.Thread: 繼承方式, 不建議使用, 因為Java是單繼承的,繼承了Thread就沒辦法繼承其它類了,不夠靈活

2.Runnable: 實現接口,比Thread類更加靈活,沒有單繼承的限制

3.Callable: Thread和Runnable都是重寫的run()方法并且沒有返回值,Callable是重寫的call()方法并且有返回值并可以借助FutureTask類來判斷線程是否已經執行完畢或者取消線程執行

4.當線程不需要返回值時使用Runnable,需要返回值時就使用Callable

常用方法

1.Thread.currentThread()public static void main(String[] args) {

Thread thread = Thread.currentThread();

// 線程名稱

String name = thread.getName();

// 線程id

long id = thread.getId();

// 線程已經啟動且尚未終止

// 線程處于正在運行或準備開始運行的狀態,就認為線程是“存活”的

boolean alive = thread.isAlive();

// 線程優先級

int priority = thread.getPriority();

// 是否守護線程

boolean daemon = thread.isDaemon();

// Thread[name=main,id=1,alive=true,priority=5,daemon=false]

System.out.println("Thread[name=" + name + ",id=" + id + ",alive=" + alive + ",priority=" + priority + ",daemon=" + daemon + "]");

}

2.sleep() 與 interrupt()

sleep(): 睡眠指定時間,即讓程序暫停指定時間運行,時間到了會繼續執行代碼,如果時間未到就要醒需要使用interrupt()來隨時喚醒

interrupt(): 喚醒正在睡眠的程序,調用interrupt()方法,會使得sleep()方法拋出InterruptedException異常,當sleep()方法拋出異常就中斷了sleep的方法,從而讓程序繼續運行下去public static void main(String[] args) throws Exception {

Thread thread0 = new Thread(()-> {

try {

System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t太困了,讓我睡10秒,中間有事叫我,zZZ。。。");

Thread.sleep(10000);

} catch (InterruptedException e) {

System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t被叫醒了,又要繼續干活了");

}

});

thread0.start();

// 這里睡眠只是為了保證先讓上面的那個線程先執行

Thread.sleep(2000);

new Thread(()-> {

System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t醒醒,醒醒,別睡了,起來干活了!!!");

// 無需獲取鎖就可以調用interrupt

thread0.interrupt();

}).start();

}

3.wait() 與 notify()

wait、notify和notifyAll方法是Object類的final方法,這些方法不能被子類重寫。因此在程序中可以通過this或者super來調this.wait(), super.wait()。

wait(): 導致線程進入等待阻塞狀態,會一直等待直到它被其他線程通過notify()或者notifyAll喚醒。該方法只能在同步方法或同步塊內部調用。如果當前線程不是鎖的持有者,該方法拋出一個IllegalMonitorStateException異常。wait(long timeout): 時間到了自動執行,類似于sleep(long millis)

notify(): 該方法只能在同步方法或同步塊內部調用, 隨機選擇一個(注意:只會通知一個)在該對象上調用wait方法的線程,解除其阻塞狀態

notifyAll(): 喚醒所有的wait對象

wait()是讓程序暫停執行,線程進入當前實例的等待隊列,這個隊列屬于該實例對象,所以調用notify也必須使用該對象來調用,不能使用別的對象來調用。調用wait和notify必須使用同一個對象來調用。

注意:Object.wait()和Object.notify()和Object.notifyall()必須寫在synchronized方法內部或者synchronized塊內部

讓哪個對象等待wait就去通知notify哪個對象,不要讓A對象等待,結果卻去通知B對象,要操作同一個對象public class WaitNotifyTest {

public static void main(String[] args) throws Exception {

WaitNotifyTest waitNotifyTest = new WaitNotifyTest();

new Thread(() -> {

try {

waitNotifyTest.printFile();

} catch (InterruptedException e) {

e.printStackTrace();

}

}).start();

new Thread(() -> {

try {

waitNotifyTest.printFile();

} catch (InterruptedException e) {

e.printStackTrace();

}

}).start();

new Thread(() -> {

try {

System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t睡覺1秒中,目的是讓上面的線程先執行,即先執行wait()");

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

waitNotifyTest.notifyPrint();

}).start();

}

private synchronized void printFile() throws InterruptedException {

System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t等待打印文件...");

this.wait();

System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t打印結束。。。");

}

private synchronized void notifyPrint() {

this.notify();

System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t通知完成...");

}

}

notifyAll()

4.sleep() 與 wait()

Thread.sleep(long millis): 睡眠時不會釋放鎖package com.example.demo;

import java.util.Date;

public class Main {

public static void main(String[] args) throws InterruptedException {

Object lock = new Object();

new Thread(() -> {

synchronized (lock) {

for (int i = 0; i < 5; i++) {

System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t" + i);

try { Thread.sleep(1000); } catch (InterruptedException e) { }

}

}

}).start();

Thread.sleep(1000);

new Thread(() -> {

synchronized (lock) {

for (int i = 0; i < 5; i++) {

System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t" + i);

}

}

}).start();

}

}

因main方法中Thread.sleep(1000)所以上面的線程Thread-0先被執行,當循環第一次時就會Thread.sleep(1000)睡眠,因為sleep并不會釋放鎖,所以Thread-1得不到執行的機會,所以直到Thread-0執行完畢釋放鎖對象lock,Thread-1才能拿到鎖,然后執行Thread-1;

object.wait(long timeout): 會釋放鎖public class Main {

public static void main(String[] args) throws InterruptedException {

Object lock = new Object();

new Thread(() -> {

synchronized (object) {

System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t等待打印文件...");

try {

object.wait(5000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t打印結束。。。");

}

}).start();

// 先上面的線程先執行

Thread.sleep(1000);

new Thread(() -> {

synchronized (object) {

for (int i = 0; i < 5; i++) {

System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "\t" + i);

}

}

}).start();

}

}

sleep與wait的區別

sleep在Thread類中,wait在Object類中

sleep不會釋放鎖,wait會釋放鎖

sleep使用interrupt()來喚醒,wait需要notify或者notifyAll來通知

5.join()public class JoinTest {

public static void main(String[] args) {

new Thread(new ParentRunnable()).start();

}

}

class ParentRunnable implements Runnable {

@Override

public void run() {

// 線程處于new狀態

Thread childThread = new Thread(new ChildRunable());

// 線程處于runnable就緒狀態

childThread.start();

try {

// 當調用join時,parent會等待child執行完畢后再繼續運行

// 將某個線程加入到當前線程

childThread.join();

} catch (InterruptedException e) {

e.printStackTrace();

}

for (int i = 0; i < 5; i++) {

System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "父線程 running");

}

}

}

class ChildRunable implements Runnable {

@Override

public void run() {

for (int i = 0; i < 5; i++) {

try { Thread.sleep(1000); } catch (InterruptedException e) {}

System.out.println(new Date() + "\t" + Thread.currentThread().getName() + "子線程 running");

}

}

}

程序進入主線程,運行Parent對應的線程,Parent的線程代碼分兩段,一段是啟動一個子線程,一段是Parent線程的線程體代碼,首先會將Child線程加入到Parent線程,join()方法會調用join(0)方法(join()方法是普通方法并沒有加鎖,join(0)會加鎖),join(0)會執行while(isAlive()) { wait(0);} 循環判斷線程是否處于活動狀態,如果是繼續wait(0)知道isAlive=false結束掉join(0), 從而結束掉join(), 最后回到Parent線程體中繼續執行其它代碼。

6.yield()

交出CPU的執行時間,不會釋放鎖,讓線程進入就緒狀態,等待重新獲取CPU執行時間,yield就像一個好人似的,當CPU輪到它了,它卻說我先不急,先給其他線程執行吧, 此方法很少被使用到public class Main {

public static void main(String[] args) {

new Thread(new Runnable() {

int sum = 0;

@Override

public void run() {

long beginTime=System.currentTimeMillis();

for (int i = 0; i < 99999; i++) {

sum += 1;

// 去掉該行執行用2毫秒,加上271毫秒

Thread.yield();

}

long endTime=System.currentTimeMillis();

System.out.println("用時:"+ (endTime - beginTime) + " 毫秒!");

}

}).start();

}

}

sleep(long millis) 與 yeid()

sleep(long millis): 需要指定具體睡眠的時間,不會釋放鎖,睡眠期間CPU會執行其它線程,睡眠時間到會立刻執行

yeid(): 交出CPU的執行權,不會釋放鎖,和sleep不同的時當再次獲取到CPU的執行,不能確定是什么時候,而sleep是能確定什么時候再次執行。兩者的區別就是sleep后再次執行的時間能確定,而yeid是不能確定的

yield會把CPU的執行權交出去,所以可以用yield來控制線程的執行速度,當一個線程執行的比較快,此時想讓它執行的稍微慢一些可以使用該方法,想讓線程變慢可以使用sleep和wait,但是這兩個方法都需要指定具體時間,而yield不需要指定具體時間,讓CPU決定什么時候能再次被執行,當放棄到下次再次被執行的中間時間就是間歇等待的時間

7.setDaemon(boolean on)

線程分兩種:

1.用戶線程:如果主線程main停止掉,不會影響用戶線程,用戶線程可以繼續運行。

2.守護線程:如果主線程死亡,守護線程如果沒有執行完畢也要跟著一塊死(就像皇上死了,帶刀侍衛也要一塊死),GC垃圾回收線程就是守護線程public class Main {

public static void main(String[] args) {

Thread thread = new Thread() {

@Override

public void run() {

IntStream.range(0, 5).forEach(i -> {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName() + "\ti=" + i);

});

}

};

thread.start();

for (int i = 0; i < 2; i++) {

System.out.println(Thread.currentThread().getName() + "\ti=" + i);

}

System.out.println("主線程執行結束,子線程仍然繼續執行,主線程和用戶線程的生命周期各自獨立。");

}

}

public class Main {

public static void main(String[] args) {

Thread thread = new Thread() {

@Override

public void run() {

IntStream.range(0, 5).forEach(i -> {

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName() + "\ti=" + i);

});

}

};

thread.setDaemon(true);

thread.start();

for (int i = 0; i < 2; i++) {

System.out.println(Thread.currentThread().getName() + "\ti=" + i);

}

System.out.println("主線程死亡,子線程也要陪著一塊死!");

}

}

總結

以上是生活随笔為你收集整理的java main是多线程的吗_Java多线程之线程及其常用方法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。