java執行緒基礎知識整理
目錄
執行緒基本概念
1、什麼是程序?什麼是執行緒?
程序是一個應用程式,執行緒是一個程序中的執行場景/執行單元。一個程序可以啟動多個執行緒。在java語言中對於兩個執行緒A和B,堆記憶體和方法區記憶體共享。但是棧記憶體獨立,一個執行緒一個棧。在使用了多執行緒機制之後,main()方法結束了,只是主執行緒結束了,主棧空了,但其他執行緒不一定結束,其他棧(執行緒)可能還在壓棧彈棧。
1、java實現執行緒
java語言支援多執行緒機制。並且java已經實現了多執行緒(java.lang.Thread類和java.lang.Runnable介面)
第一種實現方式(繼承java.lang.Thread類並重寫run方法)
- public class Thread_01 extends Thread {
- public void run() {
- super.run();
- System.out.println("第一個執行緒");
- }
- public static void main(String[] args) {
-
Thread thread=new Thread(new Thread_01(),"first_thread"); //建立執行緒物件
- thread.start(); //啟動執行緒 start方法使執行緒處於就緒佇列,等待CPU呼叫
- }
- }
第二種實現方式(實現java.lang.Runnable介面並實現run方法)
- public class Thread_02 implements Runnable {
- public void run() {
- System.out.println("第一個執行緒");
- }
- public static void main(String[] args) {
- Thread thread=new Thread(new Thread_01(),"first_thread"); //建立執行緒物件
- thread.start(); //啟動執行緒 start方法使執行緒處於就緒佇列,等待CPU呼叫
- }
- }
通常使用第二種方法,因為一個類實現了介面還可以繼承其他類
注意:start方法和run方法的區別!!!
run方法不會啟動執行緒。
start方法的作用是:啟動一個執行緒,在JVM中為執行緒開闢一個新的棧空間。之後start方法就結束了。執行緒啟動成功並進入排隊等待序列。等到被CPU呼叫到,就會自動呼叫run方法。
2、執行緒的生命週期
3、執行緒常用的方法
3.1、sleep()
public static native void sleep(long millis) throws InterruptedException;
作用:讓執行緒進入休眠,進入“阻塞狀態”。放棄佔有CPU時間片,讓其他執行緒使用。
- public class Thread_03 {
- public static void main(String[] args) throws InterruptedException {
- Thread thread=new Thread(new Thread_03_1(),"Thread_03_1");
- thread.start();
- int count=1; //計數器
- while(true){
- System.out.println("Thread_03_1執行緒沉睡了"+count+++"秒");
- Thread.sleep(1000);
- if(count>5){
- break;
- }
- }
- }
- }
- class Thread_03_1 implements Runnable{
- public void run() {
- try {
- Thread.sleep(1000*5); //讓執行緒沉睡(等待) 5秒
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName());
- }
- }
3.2、interrupt方法
interrupt方法可以中斷執行緒的睡眠,依靠了java異常處理機制
- public class Thread_03 {
- public static void main(String[] args) throws InterruptedException {
- Thread thread=new Thread(new Thread_03_1(),"Thread_03_1");
- thread.start();
- int count=1; //計數器
- while(true){
- System.out.println("Thread_03_1執行緒沉睡了"+count+++"秒");
- Thread.sleep(1000);
- if(count==3){
- System.out.println("打斷Thread_03_1睡眠");
- thread.interrupt();
- break;
- }
- }
- }
- }
- class Thread_03_1 implements Runnable{
- public void run() {
- try {
- Thread.sleep(1000*5); //讓執行緒沉睡(等待) 5秒
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName()+"醒了");
- }
- }
執行緒Thread_03_1原計劃沉睡5秒,在它睡到3秒時,使用interrupt方法打斷其睡眠。
3.3、stop方法
stop方法可以強制終止一個執行緒的執行。不過這種方式容易丟失資料。因為這種方式會直接殺死執行緒,執行緒沒有儲存的資料會丟失。所以不建議使用
- public class Thread_04 {
- public static void main(String[] args) throws InterruptedException {
- Thread thread = new Thread(new Thread_04_1(), "Thread_03_1");
- thread.start();
- //執行緒Thread_03_1執行5秒後,強制結束執行緒Thread_03_1
- Thread.sleep(1000*6);
- System.out.println("強制終止執行緒Thread_03_1");
- thread.stop(); //強制終止執行緒
- }
- }
- class Thread_04_1 implements Runnable{
- public void run() {
- for(int i=1;i<1000;i++) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread()+"-->"+i+"秒");
- }
- }
- }
建議使用如下方法結束一個執行緒:線上程類中增加一個布林型別的變數run,通過改變run的值,來控制執行緒執行/停止狀態。
- public class Thread_05 {
- public static void main(String[] args) throws InterruptedException {
- Thread_05_1 t=new Thread_05_1();
- Thread thread = new Thread(t, "Thread_05_1");
- thread.start();
- //等候5秒之後,終止該執行緒
- Thread.sleep(1000*5);
- t.run=false;
- System.out.println(thread.getName()+"執行緒已暫停");
- }
- }
- class Thread_05_1 implements Runnable{
- boolean run=true; //通過引入一個布林型別的變數,來標記該執行緒的狀態(執行/停止)
- public void run() {
- for (int i = 1; i < 1000; i++) {
- if (run) {
- System.out.println(Thread.currentThread().getName() + "-->" + i + "秒");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }else{
- /**
- * return就表示該執行緒結束了
- * 如果有什麼需要儲存的,可以寫在return之前
- */
- return;
- }
- }
- }
- }
4、執行緒排程
4.1、常見的執行緒排程模型
搶佔式排程模型、均分式排程模型
4.2、java中提供的執行緒排程方法
- void setPriority(int newPriority) //設定執行緒優先順序
- int getPriority() //獲取執行緒優先順序
最低優先順序:1 預設優先順序:5 最高優先順序:10
優先順序高的執行緒搶佔的CPU時間片就多一些,處於執行狀態的時間片就多一些
- public class Thread_06 {
- public static void main(String[] args) {
- Thread_06_1 t61=new Thread_06_1();
- Thread_06_2 t62=new Thread_06_2();
- Thread_06_3 t63=new Thread_06_3();
- Thread thread1=new Thread(t61,"Thread_06_1");
- Thread thread2=new Thread(t62,"Thread_06_2");
- Thread thread3=new Thread(t63,"Thread_06_3");
- //設定執行緒優先順序
- thread1.setPriority(1); thread2.setPriority(2); thread3.setPriority(10);
- thread1.start(); thread2.start(); thread3.start();
- System.out.println("主執行緒優先順序為:"+Thread.currentThread().getPriority());
- for(int i=0;i<1000;i++){
- System.out.println(Thread.currentThread().getName()+"-->"+i);
- }
- }
- }
- class Thread_06_1 implements Runnable{
- public void run() {
- System.out.println(Thread.currentThread().getName()+"優先順序為:"+Thread.currentThread().getPriority());
- for(int i=0;i<1000;i++){
- System.out.println(Thread.currentThread().getName()+"-->"+i);
- }
- }
- }
- class Thread_06_2 implements Runnable{
- public void run() {
- System.out.println(Thread.currentThread().getName()+"優先順序為:"+Thread.currentThread().getPriority());
- for(int i=0;i<1000;i++){
- System.out.println(Thread.currentThread().getName()+"-->"+i);
- }
- }
- }
- class Thread_06_3 implements Runnable{
- public void run() {
- System.out.println(Thread.currentThread().getName()+"優先順序為:"+Thread.currentThread().getPriority());
- for(int i=0;i<1000;i++){
- System.out.println(Thread.currentThread().getName()+"-->"+i);
- }
- }
- }
4.3、執行緒讓步
yield方法:使當前執行緒暫停,回到就緒狀態,讓給其他執行緒
public static native void yield();
- public class Thread_07 {
- public static void main(String[] args) {
- Thread_07_1 tt=new Thread_07_1();
- Thread thread=new Thread(tt,"Thread_07_1");
- thread.start();
- for(int i=0;i<100;i++){
- System.out.println(Thread.currentThread().getName()+"-->"+i);
- }
- }
- }
- class Thread_07_1 implements Runnable{
- public void run() {
- for(int i=0;i<100;i++){
- if(i%10==0){
- System.out.println(Thread.currentThread().getName()+"暫停了一下");
- Thread.yield(); //讓當前執行緒暫停一下
- }
- System.out.println(Thread.currentThread().getName()+"-->"+i);
- }
- }
- }
4.4、執行緒合併
join方法可以使得在t.join()中讓CPU優先執行完t。將t合併到當前執行緒中,使當前執行緒受阻,t執行緒執行直到結束。
- public class Thread_08 {
- public static void main(String[] args) throws InterruptedException {
- Thread_08_1 thread_08_1=new Thread_08_1();
- Thread_08_2 thread_08_2=new Thread_08_2();
- Thread thread=new Thread(thread_08_1,"Thread_08_1");
- Thread thread1=new Thread(thread_08_2,"Thread_08_2");
- thread.start();
- //合併執行緒
- thread.join(); //t合併到當前執行緒中,當前執行緒受阻塞,t執行緒執行直到結束
- thread1.start();
- thread1.join();
- //Thread.currentThread().join();
- System.out.println("main over");
- }
- }
- class Thread_08_1 implements Runnable{
- public void run() {
- for(int i=0;i<3;i++){
- System.out.println(Thread.currentThread().getName()+"-->"+i);
- }
- }
- }
- class Thread_08_2 implements Runnable{
- public void run() {
- for(int i=0;i<3;i++){
- System.out.println(Thread.currentThread().getName()+"-->"+i);
- }
- }
- }
join()的底層實現程式碼。
- public final void join() throws InterruptedException {
- join(0);
- }
- public final synchronized void join(long millis)
- throws InterruptedException {
- long base = System.currentTimeMillis();
- long now = 0;
- if (millis < 0) {
- throw new IllegalArgumentException("timeout value is negative");
- }
- if (millis == 0) {
- while (isAlive()) {
- wait(0);
- }
- } else {
- while (isAlive()) {
- long delay = millis - now;
- if (delay <= 0) {
- break;
- }
- wait(delay);
- now = System.currentTimeMillis() - base;
- }
- }
- }
join()是在底層呼叫了wait方法,當主執行緒呼叫了thread.join()之後,主執行緒進入此方法,呼叫join()方法中的wait(0)方法,wait(0)表示無限等待直到被notify。即主執行緒會無限等待thread執行緒執行完成。
- public final void wait(long timeout, int nanos) throws InterruptedException {
- if (timeout < 0) {
- throw new IllegalArgumentException("timeout value is negative");
- }
- if (nanos < 0 || nanos > 999999) {
- throw new IllegalArgumentException(
- "nanosecond timeout value out of range");
- }
- if (nanos > 0) {
- timeout++;
- }
- wait(timeout);
- }
4.5、執行緒安全
資料在多執行緒併發的環境下會存在安全問題。例如如果多個使用者想要修改某個共享的資料,就會引發執行緒安全問題。因此需要引入執行緒同步機制(即執行緒排隊執行,不能併發)
4.5.1、執行緒同步的實現
java裡面通過關鍵字synchronized給執行緒加鎖。執行緒會獲取鎖,並獨佔cpu,只有當執行緒釋放了鎖之後,其餘執行緒拿到鎖之後才能執行。
當一個執行緒在執行狀態時遇到synchronized關鍵字,該執行緒就會放棄佔有的cpu時間片,在鎖池裡面找共享物件的物件鎖。
一個執行緒同步synchronized的例子——模擬ATM機取款
Account類
- package ATM;
- /*
- 銀行賬戶,使用執行緒同步機制,解決執行緒安全問題
- */
- public class Account {
- private String accountName; //賬戶名
- private double remain; //餘額
- Object obj=new Object();
- public Account(String accountName, int remain) {
- this.accountName = accountName;
- this.remain = remain;
- }
- public String getAccountName() {
- return accountName;
- }
- public void setAccountName(String accountName) {
- this.accountName = accountName;
- }
- public double getRemain() {
- return remain;
- }
- public void setRemain(double remain) {
- this.remain = remain;
- }
- //取款方法
- public void withdraw(double money) {
- //synchronized (obj){ //obj是一個全域性變數,例項一次Account建立一個obj物件,是可以被共享的
- /*Object obj1=new Object();
- synchronized (obj1){*/ //obj1是一個區域性變數,每呼叫一次run方法則會建立一個obj1物件,所以obj1不是共享物件
- //synchronized (null){ //報錯:空指標
- //synchronized ("abc"){ //"abc"在字串常量池當中,會讓所有的執行緒都同步
- synchronized (this){
- double before = this.getRemain(); //取款之前的餘額
- double after = before - money; //取款之後的餘額
- try{
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- this.setRemain(after); //更新餘額
- }
- }
- }
AccountThread類
- package ATM;
- public class AccountThread extends Thread {
- //兩個執行緒必須共享同一個賬戶物件
- private Account act;
- private double money; //取款金額
- //通過構造方法傳遞過來構造物件
- public AccountThread(Account act,double money) {
- this.act = act;
- this.money=money;
- }
- public void run() { //run方法執行表示取款操作
- act.withdraw(money);
- System.out.println(Thread.currentThread().getName()+"在賬戶"+
- act.getAccountName()+"取款"+money+"之後餘額為:"+act.getRemain());
- }
- }
啟動類:Test類
- package ATM;
- public class Test {
- public static void main(String[] args) {
- Account act=new Account("act001",10000); //建立賬戶物件
- //建立兩個執行緒,對同一賬戶取款
- Thread threadA=new AccountThread(act,5000);
- Thread threadB=new AccountThread(act,3000);
- threadA.setName("小明");
- threadB.setName("小華");
- threadA.start();
- threadB.start();
- }
- }
結果:
- //synchronized可以用在例項方法上,此時鎖是this
- public synchronized void withdraw(double money) {
- double before = this.getRemain(); //取款之前的餘額
- double after = before - money; //取款之後的餘額
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- this.setRemain(after); //更新餘額
- }
- }
可以在例項方法上使用synchronized,synchronized出現在例項方法上,鎖一定是this,不能是其他的物件了。所以這種方式不靈活。
另外還有一個缺點:synchronized出現在例項方法上,表示整個方法體都需要同步,可能會無故擴大同步的範圍,導致程式的執行效率降低。所以這種方式不常用。
4.5.2、java中的執行緒安全性
java中有三大變數 :1、例項變數(在堆中) 2、靜態變數(在方法中) 3、區域性變數(在棧中)
區域性變數不會有執行緒安全問題,因為區域性變數不共享,區域性變數在棧中,一個執行緒一個棧。
常量不會有執行緒安全問題,因為常量不會被改變。
例項變數在堆中,堆只有1個。靜態變數在方法區中,方法區只有1個。由於堆和方法區都是多執行緒可共享的,所以例項變數和靜態變數可能存線上程安全問題。
ArrayList、HashMap 、HashSet 是非執行緒安全的
Vector、Hashtable 是執行緒安全的
4.5.3、synchronized總結
synchronized有三種寫法:
第一種:同步程式碼塊(靈活)
synchronized(執行緒共享物件){
同步程式碼塊;
}
第二種:在例項方法上使用 synchronized
表示共享物件一定是this,並且同步程式碼塊是整個方法體。
第三種:在靜態方法上使用 synchronized
表示找類鎖。
類鎖永遠只有1把(就算建立了100個物件,那類鎖也只有1把)
物件鎖:1個物件1把鎖,100個物件100把鎖。 類鎖:100個物件,也可能只是1把類鎖。
- package Synchronized;
- //Q:doOther方法執行的時候需要等待doSome方法的結束嗎?
- //A:需要,因為靜態方法是類鎖,不管建立了幾個物件,類鎖只有1把
- public class Exam01 {
- public static void main(String[] args) throws InterruptedException {
- MyClass mc1 = new MyClass();
- MyClass mc2 = new MyClass();
- Thread t1 = new Mythread(mc1);
- Thread t2 = new Mythread(mc2);
- t1.setName("t1");
- t2.setName("t2");
- t1.start();
- Thread.sleep(1000);
- t2.start();
- }
- }
- class Mythread extends Thread {
- private MyClass mc;
- public Mythread(MyClass mc) {
- this.mc = mc;
- }
- public void run() {
- super.run();
- if (Thread.currentThread().getName().equals("t1")) {
- try {
- mc.doSome();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- if (Thread.currentThread().getName().equals("t2")) {
- mc.doOther();
- }
- }
- }
- class MyClass {
- //synchronized出現在靜態方法上是走 類鎖
- public synchronized static void doSome() throws InterruptedException {
- System.out.println("doSome begin");
- Thread.sleep(1000 * 5);
- System.out.println("doSome over");
- }
- public synchronized static void doOther() {
- System.out.println("doOther begin");
- System.out.println("doOther over");
- }
- }
4.5.4、死鎖
一個簡單的死鎖實現的例子
- package Thread;
- /*
- 實現一個死鎖,
- t1中obj1鎖上後就睡了,t2中obj2鎖上後就睡了。
- t1想要釋放obj1鎖,就必須請求到obj2鎖
- t2想要釋放obj2鎖,就必須請求到obj1鎖
- t1與t2互相請求鎖,但彼此都無法釋放鎖,所以形成了死鎖
- */
- public class Dead_lock {
- public static void main(String[] args) {
- Object obj1 = new Object();
- Object obj2 = new Object();
- //t1,t2兩個執行緒共享o1,o2
- Thread t1 = new MyThread_1(obj1, obj2);
- Thread t2 = new MyThread_2(obj1, obj2);
- t1.start();
- t2.start();
- }
- }
- class MyThread_1 extends Thread {
- Object obj1 = new Object();
- Object obj2 = new Object();
- public MyThread_1(Object obj1, Object obj2) {
- this.obj1 = obj1;
- this.obj2 = obj2;
- }
- public void run() {
- synchronized (obj1) {
- try {
- Thread.sleep(1000 * 3);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- synchronized (obj2) {
- }
- }
- }
- }
- class MyThread_2 extends Thread {
- Object obj1 = new Object();
- Object obj2 = new Object();
- public MyThread_2(Object obj1, Object obj2) {
- this.obj1 = obj1;
- this.obj2 = obj2;
- }
- public void run() {
- synchronized (obj2) {
- try {
- Thread.sleep(1000 * 3);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- synchronized (obj1) {
- }
- }
- }
- }
注:synchronized在開發中最好不要巢狀使用,一不小心就可能導致死鎖現象發生
4.6、守護執行緒
java語言中執行緒可分為兩大類:
一類是:使用者執行緒 例如:main方法主執行緒
一類是:守護執行緒(後臺執行緒) 例如java垃圾回收執行緒
4.6.1、守護執行緒的特點
一般守護執行緒是一個死迴圈,所有使用者執行緒只要結束,守護執行緒自動結束。
守護執行緒一般會用在一些定時任務,例如每天0點系統自動備份需要用到定時器,我們可以將定時器設定為守護執行緒一直在那裡看著。每到0點就備份一次。所有的使用者執行緒如果結束了,守護執行緒就自動退出。
4.6.2、一個簡單的守護執行緒的例子
- package Thread;
- /**
- * 使用者執行緒備份資料,守護執行緒守護。
- */
- public class GuardThread {
- public static void main(String[] args) {
- Thread thread = new BakDataThread();
- thread.setName("備份資料的執行緒");
- //啟動執行緒之前,將執行緒設定為守護執行緒
- thread.setDaemon(true);
- thread.start();
- for (int i = 0; i < 5; i++) {
- System.out.println(Thread.currentThread().getName() + "--->" + i);
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- class BakDataThread extends Thread {
- public void run() {
- int i = 0;
- //即使是死迴圈,但由於該執行緒是守護者。當用戶執行緒結束,守護執行緒自動終止
- while (true) {
- System.out.println(Thread.currentThread().getName() + "--->" + i++);
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
5、定時任務
5.1、實現一個定時器
- package Thread;
- import java.text.ParseException;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.Timer;
- import java.util.TimerTask;
- public class TimerTest {
- public static void main(String[] args) throws ParseException {
- //建立定時器物件
- Timer timer = new Timer();
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- Date firstTime = sdf.parse("2020-07-25 19:10:30");
- //指定定時任務
- //timer.schedule(定時任務,第一次執行時間時間,間隔多久執行一次)
- timer.schedule(new LogTimerTask(), firstTime, 1000 * 5);
- }
- }
- //編寫一個定時任務
- class LogTimerTask extends TimerTask {
- public void run() {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- String strTime = sdf.format(new Date());
- System.out.println(strTime + ":成功完成了一次資料備份!");
- }
- }
6、通過Callable介面實現一個執行緒
使用Callable介面實現執行緒,可以獲得該執行緒的返回值(JDK8新特性)
一個簡單的例項:
- package Thread;
- import java.util.concurrent.Callable;
- import java.util.concurrent.ExecutionException;
- import java.util.concurrent.FutureTask;
- public class ThirdWay {
- public static void main(String[] args) throws ExecutionException, InterruptedException {
- FutureTask futureTask = new FutureTask(new Task());
- Thread thread = new Thread(futureTask);
- thread.start();
- Object object = futureTask.get(); //通過get方法可以獲取當前執行緒的返回值
- ////主執行緒中這裡的程式必須等待get()方法結束才執行
- // get()方法為了拿另一個執行緒的執行結果需要等待其執行完成,因此要等待較長時間
- System.out.print("執行緒執行結果:");
- System.out.println(object);
- }
- }
- class Task implements Callable {
- public Object call() throws Exception {
- System.out.println("call method begin");
- Thread.sleep(1000 * 10);
- System.out.println("call method end");
- int a = 100;
- int b = 200;
- return a + b;
- }
- }
FutureTask類相關原始碼
- public FutureTask(Callable<V> callable) {
- if (callable == null)
- throw new NullPointerException();
- this.callable = callable;
- this.state = NEW; // ensure visibility of callable
- }
- public V get() throws InterruptedException, ExecutionException {
- int s = state;
- if (s <= COMPLETING)
- s = awaitDone(false, 0L);
- return report(s);
- }
Future類中實現了get方法獲取傳入執行緒的返回結果
7、Object類中的wait和notify方法
7.1、wait和notify方法介紹
wait和notify方法不是執行緒物件的方法,不能通過執行緒物件呼叫。
- Object object=new Object();
- object.wait();//object.wait()讓正在object物件上活動的執行緒進入等待狀態,無限等待,直到被喚醒為止
- object.notify();//object.notify()喚醒正在object物件上等待的執行緒
- object.notifyAll();//object.notifyAll喚醒正在object物件上等待的所有執行緒
7.2、生產者和消費者模式
一個簡單的生產者和消費者例項
模擬生產者生產一個,消費者就消費一個。讓倉庫始終零庫存。
- package Thread;
- import java.awt.*;
- import java.util.*;
- import java.util.List;
- public class Producer_Consumer {
- public static void main(String[] args) {
- List<String> list = new ArrayList<String>();
- Thread thread1 = new Thread(new Consumer(list), "消費者執行緒");
- Thread thread2 = new Thread(new Producer(list), "生產者執行緒");
- thread1.start();
- thread2.start();
- }
- }
- //消費者執行緒
- class Consumer implements Runnable {
- //倉庫
- private List<String> list;
- public Consumer(List<String> list) {
- this.list = list;
- }
- public void run() {
- //消費
- while (true) {
- synchronized (list) {
- //如果倉庫已經空了,消費者執行緒等待並釋放list集合的鎖
- if (list.size() == 0) {
- try {
- list.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- //倉庫中有商品,消費者進行消費
- String str = list.remove(0);
- System.out.println(Thread.currentThread().getName() + " 消費 " + str);
- list.notify(); //喚醒生產者
- }
- }
- }
- }
- //生產者執行緒
- class Producer implements Runnable {
- //倉庫
- private List<String> list;
- public Producer(List<String> list) {
- this.list = list;
- }
- public void run() {
- //生產
- while (true) {
- synchronized (list) {
- //如果倉庫裡有東西,則停止生產。生產者執行緒等待並釋放list集合的鎖
- if (list.size() > 0) {
- try {
- list.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- list.add("商品");
- System.out.println(Thread.currentThread().getName() + " 生產 " + list.get(0));
- list.notify(); //喚醒消費者
- }
- }
- }
- }
:
7.3、實現奇偶數的交替輸出
- package Thread;
- /**
- * 使用生產者和消費者模式實現兩個執行緒交替輸出:一個執行緒負責輸出奇數,另一個執行緒負責輸出偶數
- */
- public class Number {
- public static void main(String[] args) throws InterruptedException {
- Num num = new Num(0);
- Thread thread1 = new Thread(new Odd(num), "Odd");
- Thread thread2 = new Thread(new Event(num), "Event");
- thread1.start();
- thread2.start();
- }
- }
- class Odd implements Runnable {
- Num num;
- public Odd(Num num) {
- this.num = num;
- }
- public void run() {
- while (true) {
- synchronized (num) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- if (num.getI() % 2 == 0) {
- try {
- num.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- System.out.println(Thread.currentThread().getName() + "--->" + num.printNum());
- num.notifyAll();
- }
- }
- }
- }
- class Event implements Runnable {
- Num num;
- public Event(Num num) {
- this.num = num;
- }
- public void run() {
- while (true) {
- synchronized (num) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- if (num.getI() % 2 != 0) {
- try {
- num.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- System.out.println(Thread.currentThread().getName() + "--->" + num.printNum());
- num.notifyAll();
- }
- }
- }
- }
- class Num {
- private int i = 0;
- public Num(int i) {
- this.i = i;
- }
- public int getI() {
- return i;
- }
- public void setI(int i) {
- this.i = i;
- }
- int printNum() {
- return i++;
- }