Java多線程學(xué)習(xí)筆記
多任務(wù)、多線程
在多任務(wù)場(chǎng)景下,兩件事看上去同時(shí)在做,但實(shí)際上,你的大腦在同一時(shí)間只做一件事,間隔時(shí)間可能很少,但這似乎讓你感覺(jué)這兩件事是同時(shí)在做
考慮阻塞問(wèn)題,引入多線程的場(chǎng)景,多線程并發(fā)場(chǎng)景
程序、進(jìn)程、線程
程序=指令+數(shù)據(jù)(靜態(tài)的)
在操作系統(tǒng)中運(yùn)行的程序就是進(jìn)程,一個(gè)進(jìn)程可以有多個(gè)線程
比如,看視頻時(shí)聽(tīng)聲音,看圖像,看彈幕等
學(xué)著看jdk文檔
比如你要看Thread
你可以搜索,然后閱讀
往下翻你會(huì)看到:
線程的創(chuàng)建
1.繼承Thread類
//創(chuàng)建線程方式一:繼承Thread類,重寫(xiě)run方法,調(diào)用start()方法開(kāi)啟線程 public class TestThread1 extends Thread{ @Override public void run() { //run()方法線程體 IntStream.range(0,20).forEach(i->{ System.out.println("我在看代碼"+i); }); } public static void main(String[] args) { //創(chuàng)建一個(gè)線程對(duì)象 TestThread1 testThread1=new TestThread1(); //調(diào)用start()方法,啟動(dòng)線程,不一定立即執(zhí)行,由cpu調(diào)度執(zhí)行 testThread1.start(); //主方法 main方法 IntStream.range(0,20).forEach(i->{ System.out.println("我在學(xué)習(xí)多線程"+i); }); } }
一個(gè)小練習(xí):
//練習(xí)thread實(shí)現(xiàn)對(duì)線程同步下載圖片 public class TestThread2 extends Thread{ private String url; private String name; public TestThread2(String url, String name) { this.url = url; this.name = name; } @Override public void run() { WebDownload webDownload=new WebDownload(); webDownload.downloader(url,name); System.out.println("下載了文件名:"+name); } public static void main(String[] args) { TestThread2 t1=new TestThread2("https://profile.csdnimg.cn/B/D/2/3_sxh06","1.jpg"); TestThread2 t2=new TestThread2("https://profile.csdnimg.cn/B/D/2/3_sxh06","2.jpg"); TestThread2 t3=new TestThread2("https://profile.csdnimg.cn/B/D/2/3_sxh06","3.jpg"); t1.start(); t2.start(); t3.start(); } } //下載器 class WebDownload{ //下載方法 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方法出錯(cuò)"); } } }
2.實(shí)現(xiàn)Runable接口
//創(chuàng)建線程的方法2:實(shí)現(xiàn)Runable接口 public class TestThread3 implements Runnable{ @Override public void run() { //run()方法線程體 IntStream.range(0,20).forEach(i->{ System.out.println("我在看代碼"+i); }); } public static void main(String[] args) { //創(chuàng)建一個(gè)線程對(duì)象 TestThread3 testThread3=new TestThread3(); //調(diào)用start()方法,啟動(dòng)線程,不一定立即執(zhí)行,由cpu調(diào)度執(zhí)行 // Thread thread=new Thread(testThread3); // thread.start(); //或者這樣簡(jiǎn)寫(xiě) new Thread(testThread3).start(); //主方法 main方法 IntStream.range(0,100).forEach(i->{ System.out.println("我在學(xué)習(xí)多線程"+i); }); } }
理解并發(fā)的場(chǎng)景
當(dāng)多個(gè)線程使用同一個(gè)資源時(shí),會(huì)出現(xiàn)問(wèn)題,看看下面這個(gè)買火車票的例子:
public class TestThread4 implements Runnable{ //票數(shù) private int ticketNums=10; @Override public 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) { TestThread4 ticket=new TestThread4(); new Thread(ticket,"小明").start(); new Thread(ticket,"張三").start(); new Thread(ticket,"李四").start(); } }
看看運(yùn)行的結(jié)果:
可以看到案例中的線程不安全問(wèn)題,同時(shí)數(shù)據(jù)也是不正確的
龜兔賽跑場(chǎng)景
/** * 模擬龜兔賽跑 */ public class Race implements Runnable{ //勝利者 private static String winner; @Override public 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(); } } boolean flag=gameOver(i); if (flag){ //判斷比賽是否結(jié)束 break; } System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步"); } } /** * 判斷比賽是否結(jié)束 */ private boolean gameOver(int steps){ //判斷是否有勝利者 if (winner !=null){ //已經(jīng)存在勝利者 return true; }else if (steps >= 100){ winner=Thread.currentThread().getName(); System.out.println("勝利者是:"+winner); return true; }else{ return false; } } public static void main(String[] args) { Race race=new Race(); new Thread(race,"兔子").start(); new Thread(race,"烏龜").start(); } }
實(shí)現(xiàn)callable接口
//線程創(chuàng)建方式3 public class TestCallable implements Callable<Boolean> { private String url; private String name; public TestCallable(String url, String name) { this.url = url; this.name = name; } @Override public Boolean call() { com.sxh.thread.WebDownload webDownload=new com.sxh.thread.WebDownload(); webDownload.downloader(url,name); System.out.println("下載了文件名:"+name); return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { TestCallable t1=new TestCallable("https://profile.csdnimg.cn/B/D/2/3_sxh06","1.jpg"); TestCallable t2=new TestCallable("https://profile.csdnimg.cn/B/D/2/3_sxh06","2.jpg"); TestCallable t3=new TestCallable("https://profile.csdnimg.cn/B/D/2/3_sxh06","3.jpg"); //創(chuàng)建執(zhí)行服務(wù) ExecutorService ser= Executors.newFixedThreadPool(3); //提交執(zhí)行 Future<Boolean> r1=ser.submit(t1); Future<Boolean> r2=ser.submit(t2); Future<Boolean> r3=ser.submit(t3); //獲取結(jié)果 boolean rs1=r1.get(); boolean rs2=r2.get(); boolean rs3=r3.get(); //關(guān)閉服務(wù) ser.shutdownNow(); } }
理解函數(shù)式接口
任何接口,只包含唯一一個(gè)抽象方法,就是函數(shù)式接口
/** * lambdab表達(dá)式的發(fā)展 */ public class TestLambda1 { //3.靜態(tài)內(nèi)部類 static class Like2 implements ILike{ @Override public void lambda() { System.out.println("i like lambda2"); } } public static void main(String[] args) { ILike like=new Like(); like.lambda(); like=new Like2(); like.lambda(); //4.局部?jī)?nèi)部類 class Like3 implements ILike{ @Override public void lambda() { System.out.println("i like lambda3"); } } like=new Like3(); like.lambda(); //5.匿名內(nèi)部類 like=new ILike() { @Override public void lambda() { System.out.println("i like lambda4"); } }; like.lambda(); //6.用lambda簡(jiǎn)化 like=()->{ System.out.println("i like lambda5"); }; like.lambda(); } } //1.定義一個(gè)函數(shù)式接口 interface ILike{ void lambda(); } //2.實(shí)現(xiàn)類 class Like implements ILike{ @Override public void lambda() { System.out.println("i like lambda"); } }
理解線程的狀態(tài)
線程停止
public class TestStop implements Runnable{ //1.設(shè)置一個(gè)標(biāo)志位 private boolean flag=true; @Override public void run() { int i=0; while (flag){ System.out.println("run...thread.."+i++); } } //2.設(shè)置一個(gè)公開(kāi)的方法停止線程,轉(zhuǎn)換標(biāo)志位 public void stop(){ this.flag=false; } public static void main(String[] args) { TestStop stop=new TestStop(); new Thread(stop).start(); for (int i = 0; i < 1000; i++) { System.out.println("main"+i); if (i==900){ //調(diào)用stop方法,讓線程停止 stop.stop(); System.out.println("線程該停止了"); } } // IntStream.range(0,1000).forEach(i->{ // // }); } }
線程休眠sleep
每個(gè)對(duì)象都有一把鎖,sleep不會(huì)釋放鎖
1.網(wǎng)路延遲
//模擬延遲 try { Thread.sleep(200); //ms } catch (InterruptedException e) { e.printStackTrace(); }
2.倒計(jì)時(shí)等
public static void main(String[] args) { try { tendown(); } catch (InterruptedException e) { e.printStackTrace(); } } public static void tendown() throws InterruptedException { int num=10; while (true){ Thread.sleep(1000); System.out.println(num--); if(num<=0) { break; } } }
public static void main(String[] args) { //打印系統(tǒng)當(dāng)前時(shí)間 Date startTime=new Date(System.currentTimeMillis()); while (true){ try { Thread.sleep(1000); System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime)); startTime=new Date(System.currentTimeMillis());//更新時(shí)間 } catch (InterruptedException e) { e.printStackTrace(); } } }
線程禮讓yield
//線程禮讓 禮讓不一定成功,由cpu重新調(diào)度 public class TestYield { public static void main(String[] args) { MyYield myYield=new MyYield(); new Thread(myYield,"a").start(); new Thread(myYield,"b").start(); } } class MyYield implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"線程開(kāi)始執(zhí)行"); Thread.yield(); System.out.println(Thread.currentThread().getName()+"線程停止執(zhí)行"); } }
線程強(qiáng)制執(zhí)行
//測(cè)試join方法 想象為插隊(duì) public class TestJoin implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("線程vip來(lái)了"+i); } } public static void main(String[] args) throws InterruptedException { //啟動(dòng)線程 TestJoin testJoin=new TestJoin(); Thread thread=new Thread(testJoin); thread.start(); //主線程 for (int i = 0; i < 1000; i++) { if (i==200){ thread.join(); //插隊(duì) } System.out.println("main"+i); } } }
觀察線程狀態(tài)
public class TestState { 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("http://"); }); //觀察狀態(tài) Thread.State state=thread.getState(); System.out.println(state); //NEW //啟動(dòng)后 thread.start(); state=thread.getState(); System.out.println(state); //Run while (state != Thread.State.TERMINATED) { Thread.sleep(100); state=thread.getState();//更新線程狀態(tài) System.out.println(state); //Run } } }
線程的優(yōu)先級(jí)
//測(cè)試線程的優(yōu)先級(jí) public class TestPriority { public static void main(String[] args) { //主線程默認(rèn)優(yōu)先級(jí) System.out.println(Thread.currentThread().getName()+"--->"+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); Thread t5=new Thread(myPriority); Thread t6=new Thread(myPriority); //先設(shè)置優(yōu)先級(jí),在啟動(dòng) t1.start(); t2.setPriority(1); t2.start(); t3.setPriority(4); t3.start(); t4.setPriority(Thread.MAX_PRIORITY); t4.start(); t5.setPriority(-1); t5.start(); t6.setPriority(11); t6.start(); } } class MyPriority implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority()); } }
守護(hù)線程
線程分為用戶線程和守護(hù)線程
//測(cè)試守護(hù)線程 public class TestDaemon { public static void main(String[] args) { God god=new God(); You you=new You(); Thread thread=new Thread(god); thread.setDaemon(true); //默認(rèn)是false表示用戶線程 thread.start(); new Thread(you).start(); } } class God implements Runnable{ @Override public void run() { while (true){ System.out.println("上帝保佑著你"); } } } class You implements Runnable{ @Override public void run() { for (int i = 0; i < 36000; i++) { System.out.println("你活著"+i); } System.out.println("goodbye!!"); } }
線程同步機(jī)制
解決安全性問(wèn)題:隊(duì)列+鎖
1.synchronized 同步方法
默認(rèn)鎖的是this,如需鎖其他的,使用下面的同步塊
//synchronized 同步方法 private synchronized void buy(){ if (ticketNums<=0){ flag=false; return; } //模擬延遲 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } //買票 System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNums--+"張票"); }
2.同步塊synchronized(Obj){}
鎖的對(duì)象是變化的量,需要增刪改的對(duì)象
obj稱之為同步監(jiān)視器,即監(jiān)視對(duì)象
public class UnsafeList { public static void main(String[] args) { List<String> list=new ArrayList<String>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ synchronized (list){ list.add(Thread.currentThread().getName()); } }).start(); } try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size()); } }
lock
class A{ //ReentrantLock 可重入鎖 private final ReentrantLock lock=new ReentrantLock(); public void f(){ lock.lock();//加鎖 try{ //..... } finally{ lock.unlock();//釋放鎖 } } }
synchronized與lock
- lock是顯示鎖需要手動(dòng)開(kāi)關(guān),synchronized是隱式鎖,出了作用域自動(dòng)釋放
- lock只有代碼塊鎖,synchronized有代碼塊鎖和方法鎖
- JVM將花費(fèi)更少的時(shí)間來(lái)調(diào)度線程,性能更好,更有擴(kuò)展性
- 優(yōu)先使用:Lock>同步代碼塊>同步方法
到此這篇關(guān)于Java多線程學(xué)習(xí)筆記的文章就介紹到這了,更多相關(guān)Java 多線程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MybatisPlusInterceptor依賴變紅如何解決,無(wú)法識(shí)別問(wèn)題
這篇文章主要介紹了MybatisPlusInterceptor依賴變紅如何解決,無(wú)法識(shí)別問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07java獲取http請(qǐng)求的Header和Body的簡(jiǎn)單方法
下面小編就為大家?guī)?lái)一篇java獲取http請(qǐng)求的Header和Body的簡(jiǎn)單方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-11-11Java中的ReentrantReadWriteLock使用詳解
這篇文章主要介紹了Java中的ReentrantReadWriteLock使用詳解,ReentrantReadWriteLock是Java中的一個(gè)鎖實(shí)現(xiàn),它提供了讀寫(xiě)分離的功能,這種讀寫(xiě)分離的機(jī)制可以提高并發(fā)性能,特別適用于讀多寫(xiě)少的場(chǎng)景,需要的朋友可以參考下2023-11-11java 實(shí)現(xiàn)多線程的方法總結(jié)
這篇文章主要介紹了java 實(shí)現(xiàn)多線程的方法總結(jié)的相關(guān)資料,需要的朋友可以參考下2016-10-10Java設(shè)計(jì)模式之原型模式詳細(xì)解讀
這篇文章主要介紹了Java設(shè)計(jì)模式之原型模式詳細(xì)解讀,原型模式屬于創(chuàng)建型設(shè)計(jì)模式,用于創(chuàng)建重復(fù)的對(duì)象,且同時(shí)又保證了性能,該設(shè)計(jì)模式的好處是將對(duì)象的創(chuàng)建與調(diào)用方分離,需要的朋友可以參考下2023-12-12