28线程
進程:計算機執行的任務
線程:執行任務中的小任務 多線程
計算機再執行過程中,再同一時間只能讓cpu的一個核執行一個進程。進程有多個線程構成,再同一時刻Cpu只能處理一個線程。
引入多線程
? 當線程被cpu執行時cpu開始工作,線程需要和軟硬件進行交互,這個時候cpu就是處于空閑狀態
引用多線程可以提高cpu的使用效率
?
創建多線程的方式-----(Thead)
1.繼承Thread類,重寫run方法(線程代碼邏輯所在的地方,調用start方法,開啟線程. 有一個不好的地方就是java是單繼承所以我們繼承了Thread后就不能繼承其他類
,所以我們通常采用第二種方法 實現接口
?
public class ThreadDemo {public static void main(String[] args) {//線程執行---執行線程邏輯所在的類Demo d=new Demo();//標記線程可以被cpu執行 d.start();for(int i=0;i<10;i++){System.out.println("main:"+i);}}} //線程任務的執行的代碼邏輯 class Demo extends Thread{//重寫方法----實現線程的代碼邏輯 @Overridepublic void run() {for(int i=0;i<10;i++){System.out.println("i"+i);}} }?
?
?
?2.實現Runnable接口,重寫run方法(線程代碼邏輯),通過Runnable接口的實現類對象構建Thread類對象,調用start方法開啟線程
?
public class ThreadDemo2 {public static void main(String[] args) {//通過Runnable實現類對象構建Thread類對象Thread t=new Thread(new TDemo() );//開啟線程 t.start();for(int i=0;i<10;i++){System.out.println("main:"+i);}}} //線程代碼邏輯所在類,實現Runnable接口 class TDemo implements Runnable{//重寫方法 --線程代碼邏輯 @Overridepublic void run() {for(int i=0;i<10;i++){System.out.println("i"+i);}} }?
?
3.實現Callable接口,重寫call方法(現階段了解就好)
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future;public class ThreadDemo3 { public static void main(String[] args) throws InterruptedException, ExecutionException {//創建線程類對象DTDemo1 dt=new DTDemo1();//獲取執行服務器,ExecutorService e=Executors.newCachedThreadPool();//把你想要操作的東西放到執行服務器上Future<Integer> f =e.submit(dt);System.out.println(f.get()); } } //Integer 重寫方法返回值類型 class DTDemo1 implements Callable<Integer>{//重寫方法----線程代碼邏輯所在的地方 @Overridepublic Integer call() throws Exception {// TODO Auto-generated method stubreturn 20;}}因為底層多線程之間存在搶占問題,搶占發生再代碼的每一步,導致了數據安全問題
為了排除我們多線程的搶占問題我們采用加鎖的策略,有兩種 1.同步代碼塊鎖,同步方法鎖
同步代碼塊鎖--synchtonized(鎖對象){} ----鎖對象指的是 可以被線程共享--方法區里的內容可以被所有線程共享(對多少個線程對象進行加鎖,這些對象都是同步的)
同步方法鎖--在方法上加上synchronized,如果這個方法是靜態方法鎖對象就是類名.class,如果這個方法是非靜態方法鎖對象就是this,構造器和屬性上不能夾synchronized
同步:多個線程每次只能執行一個(一個一個)
異步:多個線程每次可以執行多個(搶占)
同步一定是安全的
安全的不一定是同步
不安全一定是異步
異步不一定不安全
從微觀上同步一定是安全的,異步一定是不安全的
?
public class SellTicketDemo {public static void main(String[] args) {//創建票類對象Ticket t=new Ticket();//設置票數t.setCount(100);//四個售票員Seller s1=new Seller(t);Seller s2=new Seller(t);Seller s3=new Seller(t);Seller s4=new Seller(t);//開啟線程 并給每個線程new Thread(s1,"A").start();new Thread(s2,"B").start();new Thread(s3,"C").start();new Thread(s4,"D").start();} } //模擬賣票的過程---線程的代碼邏輯 class Seller implements Runnable{//引入票類 Ticket t;//有參構造public Seller(Ticket t) {this.t=t;}//線程的代碼邏輯---買票的過程//同步方法鎖是直接加在方法上 同步方法鎖如果是非靜態方法那么他的鎖對象是this//如果是靜態方法的話,那么鎖對象就是類名.class @Overridepublic synchronized void run() {while(true){ //同步代碼塊鎖---()中的是鎖對象 ----被線程共享,只要是能被所有對象共享的就可以,鎖對象必須被所有被執行的線程共享 方法區中的就可以因為方法區(是被所有的線程共享的)但是范圍太大了,能小的鎖就小得鎖synchronized (Seller.class) {if(t.getCount()<=0) //票買完的時候就是票數為0 {break;}//設置新的票數t.setCount(t.getCount()-1);//打印出具體是那個售票員賣的----具體是那個線程執行的//Thread.currentThread()當前正在執行的線程System.out.println(Thread.currentThread().getName()+"買了一張票,還剩"+t.getCount()+"票");}}}}//表示票類 class Ticket{//屬性//票數private int count;public int getCount() {return count;}public void setCount(int count) {this.count = count;}}?鎖之間的相互嵌套----死鎖
?
public class DeadLoackDemo {// static Print p=new Print();static Scan s=new Scan();public static void main(String[] args) {//開啟線程new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stub//打印信息synchronized (p) {p.print();//讓線程進行休眠---線程釋放執行權try { Thread.sleep(20);} catch (InterruptedException e) {// TODO Auto-generated catch block e.printStackTrace();}//掃描synchronized (s) {s.sacnn();}}}}).start();new Thread(new Runnable() {@Overridepublic void run() {// TODO Auto-generated method stub//先掃描synchronized (s) {s.sacnn();synchronized (p) {p.print();}}}}).start();} }//打印機 class Print{public void print(){System.out.println("在打印東西...");} }//掃描儀 class Scan{public void sacnn(){System.out.println("在掃描信息...");} }?
?
?如果破解死鎖:上面的死鎖出現的問題是兩個線程需要同一個鎖,如果一個走一個等待那么就不會產生死鎖現象,所以我們需要控制鎖一個鎖走完再讓另一個線程獲取這個鎖
package cn.tedu.thread;public class WaitNotifyDemo {public static void main(String[] args) {//創建學生類對象Student s=new Student();s.setName("lili");s.setGender('女');//開啟線程new Thread(new Ask(s)).start();new Thread(new Change(s)).start();} }//線程所在的類---問問題 class Ask implements Runnable{// 引入學生類對象private Student s;public Ask(Student s){this.s=s;}@Overridepublic void run() {// TODO Auto-generated method stub//表示問問題的結果while(true){synchronized (s) { //防止多線程搶占,保證性別//釋放線程執行權---等待if(s.flag==false)try {//讓線程等待----相當于堵塞主要是為了挨個回答問題 s.wait();} catch (InterruptedException e) {// TODO Auto-generated catch block e.printStackTrace();}//輸出 System.out.println("老師你好,我是"+s.getName()+",是一個"+s.getGender()+"生,想問問題...");//喚醒線程 s.notify();//改變布爾值s.flag=false;}}}}//線程所在的類---換學生 class Change implements Runnable{//引入學生類對象private Student s;public Change(Student s){this.s=s;}@Overridepublic void run() {// TODO Auto-generated method stubwhile(true){synchronized (s) { //防止多線程的搶占---保證性別//線程等待if(s.flag==true)try {s.wait();} catch (InterruptedException e) {// TODO Auto-generated catch block e.printStackTrace();}// if (s.getName().equals("tom")) {s.setName("lili");s.setGender('女');} else {s.setName("tom");s.setGender('男');}//線程喚醒 s.notify(); //喚醒阻塞的線程,CPU可以將阻塞的線程搶執行權了。//改變布爾值s.flag=true;}}}}//學生類 class Student{//屬性private String name;private char gender;// boolean flag=true;public char getGender() {return gender;}public void setGender(char gender) {this.gender = gender;}public String getName() {return name;}public void setName(String name) {this.name = name;}}?wait、notify、notifyAll是線程中通信可以使用的方法。線程中調用了wait方法,則進入阻塞狀態,只有等另一個線程調用與wait同一個對象的notify方法。這里有個特殊的地方,調用wait或者notify,前提是需要獲取鎖,也就是說,需要在同步塊中做以上操作。
?
?等待喚醒的前提喚醒一個 是得有鎖
notify()隨機喚醒 肯定給你喚醒一個 作用 喚醒阻塞的線程,CPU可以將阻塞的線程搶執行權了。兩個對象的話就是一個線程執行一次,因為只有一種情況,一個線程再執行,另一個再阻塞wait和sleep的區別
sleep--用于是線程進入休眠狀態(需要制定休眠的時間,到了這個時間才會喚醒),在其s0leep時間段內,該線程不會獲得執行的機會,即使系統種沒有其他可以運行的線程如果線程沒有加鎖,就會釋放線程的執行權,如果加鎖就不會釋放執行權,但是會有CPU的切換 ,可以指定休眠時間 這是Thread的靜態方法
wait---如果指定等待時間,就必須等到時間結束才能喚醒,如果不指定時間就只能手動喚醒,如果線程加鎖就會釋放鎖也能釋放執行權,如果沒有加鎖就釋放執行權,是Object里的普通方法
線程狀態:
當線程被創建并啟動以后,它既不是以啟動就進入了執行狀態,也不是一致處于執行狀態,在線程的生命周期中,他要徑路新建,就緒,運行,阻塞和死亡5種狀態。 尤其是線程啟動以后,它不能一直“霸占”
著CPU獨自運行,所以CPU需要在多條線程之間切換,于是線程狀態也會多次在運行、阻塞之間切換
1.新建:當程序使用new關鍵字創建了一個線程之后,該線程就處于新建狀態,此時它和其他java對象一樣,僅僅有java虛擬機為其分配了內存,并初始化了其成員變量的值。此時的線程對象沒有表現出任何線程的動態特征,程序也不會執行線程的線程執行體。
2.就緒狀態:當線程對象調用了start()方法之后,該線程處于就緒狀態,java虛擬機會為其創建方法調用棧和程序計數器,處于這個狀態下的線程并沒有開始運行,它只是表示該線程可以運行了,至于該線程何時開始運行,取決于jvm里線程調度器的調度
不要對已經處于啟動狀態的線程再次調用start方法,否則將引發IllegalThreadStateException異常
如果程序希望調用子線程的start()方法后子線程立即開始執行,程序可以使用Thread.sleep(1)來讓當前運行的線程(主線程)睡眠一毫秒---1毫秒就夠了,因為在這1毫秒內CPU不會空閑,它就會去執行另一條就緒狀態的線程,這樣就可以讓我們的子線程立即獲得執行
?
?
?
?
?
?
?
守護線程
需要手動開啟,如果被守護線程執行結束,守護線程也隨著結束,反之不是 如果有多個線程,除了守護
線程,其他的都是被守護線程 ,java中最大的守護線程是GC
package cn.tedu.thread;public class DemonDemo {public static void main(String[] args) {//創建出小兵對象Thread t1=new Thread(new Soilder(),"小兵1");Thread t2=new Thread(new Soilder(),"小兵2");Thread t3=new Thread(new Soilder(),"小兵3");Thread t4=new Thread(new Soilder(),"小兵4");//設置守護線程t1.setDaemon(true); //true代表手動開啟守護線程t2.setDaemon(true);t3.setDaemon(true);t4.setDaemon(true);//開啟線程 t1.start();t2.start();t3.start();t4.start();//被守護線程for(int i=10;i>=0;i--){System.out.println("boss剩余"+i);}}}//線程類---小兵 class Soilder implements Runnable{@Overridepublic void run() {// TODO Auto-generated method stub//輸出每個小兵的剩余血量for(int i=10;i>=0;i--){System.out.println(Thread.currentThread().getName()+"還剩"+i+"滴血...");}//線程走的太快就讓慢點,方便自己查看結果try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch block e.printStackTrace();}}}前臺線程死亡后,jvm會通知后臺線程死亡,但從它接受命令,到它做出響應,需要一定時間,而且要將某個線程設置為后臺線程,必須在該線程啟動之前設置,也就是說setDaemon(true)必須在start()方法調用之前否則會引發IllegalThreadStateException
?
?
線程優先級:
優先級(1-10),理論上優先級越大越有機會搶到執行權,理論上如果線程1與線程2之間的優先級之差大于5,那么線程1強到執行權的機會比線程2大一點。就算你設置成10和1差距也不大,功能比較雞肋
package cn.tedu.thread;public class PririotyDemo {public static void main(String[] args) {Thread t1=new Thread(new PDemo(),"A");Thread t2=new Thread(new PDemo(),"B");//設置優先級t1.setPriority(1);t2.setPriority(9);//開啟線程 t1.start();t2.start();}}class PDemo implements Runnable{@Overridepublic void run() {// TODO Auto-generated method stubfor(int i=0;i<10;i++){System.out.println(Thread.currentThread().getName());}}// }?生產者和消費者問題
?
//生產消費模型 public class WaitNotifyText {public static void main(String[] args) {//商品對象Product p=new Product();//開啟線程new Thread(new Productor(p)).start();new Thread(new Productor(p)).start();new Thread(new Consumer(p)).start();new Thread(new Consumer(p)).start();} }//模擬生產過程---線程邏輯代碼 class Productor implements Runnable{//引入商品類 Product p;public Productor(Product p){this.p=p;}//重寫 @Overridepublic void run() {while (true) {synchronized (p) {while(p.flag==true)//加上while保證線程一定會進行判斷try {p.wait();} catch (InterruptedException e) {// TODO Auto-generated catch block e.printStackTrace();}// TODO Auto-generated method stub// 此時生產的最大值int max = 1000 - p.getNum();// 減去上次剩余// 隨機生產的商品數量int count = (int) (Math.random() * (max + 1));// 設置新的商品數量p.setNum(p.getNum() + count);// 輸出System.out.println("此次生產了" + count + "個商品,還剩余" + p.getNum() + "個商品...");//喚醒//p.notify();//隨機喚醒一個 p.notifyAll();p.flag=true;}}}}//模擬消費過程 class Consumer implements Runnable{// 引入商品類 Product p;public Consumer(Product p){this.p=p;}@Overridepublic void run() {// TODO Auto-generated method stubwhile (true) {synchronized (p) {while(p.flag==false)try {p.wait();} catch (InterruptedException e) {// TODO Auto-generated catch block e.printStackTrace();}// 此次消費最大值int max = p.getNum();// 此次消費的隨機商品數量int count = (int) (Math.random() * (max + 1));// 設置新的商品數量p.setNum(p.getNum() - count);// 輸出System.out.println("此次消費了" + count + "個商品,還剩余" + p.getNum() + "個商品...");//喚醒//p.notify(); p.notifyAll();p.flag=false;}}}}//商品類 class Product{//商品數量private int num;// boolean flag=true;public int getNum() {return num;}public void setNum(int num) {this.num = num;}}?
轉載于:https://www.cnblogs.com/xuwangqi/p/11249416.html
總結