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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java学习笔记5-1——多线程

發(fā)布時間:2023/12/10 java 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java学习笔记5-1——多线程 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

  • 前言
  • 核心概念
  • 線程創(chuàng)建
    • 繼承Thread類
    • 實現(xiàn)Runnable接口
    • 上述兩個方法小結(jié)
    • 實現(xiàn)Callable接口
  • 并發(fā)問題簡介
  • 靜態(tài)代理模式
  • 線程狀態(tài)
    • 線程停止(stop)
    • 線程休眠(sleep)
    • 線程禮讓(yield)
    • 線程強制執(zhí)行(join)
    • 線程狀態(tài)觀測
  • 線程優(yōu)先級
  • 守護線程
  • 后續(xù)內(nèi)容見《Java學(xué)習(xí)筆記5-2》

前言

Process與Thread:

  • 程序(programm)是指令和數(shù)據(jù)的有序集合,其本身沒有任何運行的含義,是一個靜態(tài)的概念。
  • 進程(process)是執(zhí)行程序的一次執(zhí)行過程,它是一個動態(tài)的概念。是系統(tǒng)資源分配的單位。
  • 通常在一個進程中可以包含若干個線程(thread),當(dāng)然一個進程中至少有一個線程,不然沒有存在的意義。線程是CPU調(diào)度和執(zhí)行的單位。

很多多線程是模擬出來的,真正的多線程是指有多個CPU。

核心概念

  • 線程就是獨立的執(zhí)行路徑
  • 在程序運行時,即使沒有自己創(chuàng)建線程,后臺也會有多個線程,如主線程,gc線程
  • main()稱之為主線程,為系統(tǒng)的入口,用于執(zhí)行整個程序
  • 在一個進程中,如果開辟了多個線程,線程的運行由調(diào)度器安排調(diào)度,調(diào)度器是與操作系統(tǒng)緊密相關(guān)的,先后順序是不能人為干預(yù)的。
  • 對同一份資源操作時,會存在資源搶奪的問題,需要加入并發(fā)控制
  • 線程會帶來額外的開銷,如cpu調(diào)度時間,并發(fā)控制開銷
  • 每個線程在自己的工作內(nèi)存交互,內(nèi)存控制不當(dāng)會造成數(shù)據(jù)不一致

線程創(chuàng)建

三種創(chuàng)建線程的方式:

  • 繼承Thread類【重點】
  • 實現(xiàn)Runnable接口【重點】
  • 實現(xiàn)Callable接口
  • 繼承Thread類

    創(chuàng)建線程方式1:繼承Thread類,重寫run()方法,調(diào)用start開啟線程。

    • public class Thread extends Object implements Runnable

    • 構(gòu)造方法:
      Thread(Runnable target) 分配一個新的 Thread對象。
      Thread(Runnable target, String name) 分配一個新的 Thread對象,具有指定的name作為其名稱

    • 常用方法:
      void start() 使此線程開始執(zhí)行; JVM調(diào)用此線程的run方法。

    簡單例子:

    //創(chuàng)建線程方式1:繼承Thread類,重寫run()方法,調(diào)用start開啟線程 //總結(jié):注意線程開啟不一定立即執(zhí)行,由CPU調(diào)度執(zhí)行 public class ThreadTest1 extends Thread{@Overridepublic void run() {//run方法線程的執(zhí)行體for (int i = 0; i < 200; i++) {System.out.println("我在學(xué)線程---"+i);}}public static void main(String[] args) {//main線程,主線程//創(chuàng)建一個線程對象ThreadTest1 tt = new ThreadTest1();//調(diào)用start()tt.start();for (int i = 0; i < 1000; i++) {System.out.println("(主)我在看代碼---"+i);}} }

    網(wǎng)圖下載例子:

    //練習(xí)Thread,實現(xiàn)多線程同步下載圖片 public class TestThread2 extends Thread{private String url;//網(wǎng)絡(luò)圖片地址private String name;//保存的文件名public TestThread2(String url,String name){this.url = url;this.name = name;}// 下載圖片線程的執(zhí)行體@Overridepublic void run() {WebDownloader webDownloader = new WebDownloader();webDownloader.downloader(url,name);System.out.println("下載了文件名為"+name+"的文件");}public static void main(String[] args) {TestThread2 t1 = new TestThread2("https://www.kuangstudy.com/assert/course/c1/01.jpg","圖片1.jpg");TestThread2 t2 = new TestThread2("https://www.kuangstudy.com/assert/course/c1/02.jpg","圖片2.jpg");TestThread2 t3 = new TestThread2("https://www.kuangstudy.com/assert/course/c1/03.jpg","圖片3.jpg");t1.start();t2.start();t3.start();} } //下載器 class WebDownloader{//下載方法public void downloader(String url,String name){try {FileUtils.copyURLToFile(new URL(url),new File(name));} catch (IOException e) {e.printStackTrace();System.out.println("IO異常,Downloader出現(xiàn)問題");}} }

    實現(xiàn)Runnable接口

    創(chuàng)建線程方式2:實現(xiàn)Runnable接口,重寫run方法,執(zhí)行線程需要丟入runnable接口實現(xiàn)類,調(diào)用start方法。

    • public interface Runnable:
      Runnable接口應(yīng)由任何類實現(xiàn),其實例將由線程執(zhí)行。 該類必須定義一個無參數(shù)的方法,稱為run 。

    簡單例子:

    //創(chuàng)建線程方式2:實現(xiàn)Runnable接口,重寫run方法,執(zhí)行線程需要丟入runnable接口實現(xiàn)類,調(diào)用start方法 public class ThreadTest3 implements Runnable{@Overridepublic void run() {//run方法線程體for (int i = 0; i < 200; i++) {System.out.println("我在學(xué)線程---"+i);}}public static void main(String[] args) {//創(chuàng)建Runnable接口的實現(xiàn)類對象ThreadTest3 tt = new ThreadTest3();//創(chuàng)建線程對象,通過線程對象來開啟我們的線程,代理new Thread(tt).start();for (int i = 0; i < 1000; i++) {System.out.println("(主)我在看代碼---"+i);}} }

    上述兩個方法小結(jié)

    繼承Thread類:

    • 子類繼承Thread類具備多線程能力
    • 啟動線程:子類對象.start()
    • 不建議使用。為了避免面向?qū)ο髥卫^承局限性

    實現(xiàn)Runnable接口:

    • 實現(xiàn)接口Runnable具有多線程能力
    • 啟動線程:傳入目標對象+Thread對象.start()
    • 推薦使用。能避免單繼承局限性,靈活方便,方便同一對象被多個線程使用。 例如:
    //一份資源 ThreadTest tt = new ThreadTest();//多個代理 new Thread(tt,"張三").start(); new Thread(tt,"李四").start(); new Thread(tt,"你好").start();

    代碼例子(龜兔賽跑)作為總結(jié):

    //模擬龜兔賽跑 public class Race implements Runnable{//勝利者private static String winner;@Overridepublic void run() {for (int i = 0; i <= 100; i++) {//模擬兔子睡覺if (Thread.currentThread().getName().equals("兔子") && i%10==0){try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}//判斷比賽是否結(jié)束boolean flag = gameOver(i);if (flag){break;}System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");}}//判斷是否完成比賽private boolean gameOver(int steps){//判斷是否有勝利者if (winner!=null){//已經(jīng)存在勝利者return true;}{if (steps>=100){winner=Thread.currentThread().getName();System.out.println("winner is"+winner);return true;}}return false;}public static void main(String[] args) {Race race = new Race();new Thread(race,"烏龜").start();new Thread(race,"兔子").start();} }

    實現(xiàn)Callable接口

  • 實現(xiàn)Callable接口,需要返回值類型
  • 重寫call方法,需要拋出異常
  • 創(chuàng)建目標對象
  • 創(chuàng)建執(zhí)行服務(wù):ExecutorService ser = Executors.newFixedThreadPool(1);
  • 提交執(zhí)行:Future\<Boolean\> result1 = ser.submit(t1);
  • 獲取結(jié)果:boolean r1 = result1.get();
  • 關(guān)閉服務(wù):ser.shutdownNow();
  • 網(wǎng)圖下載例子:

    //線程創(chuàng)建方式3:實現(xiàn)Callable接口 //Callable的好處:1.可以定義返回值;2.可以拋出異常public class CallableTest implements Callable<Boolean> {private String url;//網(wǎng)絡(luò)圖片地址private String name;//保存的文件名public CallableTest(String url,String name){this.url = url;this.name = name;}// 下載圖片線程的執(zhí)行體@Overridepublic Boolean call() {WebDownloader1 webDownloader = new WebDownloader1();webDownloader.downloader(url,name);System.out.println("下載了文件名為"+name+"的文件");return true;}public static void main(String[] args) throws ExecutionException, InterruptedException {CallableTest ct1 = new CallableTest("https://www.kuangstudy.com/assert/course/c1/01.jpg","c圖片1.jpg");CallableTest ct2 = new CallableTest("https://www.kuangstudy.com/assert/course/c1/02.jpg","c圖片2.jpg");CallableTest ct3 = new CallableTest("https://www.kuangstudy.com/assert/course/c1/03.jpg","c圖片3.jpg");// 創(chuàng)建執(zhí)行對象ExecutorService ser = Executors.newFixedThreadPool(3);// 提交執(zhí)行:Future<Boolean> r1 = ser.submit(ct1);Future<Boolean> r2 = ser.submit(ct2);Future<Boolean> r3 = ser.submit(ct3);// 獲取結(jié)果:boolean rs1 = r1.get();boolean rs2 = r2.get();boolean rs3 = r3.get();System.out.println(rs1);System.out.println(rs2);System.out.println(rs3);// 關(guān)閉服務(wù)ser.shutdownNow();} } // 下載器 class WebDownloader1{// 下載方法public void downloader(String url,String name){try {FileUtils.copyURLToFile(new URL(url),new File(name));} catch (IOException e) {e.printStackTrace();System.out.println("IO異常,Downloader出現(xiàn)問題");}} }

    并發(fā)問題簡介

    // 多個線程同時操作同一個對象 // 買火車票的例子 public class ThreadTest4 implements Runnable{// 票數(shù)private int ticketNums = 10;@Overridepublic void run() {while (true){if (ticketNums<=0){break;}//模擬延時try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"張票");}}public static void main(String[] args) {ThreadTest4 ticket = new ThreadTest4();new Thread(ticket,"小明").start();new Thread(ticket,"老師").start();new Thread(ticket,"黃牛黨").start();} }

    結(jié)果:

    小明拿到了第10張票
    老師拿到了第8張票
    黃牛黨拿到了第9張票
    黃牛黨拿到了第7張票
    小明拿到了第7張票
    老師拿到了第7張票
    黃牛黨拿到了第6張票

    可見發(fā)生了多個人取到同一張票

    靜態(tài)代理模式

    靜態(tài)代理模式總結(jié):

    • 真實對象和代理對象都要實現(xiàn)同一個接口
    • 代理對象要代理真實角色

    好處:
    1.代理對象可以做很多真實對象做不了的事情
    2.真實對象專注做自己的事情

    public class StaticProxy {public static void main(String[] args) {new Thread( ()-> System.out.println("我愛你") ).start();// Thread也實現(xiàn)了Runnable接口,所以代理了Runnable接口,Thread調(diào)用了start方法(Runnable接口只有run方法,Thread調(diào)用了start方法時虛擬機調(diào)用了run方法),// 相當(dāng)于WeddingCompany代理了Marry,調(diào)用了HappyMarry方法new WeddingCompany(new You()).HappyMarry();;} }interface Marry{void HappyMarry(); }// 真實角色,你。 你去結(jié)婚 class You implements Marry{@Overridepublic void HappyMarry() {System.out.println("結(jié)婚了,好開心");} }// 代理角色,婚慶公司。 幫助你辦婚禮 class WeddingCompany implements Marry{// 代理誰-->真實目標角色private Marry target;public WeddingCompany(Marry target) {this.target = target;}@Overridepublic void HappyMarry() {before();this.target.HappyMarry();//這是真實對象after();}private void before() {System.out.println("結(jié)婚之前,布置現(xiàn)場");}private void after() {System.out.println("辦完婚禮后,付尾款");} }

    線程狀態(tài)

    線程停止(stop)

    • 建議線程正常停止(自己停下來)。即利用次數(shù),不建議死循環(huán)
    • 建議使用標志位作為終止,當(dāng)flag=false,則線程終止運行
    • 不要使用stop或者destroy等過時或不建議使用的方法

    基本步驟:

  • 線程中定義線程體使用的標識
  • 線程體使用該標識
  • 對外提供方法改變標識
  • 例子:

    public class TestStop implements Runnable{// 1.設(shè)置一個標志位private boolean flag = true;@Overridepublic void run() {int i = 0;while (flag){System.out.println("run...Thread"+i++);}}// 2.設(shè)置一個公開的方法停止線程,轉(zhuǎn)換標志位public void stop(){this.flag = false;}public static void main(String[] args) {TestStop testStop = new TestStop();new Thread(testStop).start();for (int i = 0; i < 1000; i++) {System.out.println("main"+i);if (i==900) { // 主線程到i=900的時候才停止線程testStop.stop();System.out.println("線程停止了");}}} }

    輸出:


    main900
    run…Thread662
    線程停止了
    main901

    線程休眠(sleep)

    • sleep(毫秒) 指定當(dāng)前線程阻塞的毫秒數(shù)
    • sleep存在異常InterruptedException
    • sleep時間達到后,線程進入就緒狀態(tài)
    • sleep可以模擬網(wǎng)絡(luò)延時,倒計時等
    • 每一個對象都有一個鎖,sleep不會釋放鎖

    例子1:在前面的了解并發(fā)問題的買票例子中可以加入延時Thread.sleep(100);,從而模擬網(wǎng)絡(luò)延遲,這樣可以放大問題的發(fā)生性。

    例子2:模擬倒計時。

    public class Countdown {public static void main(String[] args) {try {countdown();} catch (InterruptedException e) {e.printStackTrace();}}//模擬倒計時public static void countdown() throws InterruptedException {int num = 10;while (true){Thread.sleep(1000);System.out.println(num--);if (num<=0){break;}}} }

    例子3:打印當(dāng)前系統(tǒng)時間

    public class SystemTime {public static void main(String[] args) throws InterruptedException {//打印當(dāng)前系統(tǒng)時間Date time = new Date(System.currentTimeMillis());//獲取當(dāng)前系統(tǒng)時間while(true){try {Thread.sleep(1000);System.out.println(new SimpleDateFormat("HH:mm:ss").format(time));time = new Date(System.currentTimeMillis());//更新當(dāng)前時間} catch (InterruptedException e) {e.printStackTrace();}}}}

    線程禮讓(yield)

    • 禮讓線程,讓當(dāng)前正在執(zhí)行的線程暫停,但不阻塞
    • 將線程從運行狀態(tài)轉(zhuǎn)為就緒狀態(tài)
    • 注意:讓CPU重新調(diào)度,禮讓不一定成功!看CPU心情
    public class YieldTest {public static void main(String[] args) {MyYield myYield = new MyYield();new Thread(myYield,"A").start();new Thread(myYield,"B").start();} }class MyYield implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"線程開始執(zhí)行");Thread.yield();//禮讓System.out.println(Thread.currentThread().getName()+"線程停止執(zhí)行");} }

    輸出:(禮讓不成功)

    A線程開始執(zhí)行
    A線程停止執(zhí)行
    B線程開始執(zhí)行
    B線程停止執(zhí)行

    輸出:(禮讓成功)

    A線程開始執(zhí)行
    B線程開始執(zhí)行
    A線程停止執(zhí)行
    B線程停止執(zhí)行

    線程強制執(zhí)行(join)

    • join合并線程,待此線程執(zhí)行完成后,再執(zhí)行其他線程,其他線程阻塞(可以想象成插隊)
    public class JoinTest implements Runnable{@Overridepublic void run() {for (int i = 1; i <= 1000; i++) {System.out.println("線程vip來了"+i);}}public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new JoinTest());thread.start();for (int i = 0; i < 500; i++) {if(i==200){thread.join();//主線程跑到200的時候插隊,插隊者跑完1000才繼續(xù)其他的線程,包括主線程}System.out.println("main"+i);}} }

    線程狀態(tài)觀測

    線程狀態(tài)。 線程可以處于以下狀態(tài)之一:

    • NEW 尚未啟動的線程處于此狀態(tài)。
    • RUNNABLE 在Java虛擬機中執(zhí)行的線程處于此狀態(tài)。
    • BLOCKED 被阻塞等待監(jiān)視器鎖定的線程處于此狀態(tài)。
    • WAITING 正在等待另一個線程執(zhí)行特定動作的線程處于此狀態(tài)。
    • TIMED_WAITING 正在等待另一個線程執(zhí)行動作達到指定等待時間的線程處于此狀態(tài)。
    • TERMINATED 已退出的線程處于此狀態(tài)。

    一個線程可以在給定時間點處于一個狀態(tài)。 這些狀態(tài)是不反映任何操作系統(tǒng)線程狀態(tài)的虛擬機狀態(tài)。

    // 觀察線程的狀態(tài) public class StateTest {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{for (int i = 0; i < 5; i++) {// 線程啟動后睡五秒鐘try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("干活!");});// 觀察狀態(tài)Thread.State state = thread.getState();System.out.println(state);// 觀察new了一個線程后的狀態(tài)// 觀察啟動后thread.start();// 啟動線程state = thread.getState();System.out.println(state);// 觀察線程run后的狀態(tài)while(state!=Thread.State.TERMINATED){ // 只要線程不終止,就一直輸出狀態(tài)Thread.sleep(100);state = thread.getState();// 更新線程狀態(tài)System.out.println(state);// 輸出狀態(tài)}} }

    輸出:

    NEW
    RUNNABLE
    TIMED_WAITING
    TIMED_WAITING

    TIMED_WAITING
    干活!
    TERMINATED

    【注意】線程start后不一定立即執(zhí)行,而是處于RUNNABLE狀態(tài),即就緒狀態(tài)。線程休眠的5秒鐘內(nèi)一直是TIMED_WAITING狀態(tài),睡醒之后就干活,干完活就線程終止

    線程優(yōu)先級

    • Java提供一個線程調(diào)度器來監(jiān)控程序中啟動后進入就緒狀態(tài)的所有線程,線程調(diào)度器按照優(yōu)先級決定應(yīng)該調(diào)度哪個線程來執(zhí)行。

    • 線程的優(yōu)先級用數(shù)字表示,范圍從1~10:(數(shù)字越大優(yōu)先級越高)
      Thread.MIN_PRIORITY = 1;
      Thread.MAX_PRIORITY = 10;
      Thread.NORM_PRIORITY = 5;
      【注意】優(yōu)先級低只是意味著獲得調(diào)度的概率低。并不是優(yōu)先級低就不會被先調(diào)用,這都是取決于CPU。

    • 使用以下方式改變或者獲取優(yōu)先級:
      getPriority()
      setPriority(int x)

    • 如果需要設(shè)置優(yōu)先級,先設(shè)定好優(yōu)先級再start()

    // 測試線程的優(yōu)先級 public class PriorityTest {public static void main(String[] args) {System.out.println(Thread.currentThread().getName()+"主線程優(yōu)先級:"+Thread.currentThread().getPriority());MyPriority myPriority = new MyPriority();Thread t1 = new Thread(myPriority);Thread t2 = new Thread(myPriority);Thread t3 = new Thread(myPriority);Thread t4 = new Thread(myPriority);// 先設(shè)置優(yōu)先級 再啟動t1.start();t2.setPriority(1);t2.start();t3.setPriority(4);t3.start();t4.setPriority(Thread.MAX_PRIORITY);// MAX_PRIORITY=10t4.start();} }class MyPriority implements Runnable{@Overridepublic void run() {System.out.println(Thread.currentThread().getName()+"優(yōu)先級:"+Thread.currentThread().getPriority());} }

    守護線程

    • 線程分為用戶線程和守護線程
    • 虛擬機必須確保用戶線程執(zhí)行完畢
    • 虛擬機不用等待守護線程執(zhí)行完畢
    • 守護線程如:后臺記錄操作日志,監(jiān)控內(nèi)存,垃圾回收等
    public class DaemonTest {public static void main(String[] args) {God god = new God();You1 you = new You1();Thread thread = new Thread(god);thread.setDaemon(true); // 默認是false,表示用戶線程,一般都是用戶線程thread.start();// 上帝守護線程啟動new Thread(you).start();// 你 用戶線程啟動} }// 上帝 class God implements Runnable{@Overridepublic void run() {while (true){System.out.println("上帝保佑你");}} }// 你 class You1 implements Runnable{@Overridepublic void run() {for (int i = 0; i <= 36500; i++) {System.out.println("你一生幸福快樂地活著");}System.out.println("goodbye world");} }

    用戶線程停止之后,守護線程還在運行,因為虛擬機停止需要一段時間。

    后續(xù)內(nèi)容見《Java學(xué)習(xí)筆記5-2》

    總結(jié)

    以上是生活随笔為你收集整理的Java学习笔记5-1——多线程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。