App下載

JAVA多線程編程實(shí)例

猿友 2021-01-28 16:09:18 瀏覽數(shù) (3555)
反饋

1.三個售票窗口同時出售20張票

程序分析: (1)票數(shù)要使用同一個靜態(tài)值 (2)為保證不會出現(xiàn)賣出同一個票數(shù),要 java 多線程同步鎖。 設(shè)計思路: (1)創(chuàng)建一個站臺類Station,繼承 Thread,重寫 run方法,在 run 方法里面執(zhí)行售票操作!售票要使用同步鎖:即有一個站臺賣這張票時,其他站臺要等這張票賣完! (2)創(chuàng)建主方法調(diào)用類

(一)創(chuàng)建一個站臺類,繼承 Thread

package com.xykj.threadStation;
public class Station extends Thread {
   // 通過構(gòu)造方法給線程名字賦值
   public Station(String name) {
      super(name);// 給線程名字賦值
  }
   // 為了保持票數(shù)的一致,票數(shù)要靜態(tài)
   static int tick = 20;    
   // 創(chuàng)建一個靜態(tài)鑰匙
   static Object ob = "aa";//值是任意的
   // 重寫run方法,實(shí)現(xiàn)買票操作
   @Override
   public void run() {
     while (tick > 0) {
         synchronized (ob) {// 這個很重要,必須使用一個鎖,
         // 進(jìn)去的人會把鑰匙拿在手上,出來后才把鑰匙拿讓出來
         if (tick > 0) {
           System.out.println(getName() + "賣出了第" + tick + "張票");
           tick--;
        } else {
           System.out.println("票賣完了");
        }
      }
       try {
           sleep(1000);//休息一秒
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
    }
}
}

(二)創(chuàng)建主方法調(diào)用類

package com.xykj.threadStation;
public class MainClass {
 /**
 * java多線程同步鎖的使用
  * 示例:三個售票窗口同時出售10張票
  * */
 public static void main(String[] args) {
     //實(shí)例化站臺對象,并為每一個站臺取名字
    Station station1=new Station("窗口1");
    Station station2=new Station("窗口2");
    Station station3=new Station("窗口3");
   // 讓每一個站臺對象各自開始工作
    station1.start();
    station2.start();
    station3.start();
}
}

程序運(yùn)行結(jié)果:

窗口1賣出了第20張票
窗口2賣出了第19張票
窗口3賣出了第18張票
窗口3賣出了第17張票
窗口1賣出了第16張票
窗口2賣出了第15張票
窗口3賣出了第14張票
窗口1賣出了第13張票
窗口2賣出了第12張票
窗口2賣出了第11張票
窗口1賣出了第10張票
窗口3賣出了第9張票
窗口3賣出了第8張票
窗口1賣出了第7張票
窗口2賣出了第6張票
窗口3賣出了第5張票
窗口1賣出了第4張票
窗口2賣出了第3張票
窗口3賣出了第2張票
窗口1賣出了第1張票
票賣完了

可以看到票數(shù)是不會有錯的!

2.兩個人AB通過一個賬戶A在柜臺取錢和B在ATM機(jī)取錢!

程序分析:

錢的數(shù)量要設(shè)置成一個靜態(tài)的變量,兩個人要取的同一個對象值。 (一)創(chuàng)建一個 Bank 類

package com.thread.demo.demo2;
import java.util.Objects;
public class Bank {
// 假設(shè)一個賬戶有1000塊錢  
static double money = 1000;
// 柜臺Counter取錢的方法  
private void Counter(double money) {
Bank.money -= money;
System.out.println("柜臺取錢" + money + "元,還剩" + Bank.money + "元!");
}
// ATM取錢的方法  
private void ATM(double money) {
Bank.money -= money;
System.out.println("ATM取錢" + money + "元,還剩" + Bank.money + "元!");
}
//提供一個對外取款途徑,防止直接調(diào)取方法同時取款時,并發(fā)余額顯示錯誤
public synchronized void outMoney(double money, String mode) throws Exception{
if(money > Bank.money){
//校驗(yàn)余額是否充足
throw new Exception("取款金額"+money+",余額只剩"+Bank.money+",取款失敗");
}
if(Objects.equals(mode, "ATM")){
ATM(money);
} else {
Counter(money);
}
}
}

(二)創(chuàng)建一個 PersonA 類

package com.thread.demo.demo2;
public class PersonA extends Thread {
Bank bank;
String mode;
public PersonA(Bank bank, String mode) {
this.mode = mode;
this.bank = bank;
}
while(bank.money >= 100){
try {
bank.outMoney(100, mode);
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

(三)創(chuàng)建一個 PersonB 類

package com.thread.demo.demo2;
public class PersonB extends Thread {
Bank bank;
String mode;
public PersonB(Bank bank, String mode) {
this.bank = bank;
this.mode = mode;
}
public void run() {
while (bank.money >= 200) {
try {
bank.outMoney(200, mode);
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

(四)創(chuàng)建主方法的調(diào)用類

package com.thread.demo.demo2;
/**
* 兩個人AB通過一個賬戶A在柜臺取錢和B在ATM機(jī)取錢
* */
public class MainClass {
public static void main(String[] args) {
Bank bank = new Bank();
// 實(shí)例化兩個人,傳入同一個銀行的對象
PersonA a = new PersonA(bank, "Counter");
PersonB b = new PersonB(bank, "ATM");
a.start();
b.start();
}
}

運(yùn)行結(jié)果:

微信截圖_20210128100906
可以看到取完就停止運(yùn)行了。

3.龜兔賽跑問題

龜兔賽跑:2000米
要求:
 (1)兔子每 0.1 秒 5 米的速度,每跑20米休息1秒;
 (2)烏龜每 0.1 秒跑 2 米,不休息;
 (3)其中一個跑到終點(diǎn)后另一個不跑了!
程序設(shè)計思路:
 (1)創(chuàng)建一個 Animal 動物類,繼承Thread,編寫一個 running 抽象方法,重寫 run 方法,把 running 方法在 run 方法里面調(diào)用。
 (2)創(chuàng)建 Rabbit 兔子類和 Tortoise 烏龜類,繼承動物類
 (3)兩個子類重寫 running 方法
 (4)本題的第3個要求涉及到線程回調(diào)。需要在動物類創(chuàng)建一個回調(diào)接口,創(chuàng)建一個回調(diào)對象。

(一)創(chuàng)建 Animal 動物類

package com.thread.demo.demo3;
public abstract class Animal extends Thread {
public int length = 2000;// 比賽長度
public abstract void runing();
@Override
public void run() {
super.run();
while (length > 0) {
runing();
}
}
// 在需要回調(diào)數(shù)據(jù)的地方(兩個子類需要),聲明一個接口
public static interface Calltoback {
public void win();
}
// 2.創(chuàng)建接口對象
public Calltoback calltoback;
}

(二)創(chuàng)建 Rabbit 兔子類

package com.thread.demo.demo3;
public class Rabbit extends Animal {
public Rabbit() {
setName("兔子");
}
@Override
public void runing() {
//兔子速度
int dis = 5;
length -= dis;
System.out.println("兔子跑了" + dis + "米,距離終點(diǎn)還有" + length + "米");
if (length <= 0) {
length = 0;
System.out.println("兔子獲得了勝利");
// 給回調(diào)對象賦值,讓烏龜不要再跑了
if (calltoback != null) {
calltoback.win();
}
}
try {
if ((2000 - length) % 20 == 0) { // 每20米休息一次,休息時間是1秒
sleep(1000);
} else { //沒0.1秒跑5米
sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

(三)創(chuàng)建 Tortoise 烏龜類

package com.thread.demo.demo3;
public class Tortoise extends Animal {
public Tortoise() {
setName("烏龜");// Thread的方法,給線程賦值名字
}
// 重寫running方法,編寫烏龜?shù)谋寂懿僮?br> @Override
public void runing() {
// 烏龜速度
int dis = 2;
length -= dis;
System.out.println("烏龜跑了" + dis + "米,距離終點(diǎn)還有" + length + "米");
if (length <= 0) {
length = 0;
System.out.println("烏龜獲得了勝利");
// 讓兔子不要在跑了
if (calltoback != null) {
calltoback.win();
          }
}
try {
sleep(100); //沒0.1秒跑2米
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

(四)創(chuàng)建一個讓動物線程停止的類,這里要實(shí)現(xiàn)回調(diào)接口

package com.thread.demo.demo3;
import com.thread.demo.demo3.Animal.Calltoback;
public class LetOneStop implements Calltoback {
// 動物對象
Animal an;
// 獲取動物對象,可以傳入兔子或?yàn)觚數(shù)膶?shí)例
public LetOneStop(Animal an) {
this.an = an;
}
// 讓動物的線程停止
@Override
public void win() {
// 線程停止
an.stop();
}
}

(五)創(chuàng)建一個主方法調(diào)用類

package com.thread.demo.demo3;
public class MainClass {
/**
* 龜兔賽跑:2000米
*/
public static void main(String[] args) {
// 實(shí)例化烏龜和兔子
Tortoise tortoise = new Tortoise();
Rabbit rabbit = new Rabbit();
// 回調(diào)方法的使用,誰先調(diào)用calltoback方法,另一個就不跑了
LetOneStop letOneStop1 = new LetOneStop(tortoise);
// 讓兔子的回調(diào)方法里面存在烏龜對象的值,可以把烏龜stop
rabbit.calltoback = letOneStop1;
LetOneStop letOneStop2 = new LetOneStop(rabbit);
// 讓烏龜?shù)幕卣{(diào)方法里面存在兔子對象的值,可以把兔子stop
tortoise.calltoback = letOneStop2;
// 開始跑
tortoise.start();
rabbit.start();
}
}

運(yùn)行結(jié)果:
微信截圖_20210128102048

4. 線程示例總結(jié)

(1)代碼塊鎖是一個防止數(shù)據(jù)發(fā)生錯誤的一個重要手段;

(2)對象的統(tǒng)一性是非常重要的,這要想到對象的傳入問題,要操作的對象只能new一次,其他的操作都是對這個傳入的對象進(jìn)行的,才能保證數(shù)據(jù)一致性,完整性和正確性。


1 人點(diǎn)贊