多線程
并發執行的技術
并發和并行
并發:同一時間 有多個指令 在單個CPU上?交替執行
并行:同一時間 有多個指令 在多個CPU上 執行
進程和線程
進程:獨立運行 任何進程 都可以同其他進程一起?并發執行
線程:是進程中的單個順序控制流 是一條執行路徑 線程又分為單線程和多線程
單線程:
多線程:JVM是多線程 其實就是JVM的主線程和垃圾回收線程在工作 結果在切換著運行
多線程的運行方式:他們擁有相等的運行權限 但運行過程是誰先搶到CPU的運行權限那么誰就先運行
線程的五種狀態:新建,就緒,運行,堵塞,死亡
線程的實現:
方式一:繼承Thread類
建立一個類繼承Thread類 那么這類創建的對象可以并發的作為獨立線程運行
繼承的好處是可以使用父類中的方法 比較簡單
繼承的弊端是如果已經有父類了 就不可以再繼承了 一個子類只能繼承一個父類
public class Demo02 {public static void main(String[] args) {MyThread1 m1=new MyThread1();MyThread2 m2=new MyThread2();MyThread3 m3=new MyThread3();//普通方法/* m1.run();m2.run();m3.run();*///start開啟新線程 內部會根據cpu的分配自動執行runm1.start();m2.start();m3.start();//相當于跑步比賽的發令槍 啟動后主線程與其他線程都會等待搶到cpu的執行權//誰先搶到誰運行}
}
//建立一個類繼承Thread類 那么這類創建的對象可以并發的作為獨立線程運行
class MyThread1 extends Thread{public void run(){for (int i = 0; i <10000000 ; i++) {System.out.println("1號線程");}}
}
class MyThread2 extends Thread{public void run(){for (int i = 0; i <10000000 ; i++) {System.out.println("2號線程");}}
}
class MyThread3 extends Thread{public void run(){for (int i = 0; i <10000000 ; i++) {System.out.println("3號線程");}}
}
方式二:實現Runnable接口
建立一個類實現Runnable接口
接口的好處是就算實現了其他接口 也可以使用
接口的弊端 操作不便利 需要先獲取Thread線程對象
public class Demo03 {public static void main(String[] args) {My1 my1=new My1();Thread t1=new Thread(my1);My2 my2=new My2();Thread t2=new Thread(my2);t1.start();t2.start();}
}
//定義一個類 實現Runnable接口 重新run方法class My1 implements Runnable{@Overridepublic void run() {for (int i = 0; i <10000000 ; i++) {System.out.println("1號線程");}}
}class My2 implements Runnable{@Overridepublic void run() {for (int i = 0; i <10000000 ; i++) {System.out.println("2號線程");}}
}
start()方法把當前線程交給了底層的? ThreadGroup group[]數組管理
Jvm根據隨機分配的cpu執行權調用run()? 隨機分配的cpu執行權是相等概率的
匿名內部類實現線程
方式一:繼承Thread類
public static void main(String[] args) {new Thread(){public void run(){for(int i=0;i<10000000;i++){new Student();System.out.println("線程1");}}}.start();new Thread(){public void run(){for(int i=0;i<10000000;i++){new Student();System.out.println("線程2");}}}.start();}
方式二:實現Runnable接口
public static void main(String[] args) {new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i <10000000 ; i++) {System.out.println("1號線程");}}}).start();new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i <10000000 ; i++) {System.out.println("2號線程");}}}).start();}
有名內部類實現線程
public static void main(String[] args) {Thread t1=new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i <10000000 ; i++) {System.out.println("1號線程");}}});Thread t2=new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i <10000000 ; i++) {System.out.println("2號線程");}}});t1.start();t2.start();}
設置名字和獲取名字(getName()?setName())
可以在運行中獲取當前線程的對象Thread.currentThread()
public class Demo06 {public static void main(String[] args) {Thread t1=new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i <10000000 ; i++) {//Thread.currentThread()獲取當前運行的線程對象//獲取名字getName()System.out.println(Thread.currentThread().getName());}}});Thread t2=new Thread(new Runnable() {@Overridepublic void run() {for (int i = 0; i <10000000 ; i++) {System.out.println(Thread.currentThread().getName());}}});//設置名字t1.setName("線程1");t1.start();t2.setName("線程2");t2.start();}
}
通過構造方法給線程取名
public class Demo06 {public static void main(String[] args){//通過構造方法給線程取名Thread t1=new Thread(new M1(),"my1");Thread t2=new Thread(new M2(),"my2");t1.start();t2.start();}
}
class M1 implements Runnable{@Overridepublic void run() {for (int i = 0; i <100000 ; i++) {System.out.println(Thread.currentThread().getName());}}
}
class M2 implements Runnable{@Overridepublic void run() {for (int i = 0; i <100000 ; i++) {System.out.println(Thread.currentThread().getName());}}
}
休眠
Thread.sleep(100);讓當前線程休眠100毫秒
class MM1 implements Runnable{@Overridepublic void run() {for (int i = 0; i <100000 ; i++) {System.out.println(Thread.currentThread().getName());try {//讓當前線程休眠100毫秒Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}}}
守護線程
線程.setDaemon(true);
設置一個線程為守護線程 該線程不會單獨執行 當其他非守護線程都執行結束后 再自動退出
public class Demo08 {public static void main(String[] args) {M2Thread m2=new M2Thread();m2.setName("線程2");m2.start();M1Thread m1=new M1Thread();m1.setName("線程1");m1.start();//守護線程不會單獨執行Thread1 t=new Thread1();t.setName("守護線程");t.setDaemon(true);//設置true為守護線程 主線程結束 守護線程也結束t.start();}
}
class M1Thread extends Thread{public void run(){for (int i = 0; i <100 ; i++) {System.out.println(Thread.currentThread().getName());}}
}
class M2Thread extends Thread{public void run(){for (int i = 0; i <100 ; i++) {System.out.println(Thread.currentThread().getName());}}
}
class Thread1 extends Thread{public void run(){for (int i = 0; i <10000 ; i++) {System.out.println(Thread.currentThread().getName()+i);}}
}
加入線程
線程.join(可以限定時間 也可以不限定);
public static void main(String[] args) {Thread t1=new Thread("女朋友的電話"){public void run(){for (int i = 0; i <100 ; i++) {System.out.println(Thread.currentThread().getName()+i);}}};Thread t2=new Thread("老板的電話"){public void run(){for (int i = 0; i <100 ; i++) {System.out.println(Thread.currentThread().getName()+i);if(i==50){try {//加入女朋友的線程 基本全面占有cpu執行權// t2線程要等待t1線程執行完畢后才可以繼續執行t1.join();//可以限定時間} catch (InterruptedException e) {throw new RuntimeException(e);}}}}};t1.start();t2.start();}
禮讓(了解)
Thread.yield();//讓出cpu的執行權給別的線程
設置優先級:(優先級1-10 默認為5)
線程.setPriority(10);
同步(同步鎖,同步代碼塊)
注意:開發中盡量不要嵌套同步代碼塊 可能會死鎖
關鍵字:synchronized(同步鎖使用的多)
public class Test {String s="123";//同步鎖 在方法中加synchronized關鍵字//public synchronized void show1(){} public synchronized void show1(){System.out.print("我");System.out.print("愛");System.out.print("學");System.out.print("習");System.out.println("-----------");}public void show2(){//同步代碼塊synchronized(s鎖對象){}//多個同步代碼塊如果使用相同的鎖對象 那么他們就是同步的synchronized(s){ System.out.print("想");System.out.print("睡");System.out.print("覺");System.out.println("===========");}}
}
線程安全
synchronized線程安全? 沒有synchronized線程不安全
Vector線程安全? ArrayList線程不安全
Stringbuffer安全? stringBuilder不安全
HashTable安全? HashMap不安全
設計模式
單例設計模式
public class Single {private Single(){//私有構造方法 不讓其他類new對象}static Single s=new Single();
}
public static void main(String[] args) {Single s1=Single.s;Single s2=Single.s;System.out.println(s1==s2);//trueSingle s3=null;//對象可以被修改Single s4=Single.s;System.out.println(s3==s4);//false}
餓漢式(直接加載)
節約時間 浪費空間(例如:安卓手機應用可以在后臺運行)
一旦被加載進來 就創建好了對象 不管是否使用 都在內存中
public class Single1 {private Single1(){//私有構造方法 不讓其他類new對象}static Single1 s=new Single1();//餓漢式public static Single1 getInstance(){//一旦被加載進來 就創建好了對象 不管是否使用 都在內存中return s;}
}
public static void main(String[] args) {Single s1=Single.getInstance();Single s2=Single.getInstance();System.out.println(s1==s2);//true}
懶漢式(延遲加載)
節約空間 浪費時間(例如:蘋果手機應用不掛后臺)
什么時候用 什么時候才創建
public class Single2 {private Single2(){}public static Single2 s=null;//懶漢式public static Single2 Instance(){//需要的時候才創建對象if(s==null){s=new Single2();}return s;}
}
Runtime類單例(查API)
每個 Java 應用程序都有一個Runtime
類實例 使應用程序能夠與其運行的環境相連接 可以通過 getRuntime
方法獲取當前運行時。
應用程序不能創建自己的 Runtime
類實例
public static void main(String[] args) {Runtime r=Runtime.getRuntime();try {//r.exec("mspaint");//打開畫圖//r.exec("notepad");//打開記事本//r.exec("shutdown -s -t 6000");//6000秒后自動關機//r.exec("shurdown -a");//取消自動關機//r.exec("百度去");} catch (IOException e) {throw new RuntimeException(e);}}
Timer類(計時器)
用來定時執行程序的類
public class MyTask extends TimerTask {@Overridepublic void run() {System.out.println("起床了");}
}
public static void main(String[] args) {//用來定時執行程序的類Timer t=new Timer();//5秒后執行任務t.schedule(new MyTask(),new Date(System.currentTimeMillis()+5000));//5秒后執行任務 每隔1秒重復執行t.schedule(new MyTask(),new Date(System.currentTimeMillis()+5000),1000);}
線程通信(等待,喚醒)
必須在同步代碼塊中 使用同步鎖對象調用(喚醒 等待 喚醒 等待)代碼交替運行
并發執行時 默認情況下 cpu的執行權 是隨機的 誰先搶到誰先執行
如果希望有規律的執行 就可以用線程通信
notify喚醒 wait等待
案例:兩個方法交替
public class TestThread2 {public synchronized void print(){System.out.print("我");System.out.print("愛");System.out.print("學");System.out.print("習");System.out.println("-----------");this.notify();//喚醒正在等待的線程try {this.wait();//讓當前線程等待 讓出cpu執行權 進入就緒狀態} catch (InterruptedException e) {throw new RuntimeException(e);}}public synchronized void show(){System.out.print("想");System.out.print("睡");System.out.print("覺");System.out.println("===========");this.notify();//喚醒正在等待的線程try {this.wait();//讓當前線程等待 讓出cpu執行權 進入就緒狀態} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
public static void main(String[] args) {TestThread2 tt2=new TestThread2();new Thread(){public void run(){while(true){ tt2.print();}}}.start();new Thread(){public void run(){while(true){ tt2.show();}}}.start();}
案例:三個和三個以上方法交替
public class TestThread3 {boolean b1=true;boolean b2=false;boolean b3=false;public synchronized void show1() {if (b3) {System.out.print("我");System.out.print("愛");System.out.print("學");System.out.print("習");System.out.println("-----------");}b1 = true;b3 = false;this.notifyAll();//喚醒所有正在等待的線程try {this.wait();//讓當前線程等待 讓出cpu執行權 進入就緒狀態} catch (InterruptedException e) {throw new RuntimeException(e);}}public synchronized void show2(){if(b1){System.out.print("想");System.out.print("睡");System.out.print("覺");System.out.println("===========");}b2=true;b1=false;this.notifyAll();//喚醒所有正在等待的線程try {this.wait();//讓當前線程等待 讓出cpu執行權 進入就緒狀態} catch (InterruptedException e) {throw new RuntimeException(e);}}public synchronized void show3(){if(b2){System.out.print("想");System.out.print("吃");System.out.print("飯");System.out.println("~~~~~~~~~~~");}b2=false;b3=true;this.notifyAll();//喚醒所有正在等待的線程try {this.wait();//讓當前線程等待 讓出cpu執行權 進入就緒狀態} catch (InterruptedException e) {throw new RuntimeException(e);}}
}
public static void main(String[] args) {TestThread3 tt3=new TestThread3();new Thread(){public void run(){while(true){tt3.show1();}}}.start();new Thread(){public void run(){while(true){tt3.show2();}}}.start();new Thread(){public void run(){while(true){tt3.show3();}}}.start();}
wait和sleep區別
wait():釋放鎖 等待并釋放鎖讓別的線程運行
sleep():不釋放鎖 在指定的時間抱著鎖睡 時間一到馬上醒來