###24.01_多線程(多線程的引入)(了解)
* 1.什么是線程
????* 線程是程序執(zhí)行的一條路徑, 一個(gè)進(jìn)程中可以包含多條線程
????* 多線程并發(fā)執(zhí)行可以提高程序的效率, 可以同時(shí)完成多項(xiàng)工作
* 2.多線程的應(yīng)用場(chǎng)景
????* 紅蜘蛛同時(shí)共享屏幕給多個(gè)電腦
????* 迅雷開啟多條線程一起下載
????* QQ同時(shí)和多個(gè)人一起視頻
????* 服務(wù)器同時(shí)處理多個(gè)客戶端請(qǐng)求
###24.02_多線程(多線程并行和并發(fā)的區(qū)別)(了解)
* 并行就是兩個(gè)任務(wù)同時(shí)運(yùn)行,就是甲任務(wù)進(jìn)行的同時(shí),乙任務(wù)也在進(jìn)行。(需要多核CPU)
* 并發(fā)是指兩個(gè)任務(wù)都請(qǐng)求運(yùn)行,而處理器只能按受一個(gè)任務(wù),就把這兩個(gè)任務(wù)安排輪流進(jìn)行,由于時(shí)間間隔較短,使人感覺兩個(gè)任務(wù)都在運(yùn)行。
* 比如我跟兩個(gè)網(wǎng)友聊天,左手操作一個(gè)電腦跟甲聊,同時(shí)右手用另一臺(tái)電腦跟乙聊天,這就叫并行。
* 如果用一臺(tái)電腦我先給甲發(fā)個(gè)消息,然后立刻再給乙發(fā)消息,然后再跟甲聊,再跟乙聊。這就叫并發(fā)。
###24.03_多線程(Java程序運(yùn)行原理和JVM的啟動(dòng)是多線程的嗎)(了解)
* A:Java程序運(yùn)行原理
????* Java命令會(huì)啟動(dòng)java虛擬機(jī),啟動(dòng)JVM,等于啟動(dòng)了一個(gè)應(yīng)用程序,也就是啟動(dòng)了一個(gè)進(jìn)程。該進(jìn)程會(huì)自動(dòng)啟動(dòng)一個(gè) “主線程” ,然后主線程去調(diào)用某個(gè)類的 main 方法。
* B:JVM的啟動(dòng)是多線程的嗎
????* JVM啟動(dòng)至少啟動(dòng)了垃圾回收線程和主線程,所以是多線程的。
###24.04_多線程(多線程程序?qū)崿F(xiàn)的方式1)(掌握)
* 1.繼承Thread
????* 定義類繼承Thread
????* 重寫run方法
????* 把新線程要做的事寫在run方法中
????* 創(chuàng)建線程對(duì)象
????* 開啟新線程, 內(nèi)部會(huì)自動(dòng)執(zhí)行run方法
????*
????????????public class Demo2_Thread {
????????????????/**
?????????????????* @param args
?????????????????*/
????????????????public static void main(String[] args) {
????????????????????MyThread mt = new MyThread();?????????????????????????? //4,創(chuàng)建自定義類的對(duì)象
????????????????????mt.start();???????????????????????????????????????????? //5,開啟線程
????????????????????for(int i = 0; i < 3000; i++) {
????????????????????????System.out.println("bb");
????????????????????}
????????????????}
????????????}
????????????class MyThread extends Thread {???????????????????????????????? //1,定義類繼承Thread
????????????????public void run() {???????????????????????????????????????? //2,重寫run方法
????????????????????for(int i = 0; i < 3000; i++) {????????????????????????? //3,將要執(zhí)行的代碼,寫在run方法中
????????????????????????System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa");
????????????????????}
????????????????}
????????????}
###24.05_多線程(多線程程序?qū)崿F(xiàn)的方式2)(掌握)
* 2.實(shí)現(xiàn)Runnable
????* 定義類實(shí)現(xiàn)Runnable接口
????* 實(shí)現(xiàn)run方法
????* 把新線程要做的事寫在run方法中
????* 創(chuàng)建自定義的Runnable的子類對(duì)象
????* 創(chuàng)建Thread對(duì)象, 傳入Runnable
????* 調(diào)用start()開啟新線程, 內(nèi)部會(huì)自動(dòng)調(diào)用Runnable的run()方法
????????????public class Demo3_Runnable {
????????????????/**
?????????????????* @param args
?????????????????*/
????????????????public static void main(String[] args) {
????????????????????MyRunnable mr = new MyRunnable();?????????????????????? //4,創(chuàng)建自定義類對(duì)象
????????????????????//Runnable target = new MyRunnable();
????????????????????Thread t = new Thread(mr);????????????????????????????? //5,將其當(dāng)作參數(shù)傳遞給Thread的構(gòu)造函數(shù)
????????????????????t.start();????????????????????????????????????????????? //6,開啟線程
????????????????????for(int i = 0; i < 3000; i++) {
????????????????????????System.out.println("bb");
????????????????????}
????????????????}
????????????}
????????????class MyRunnable implements Runnable {????????????????????????? //1,自定義類實(shí)現(xiàn)Runnable接口
????????????????@Override
????????????????public void run() {???????????????????????????????????????? //2,重寫run方法
????????????????????for(int i = 0; i < 3000; i++) {????????????????????????? //3,將要執(zhí)行的代碼,寫在run方法中
????????????????????????System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa");
????????????????????}
????????????????}
????????????}
###24.06_多線程(實(shí)現(xiàn)Runnable的原理)(了解)
* 查看源碼
????* 1,看Thread類的構(gòu)造函數(shù),傳遞了Runnable接口的引用
????* 2,通過init()方法找到傳遞的target給成員變量的target賦值
????* 3,查看run方法,發(fā)現(xiàn)run方法中有判斷,如果target不為null就會(huì)調(diào)用Runnable接口子類對(duì)象的run方法
###24.07_多線程(兩種方式的區(qū)別)(掌握)
* 查看源碼的區(qū)別:
????* a.繼承Thread : 由于子類重寫了Thread類的run(), 當(dāng)調(diào)用start()時(shí), 直接找子類的run()方法
????* b.實(shí)現(xiàn)Runnable : 構(gòu)造函數(shù)中傳入了Runnable的引用, 成員變量記住了它, start()調(diào)用run()方法時(shí)內(nèi)部判斷成員變量Runnable的引用是否為空, 不為空編譯時(shí)看的是Runnable的run(),運(yùn)行時(shí)執(zhí)行的是子類的run()方法
* 繼承Thread
????* 好處是:可以直接使用Thread類中的方法,代碼簡(jiǎn)單
????* 弊端是:如果已經(jīng)有了父類,就不能用這種方法
* 實(shí)現(xiàn)Runnable接口
????* 好處是:即使自己定義的線程類有了父類也沒關(guān)系,因?yàn)橛辛烁割愐部梢詫?shí)現(xiàn)接口,而且接口是可以多實(shí)現(xiàn)的
????* 弊端是:不能直接使用Thread中的方法需要先獲取到線程對(duì)象后,才能得到Thread的方法,代碼復(fù)雜
###24.08_多線程(匿名內(nèi)部類實(shí)現(xiàn)線程的兩種方式)(掌握)
* 繼承Thread類
????????new Thread() {????????????????????????????????????????????????? //1,new 類(){}繼承這個(gè)類
????????????public void run() {???????????????????????????????????????? //2,重寫run方法
????????????????for(int i = 0; i < 3000; i++) {????????????????????????? //3,將要執(zhí)行的代碼,寫在run方法中
????????????????????System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa");
????????????????}
????????????}
????????}.start();
* 實(shí)現(xiàn)Runnable接口
????????new Thread(new Runnable(){????????????????????????????????????? //1,new 接口(){}實(shí)現(xiàn)這個(gè)接口
????????????public void run() {???????????????????????????????????????? //2,重寫run方法
????????????????for(int i = 0; i < 3000; i++) {????????????????????????? //3,將要執(zhí)行的代碼,寫在run方法中
????????????????????System.out.println("bb");
????????????????}
????????????}
????????}).start();
###24.09_多線程(獲取名字和設(shè)置名字)(掌握)
* 1.獲取名字
????* 通過getName()方法獲取線程對(duì)象的名字
* 2.設(shè)置名字
????* 通過構(gòu)造函數(shù)可以傳入String類型的名字
????*
????????????new Thread("xxx") {
????????????????public void run() {
????????????????????for(int i = 0; i < 1000; i++) {
????????????????????????System.out.println(this.getName() + "....aaaaaaaaaaaaaaaaaaaaaaa");
????????????????????}
????????????????}
????????????}.start();
????????????new Thread("yyy") {
????????????????public void run() {
????????????????????for(int i = 0; i < 1000; i++) {
????????????????????????System.out.println(this.getName() + "....bb");
????????????????????}
????????????????}
????????????}.start();
????* 通過setName(String)方法可以設(shè)置線程對(duì)象的名字
????*
????????????Thread t1 = new Thread() {
????????????????public void run() {
????????????????????for(int i = 0; i < 1000; i++) {
????????????????????????System.out.println(this.getName() + "....aaaaaaaaaaaaaaaaaaaaaaa");
????????????????????}
????????????????}
????????????};
????????????Thread t2 = new Thread() {
????????????????public void run() {
????????????????????for(int i = 0; i < 1000; i++) {
????????????????????????System.out.println(this.getName() + "....bb");
????????????????????}
????????????????}
????????????};
????????????t1.setName("芙蓉姐姐");
????????????t2.setName("鳳姐");
????????????t1.start();
????????????t2.start();
###24.10_多線程(獲取當(dāng)前線程的對(duì)象)(掌握)
* Thread.currentThread(), 主線程也可以獲取
????*
????????????new Thread(new Runnable() {
????????????????public void run() {
????????????????????for(int i = 0; i < 1000; i++) {
????????????????????????System.out.println(Thread.currentThread().getName() + "...aaaaaaaaaaaaaaaaaaaaa");
????????????????????}
????????????????}
????????????}).start();
????????????new Thread(new Runnable() {
????????????????public void run() {
????????????????????for(int i = 0; i < 1000; i++) {
????????????????????????System.out.println(Thread.currentThread().getName() + "...bb");
????????????????????}
????????????????}
????????????}).start();
????????????Thread.currentThread().setName("我是主線程");??????????????????? //獲取主函數(shù)線程的引用,并改名字
????????????System.out.println(Thread.currentThread().getName());?????? //獲取主函數(shù)線程的引用,并獲取名字
###24.11_多線程(休眠線程)(掌握)
* Thread.sleep(毫秒,納秒), 控制當(dāng)前線程休眠若干毫秒1秒= 1000毫秒 1秒 = 1000 * 1000 * 1000納秒 1000000000
????????????new Thread() {
????????????????public void run() {
????????????????????for(int i = 0; i < 10; i++) {
????????????????????????System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
????????????????????????try {
????????????????????????????Thread.sleep(10);
????????????????????????} catch (InterruptedException e) {
????????????????????????????e.printStackTrace();
????????????????????????}
????????????????????}
????????????????}
????????????}.start();
????????????new Thread() {
????????????????public void run() {
????????????????????for(int i = 0; i < 10; i++) {
????????????????????????System.out.println(getName() + "...bb");
????????????????????????try {
????????????????????????????Thread.sleep(10);
????????????????????????} catch (InterruptedException e) {
????????????????????????????e.printStackTrace();
????????????????????????}
????????????????????}
????????????????}
????????????}.start();
###24.12_多線程(守護(hù)線程)(掌握)
* setDaemon(), 設(shè)置一個(gè)線程為守護(hù)線程, 該線程不會(huì)單獨(dú)執(zhí)行, 當(dāng)其他非守護(hù)線程都執(zhí)行結(jié)束后, 自動(dòng)退出
????*
????????????Thread t1 = new Thread() {
????????????????public void run() {
????????????????????for(int i = 0; i < 50; i++) {
????????????????????????System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
????????????????????????try {
????????????????????????????Thread.sleep(10);
????????????????????????} catch (InterruptedException e) {
????????????????????????????e.printStackTrace();
????????????????????????}
????????????????????}
????????????????}
????????????};
????????????Thread t2 = new Thread() {
????????????????public void run() {
????????????????????for(int i = 0; i < 5; i++) {
????????????????????????System.out.println(getName() + "...bb");
????????????????????????try {
????????????????????????????Thread.sleep(10);
????????????????????????} catch (InterruptedException e) {
????????????????????????????e.printStackTrace();
????????????????????????}
????????????????????}
????????????????}
????????????};
????????????t1.setDaemon(true);???????????????????? //將t1設(shè)置為守護(hù)線程
????????????t1.start();
????????????t2.start();
###24.13_多線程(加入線程)(掌握)
* join(), 當(dāng)前線程暫停, 等待指定的線程執(zhí)行結(jié)束后, 當(dāng)前線程再繼續(xù)
* join(int), 可以等待指定的毫秒之后繼續(xù)
????*
????????????final Thread t1 = new Thread() {
????????????????public void run() {
????????????????????for(int i = 0; i < 50; i++) {
????????????????????????System.out.println(getName() + "...aaaaaaaaaaaaaaaaaaaaaa");
????????????????????????try {
????????????????????????????Thread.sleep(10);
????????????????????????} catch (InterruptedException e) {
????????????????????????????e.printStackTrace();
????????????????????????}
????????????????????}
????????????????}
????????????};
????????????Thread t2 = new Thread() {
????????????????public void run() {
????????????????????for(int i = 0; i < 50; i++) {
????????????????????????if(i == 2) {
????????????????????????????try {
????????????????????????????????//t1.join();??????????????????????? //插隊(duì),加入
????????????????????????????????t1.join(30);??????????????????????? //加入,有固定的時(shí)間,過了固定時(shí)間,繼續(xù)交替執(zhí)行
????????????????????????????????Thread.sleep(10);
????????????????????????????} catch (InterruptedException e) {
????????????????????????????????e.printStackTrace();
????????????????????????????}
????????????????????????}
????????????????????????System.out.println(getName() + "...bb");
????????????????????}
????????????????}
????????????};
????????????t1.start();
????????????t2.start();
###24.14_多線程(禮讓線程)(了解)
* yield讓出cpu
###24.15_多線程(設(shè)置線程的優(yōu)先級(jí))(了解)
* setPriority()設(shè)置線程的優(yōu)先級(jí)
###24.16_多線程(同步代碼塊)(掌握)
* 1.什么情況下需要同步
????* 當(dāng)多線程并發(fā), 有多段代碼同時(shí)執(zhí)行時(shí), 我們希望某一段代碼執(zhí)行的過程中CPU不要切換到其他線程工作. 這時(shí)就需要同步.
????* 如果兩段代碼是同步的, 那么同一時(shí)間只能執(zhí)行一段, 在一段代碼沒執(zhí)行結(jié)束之前, 不會(huì)執(zhí)行另外一段代碼.
* 2.同步代碼塊
????* 使用synchronized關(guān)鍵字加上一個(gè)鎖對(duì)象來定義一段代碼, 這就叫同步代碼塊
????* 多個(gè)同步代碼塊如果使用相同的鎖對(duì)象, 那么他們就是同步的
????????????class Printer {
????????????????Demo d = new Demo();
????????????????public static void print1() {
????????????????????synchronized(d){??????????????? //鎖對(duì)象可以是任意對(duì)象,但是被鎖的代碼需要保證是同一把鎖,不能用匿名對(duì)象
????????????????????????System.out.print("黑");
????????????????????????System.out.print("馬");
????????????????????????System.out.print("程");
????????????????????????System.out.print("序");
????????????????????????System.out.print("員");
????????????????????????System.out.print("\r\n");
????????????????????}
????????????????}
????????????????public static void print2() {??
????????????????????synchronized(d){???
????????????????????????System.out.print("傳");
????????????????????????System.out.print("智");
????????????????????????System.out.print("播");
????????????????????????System.out.print("客");
????????????????????????System.out.print("\r\n");
????????????????????}
????????????????}
????????????}
###24.17_多線程(同步方法)(掌握)
* 使用synchronized關(guān)鍵字修飾一個(gè)方法, 該方法中所有的代碼都是同步的
????????class Printer {
????????????public static void print1() {
????????????????synchronized(Printer.class){??????????????? //鎖對(duì)象可以是任意對(duì)象,但是被鎖的代碼需要保證是同一把鎖,不能用匿名對(duì)象
????????????????????System.out.print("黑");
????????????????????System.out.print("馬");
????????????????????System.out.print("程");
????????????????????System.out.print("序");
????????????????????System.out.print("員");
????????????????????System.out.print("\r\n");
????????????????}
????????????}
????????????/*
?????????????* 非靜態(tài)同步函數(shù)的鎖是:this
?????????????* 靜態(tài)的同步函數(shù)的鎖是:字節(jié)碼對(duì)象
?????????????*/
????????????public static synchronized void print2() {?
????????????????System.out.print("傳");
????????????????System.out.print("智");
????????????????System.out.print("播");
????????????????System.out.print("客");
????????????????System.out.print("\r\n");
????????????}
????????}
###24.18_多線程(線程安全問題)(掌握)
* 多線程并發(fā)操作同一數(shù)據(jù)時(shí), 就有可能出現(xiàn)線程安全問題
* 使用同步技術(shù)可以解決這種問題, 把操作數(shù)據(jù)的代碼進(jìn)行同步, 不要多個(gè)線程一起操作
????????????public class Demo2_Synchronized {
????????????????/**
?????????????????* @param args
?????????????????* 需求:鐵路售票,一共100張,通過四個(gè)窗口賣完.
?????????????????*/
????????????????public static void main(String[] args) {
????????????????????TicketsSeller t1 = new TicketsSeller();
????????????????????TicketsSeller t2 = new TicketsSeller();
????????????????????TicketsSeller t3 = new TicketsSeller();
????????????????????TicketsSeller t4 = new TicketsSeller();
????????????????????t1.setName("窗口1");
????????????????????t2.setName("窗口2");
????????????????????t3.setName("窗口3");
????????????????????t4.setName("窗口4");
????????????????????t1.start();
????????????????????t2.start();
????????????????????t3.start();
????????????????????t4.start();
????????????????}
????????????}
????????????class TicketsSeller extends Thread {
????????????????private static int tickets = 100;
????????????????static Object obj = new Object();
????????????????public TicketsSeller() {
????????????????????super();
????????????????}
????????????????public TicketsSeller(String name) {
????????????????????super(name);
????????????????}
????????????????public void run() {
????????????????????while(true) {
????????????????????????synchronized(obj) {
????????????????????????????if(tickets <= 0)
????????????????????????????????break;
????????????????????????????try {
????????????????????????????????Thread.sleep(10);//線程1睡,線程2睡,線程3睡,線程4睡
????????????????????????????} catch (InterruptedException e) {
????????????????????????????????e.printStackTrace();
????????????????????????????}
????????????????????????????System.out.println(getName() + "...這是第" + tickets-- + "號(hào)票");
????????????????????????}
????????????????????}
????????????????}
????????????}
###24.19_多線程(火車站賣票的例子用實(shí)現(xiàn)Runnable接口)(掌握)
###24.20_多線程(死鎖)(了解)
* 多線程同步的時(shí)候, 如果同步代碼嵌套, 使用相同鎖, 就有可能出現(xiàn)死鎖
????* 盡量不要嵌套使用
????????????private static String s1 = "筷子左";
????????????private static String s2 = "筷子右";
????????????public static void main(String[] args) {
????????????????new Thread() {
????????????????????public void run() {
????????????????????????while(true) {
????????????????????????????synchronized(s1) {
????????????????????????????????System.out.println(getName() + "...拿到" + s1 + "等待" + s2);
????????????????????????????????synchronized(s2) {
????????????????????????????????????System.out.println(getName() + "...拿到" + s2 + "開吃");
????????????????????????????????}
????????????????????????????}
????????????????????????}
????????????????????}
????????????????}.start();
????????????????new Thread() {
????????????????????public void run() {
????????????????????????while(true) {
????????????????????????????synchronized(s2) {
????????????????????????????????System.out.println(getName() + "...拿到" + s2 + "等待" + s1);
????????????????????????????????synchronized(s1) {
????????????????????????????????????System.out.println(getName() + "...拿到" + s1 + "開吃");
????????????????????????????????}
????????????????????????????}
????????????????????????}
????????????????????}
????????????????}.start();
????????????}
###24.21_多線程(以前的線程安全的類回顧)(掌握)
* A:回顧以前說過的線程安全問題
????* 看源碼:Vector,StringBuffer,Hashtable,Collections.synchroinzed(xxx)
????* Vector是線程安全的,ArrayList是線程不安全的
????* StringBuffer是線程安全的,StringBuilder是線程不安全的
????* Hashtable是線程安全的,HashMap是線程不安全的