宿州網(wǎng)站建設時間seo基礎教程視頻
一、多線程
1.1進程和線程
進程:進程就是操作系統(tǒng)中運行的每一個應用程序。例如:微信,QQ
線程:線程是進程中的每一個任務。
? ? ? ? 多線程:在一個進程中,可以同時執(zhí)行多個線程。同時完成多個任務。
? ? ? ? 并發(fā):同時發(fā)生。多個線程同時完成任務時。
我們編寫的應用程序運行時,也是一個進程。在我們進程中也是有線程的。
? ? ? ? 第一個現(xiàn)車給稱為主線程:main方法就是第一個線程。
1.2 CPU輪轉(zhuǎn)時間片
內(nèi)存是作用是什么?處理程序數(shù)據(jù)。
CPU是中央處理器。程序想要運行必須使用CPU。
一個CPU同一時間只能處理一個程序運行。
一個程序想要運行必須搶到CPU
1.3 Thread類
Thread是線程的父類
(1) 構(gòu)造方法
public Thread()
public Thread(String name)
? ? ? ? name 是線程的名稱
public Thread(Runnable target)
(2) 常用方法
public synchronized void start()
????????線程啟動方法。 啟動與運行現(xiàn)在不在是相同的概念。
????????線程啟動之后,表示當前這個線程對象可以被運行。
public void run()
????????線程運行方法。
1.4 使用Thread類創(chuàng)建自定義的線程類
創(chuàng)建Thread類的子類。重寫run的方法。為這個方法編寫具體任務代碼
Thread子類:
public class Thread01 extends Thread{@overridepublic void run(){System.out.println("abc");}
}
main方法:
public static void main(String[] args){Thread thread = new Thread01();thread.start();//啟動
}
1.5 為什么要用多線程
示例代碼:
public class Thread01 extends Thread{@overridepublic void run(){for(int i = 1;i <= 1000 ;i++){System.out.println(i+":abc");}}
}
public static void main(String[] args) {Thread thread = new Thread01();//創(chuàng)建了一個新的子線程。thread.start();//啟動子線程。//thread.run();for(int i = 1;i<=1000;i++) {System.out.println(i+":000");}
}
輸出:?
1:000
1:abc
2:abc
3:abc
2:000
4:abc
3:000
?在main方法中調(diào)用了thread.start();就表示在主線程中開辟了一個新的子線程。
主線程與子線程是并列的關系。二個線程之間的執(zhí)行要通過搶CPU。
1.6 使用Runnable接口創(chuàng)建線程任務
線程:線程是在進程中的每一個任務。
Runnable接口就表示的任務接口。
@FunctionalInterface
public interfaceRunnable{public abstract void run();
}
編寫Runnable接口的實現(xiàn)類。任務類
public class Runnbale02 implements Runnable{@Overridepublic void run() {for(int i = 1;i<=1000;i++) {System.out.println(i+":000");}}
}
使用任務類來創(chuàng)建一個線程對象
將任務類交給線程對象進行加載。線程啟動時就可以調(diào)用任務類的run方法。
public static void main(String[] args) {//使用Runnable接口的實現(xiàn)類Runnbale02 runnbale02 = new Runnbale02();Thread thread02 = new Thread(runnbale02);thread02.start();
}
1.7 使用Callable接口編寫任務類
在JDK1.5加入的關于線程的新的類。?
解決了Runnable接口的二個問題:
????????1?異常沒有拋出。調(diào)用線程啟動時類,不知道在線程執(zhí)行過程中有沒有出現(xiàn)異常。
????????2 run方法沒有返回值。調(diào)用線程結(jié)束之后。沒有數(shù)據(jù)返回。
?編寫Callable接口下的實現(xiàn)類。任務類
public class Callable03 implements Callable<String> {@Overridepublic String call() throws Exception {for(int i = 1;i<=1000;i++) {System.out.println(i+":***");}return "任務Callable03執(zhí)行完成!";}
}
1.8 使用線程池執(zhí)行Callable接口的任務
(1) 線程池
池:容器。
線程池是存放大量線程對象的容器。
一個線程對象(Thread?t)可不可能加載多個任務(Runnable?r,Callable?c)?
多個任務以串行方式,以先后順序的方式進行執(zhí)行。有多個任務可以同時使用同一個線程對象。
線程池:就是一個存放了大量線程對象的容器。
????????當有任務需要執(zhí)行時,從線程池中拿一個線程對象。任務執(zhí)行完成之后。將線程對象還給線程池。
(2)線程池Executor接口?
實際開發(fā)時使用Executor接口的子接口ExecutorService
Submit 方法稱為注冊任務方法。
public interface ExcecutorService extends Executor{
? ? ? ? <T>Future<T> submit(Callable<T> task);
}
這是一個創(chuàng)建線程池對象的工具類。
通過這個類的靜態(tài)方法來獲得不同的線程池對象
public class Executors {
返回一個有固定大小的線程池容器對象
public static ExecutorService newFixedThreadPool(int nThreads);
返回只有一個線程對象的線程池對象
public static ExecutorService newSingleThreadExecutor()
返回一個可變化的線程池對象
public static ExecutorService newCachedThreadPool()
}
(3) 返回值Future接口?
用來接收Callable任務執(zhí)行完成之后的返回值。
public interface Future<V> {
獲取返回值對象的方式
V get() throws InterruptedException, ExecutionException;
}
public static void main(String[] args) {Callable03 callable03 = new Callable03();ExecutorService service = Executors.newFixedThreadPool(5);Future<String> future03 = service.submit(callable03);try {System.out.println(future03.get());} catch (Exception e) {e.printStackTrace();} finally {service.shutdown();}
}
1.9 線程狀態(tài)
(1) 初始狀態(tài)
一個剛被創(chuàng)建好的線程對象,就是初始狀態(tài)的對象。
Thread t = new Thread(new Runnable02());
當前新創(chuàng)建的t線程對象就是初始狀態(tài)
(2) 可運行狀態(tài)
一個線程想要運行必須先搶到CPU。啟動與運行是不同的概念。
當一個線程對象調(diào)用了start()方法啟動了,但還沒有搶到CPU時,都是一個可運行狀態(tài)。
可運行狀態(tài)的線程對象只做一件事:搶CPU。
(3) 運行狀態(tài)
一個搶到CPU正在指向的線程對象。稱為運行狀態(tài)。
當一個運行裝藥的線程對象,CPU時間到期要轉(zhuǎn)換為可運行狀態(tài)。
(4) 終止狀態(tài)
一個執(zhí)行完成run()方法的線程對象,就是終止狀態(tài)。
1.10 線程中的常用屬性和方法
(1) name 屬性
用來表示一個線程對象的名稱??梢栽趧?chuàng)建線程對象時指定。也可以通過方法指定。
public class NameTest{public static void main(String[] args){Thread t = new Thread(new Runnable02(),"線程1");System.out.println(t.getName());}
}
(2) currentThread()
Thread類的靜態(tài)方法。動態(tài)獲取當前執(zhí)行的線程對象。
? ? ? ? public static native Thread currentThread()
public class Runnable02 implements Runnable{@Overridepublic void run(){for(int i = 1;i <= 10;i++){String name = Thread.currentThread().getName();System.out.println(name+"-"+i+":000");}}}
1.11 阻塞狀態(tài)
線程對象運行過程中,出現(xiàn)以下情況:
? ? ? ? 1 等待IO。做標準輸入時,用戶需要沖控制臺輸入數(shù)據(jù)時。
? ? ? ? 2 sleep方法。當前線程休眠。
當線程遇到以上兩種情況時,會進入阻塞狀態(tài)。并會釋放CPU。
?
二、本地線程對象ThreadLocal<T>類
2.1 本地線程對象是什么
1 本地線程對象其實是一個容器。而且是一個只能保存一個數(shù)據(jù)的容器對象。
2 這個容器在每一個不同的線程中,都有一份自己的實例。
3 每一個線程對象都使用自己的ThreadLocal類的實例。
4 當出現(xiàn)多個線程時,每一個線程中都有一個自己使用的ThreadLocal對象。
2.2 常用方法
public static void main(String[] args){ThreadLocal<String> local = new ThreadLocal<>();local.set("zhangsan");String s = local.get();System.out.println(s);
}
三、案例:取款與存款案例
3.1 編寫兩個任務
存款任務
取款任務
3.2 專家原則
專家原則:事情哪個對象最清楚(處理最直接),哪個對象負責
3.3 編寫賬號的類
public class Acount{private Double money;public Acount(Double money){this.money = money;}public Double getMoney() {return money;}public void setMoney(Double money) {this.money = money;}
}
3.4 編寫DAO
public class AcountDAO{public void update(Acount acount,Double money){acount.setMonet(money);}
}
3.5 編寫Service
public class AcountService{private AcountDAO acountDAO = new AcountDAO();public void deposit(Acount acount,Double money){double temp = acount.getMoney();temp = temp + money;acountDAO.update(acount,temp);}public void draw(Acount acount,Double money)throws Exception{double temp = acount.getMoney();if(temp > money){temp = temp - money;acountDAO.update(acount.temp);}else{throw new Exception("余額不足!");}}
}
3.6 編寫任務類
任務類一定是與Service層對接。
存款任務:DepositRunnable
public class DepositRunnable implements Runnable{private AcountService service = new AcountService();private Acount acount;private Double money;public DepositRunnable(Acount acount,Double money){this.acount = acount;this.money = money;}@Overridepublic void run(){service.deposit(acount money);System.out.println("存款成功!"+acount.getMoney());}
}
取款任務:Draw
public class DrawRunnable implements Runnable{private AcountService service = new AcountService();private Acount acount;private Double money;public DrawRunnable(Acount acount, Double money) {this.acount = acount;this.money = money;}@Overridepublic void run() {try {service.draw(acount,money);System.out.println("取款成功!"+acount.getMoney());} catch (Exception e) {e.printStackTrace();System.out.println(e.getMessage()+"取款失敗!"+acount.getMoney());}}
}
3.7 編寫測試類
public static void main(String[] args) {Acount acount = new Acount(10000.0);DepositRunnable depositRunnable01 = new DepositRunnable(acount,10000.0);DepositRunnable depositRunnable02 = new DepositRunnable(acount,2000.0);DrawRunnable drawRunnable01 = new DrawRunnable(acount,5000.0);Thread t1 = new Thread(depositRunnable01,"自己");Thread t2 = new Thread(depositRunnable02,"父母");Thread t3 = new Thread(drawRunnable01,"女朋友");t1.start();t2.start();t3.start();}
問題!會出現(xiàn)數(shù)據(jù)不一致的情況。
賬戶的余額會經(jīng)常變化。不是一個正確的固定的數(shù)值
四、線程不安全
線程不安全是指對各線程在操作數(shù)據(jù)時,數(shù)據(jù)不正確的情況。
線程不安全的幾個條件:
1? 多個線程????????- ????????多線程
2? 同時操作????????- ????????并發(fā)
3? 同一個數(shù)據(jù)????????- ????????臨界資源
4? 破壞了不可分割的操作時????????- ????????原子性
當以上的情況發(fā)生時,就有可能造成數(shù)據(jù)的不一致。這種情況就稱線程不安全。
當多線程并發(fā)操作臨界資源時,破壞了其原子性操作時,就有可能造成數(shù)據(jù)不一致。
解決方案:線程同步
五、線程同步
線程同步:多個線程以串行的方式執(zhí)行。這種方式稱為線程同步。
? ? ? ? 手拉手一起走,是異步。
串行的方式執(zhí)行:是針對對臨界資源的操作部分以串行的方式操作。
5.1 同步鎖 synchronized
我們使用同步鎖,來保證臨界資源的原子性不被分割。
在執(zhí)行原子性操作之前,先獲取同步鎖對象。之后再執(zhí)行原子性代碼。只有原子性代碼執(zhí)行完后曾之后。我們才會釋放同步鎖對象。
特殊注意:只有使用同一個同步鎖的多個線程,才會以串行的方式執(zhí)行。
必須要保證,多個線程使用的是同一個鎖對象。才能叫多個線程的同步。
一般情況下,挑選臨界資源作為鎖對象更合適。
5.2 同步鎖的使用方式一
synchronized(鎖對象){
? ? ? ? 原子性操作代碼;
}
加入同步鎖的存款任務?
public class DepositRunnable implements Runnable{private AcountService service = new AcountService();private Acount acount;private Double money;public DepositRunnable(Acount acount, Double money) {this.acount = acount;this.money = money;}@Overridepublic void run() {synchronized (acount) {System.out.println(Thread.currentThread().getName() + "-存款任務開始!");service.deposit(acount, money);System.out.println(Thread.currentThread().getName() + "-存款成功!" + acount.getMoney());System.out.println(Thread.currentThread().getName() + "-存款任務結(jié)束!");}}
}
?加入同步鎖的取款任務
public class DrawRunnable implements Runnable {private AcountService service = new AcountService();private Acount acount;private Double money;public DrawRunnable(Acount acount, Double money) {this.acount = acount;this.money = money;}@Overridepublic void run() {synchronized (acount) {System.out.println(Thread.currentThread().getName() + "-取款開始!");try {service.draw(acount, money);System.out.println(Thread.currentThread().getName() + "-取款成功!" + acount.getMoney());} catch (Exception e) {e.printStackTrace();System.out.println(Thread.currentThread().getName() + "-" + e.getMessage() + "取款失敗!" + acount.getMoney());}System.out.println(Thread.currentThread().getName() + "-取款結(jié)束!");}}
}
測試類:
public class Test {public static void main(String[] args) {Acount acount = new Acount(10000.0);//臨界資源DepositRunnable depositRunnable01 = new DepositRunnable(acount,10000.0);DepositRunnable depositRunnable02 = new DepositRunnable(acount,2000.0);DrawRunnable drawRunnable01 = new DrawRunnable(acount,5000.0);DrawRunnable drawRunnable02 = new DrawRunnable(acount,5000.0);Thread t1 = new Thread(depositRunnable01,"自己");Thread t2 = new Thread(depositRunnable02,"父母");Thread t3 = new Thread(drawRunnable01,"女兒");Thread t4 = new Thread(drawRunnable02,"老婆");t1.start();t2.start();t3.start();t4.start();}
}