多線程常見的例子

一.相關知識:java

 

Java多線程程序設計到的知識:數組

(一)對同一個數量進行操做多線程

(二)對同一個對象進行操做dom

(三)回調方法使用ide

(四)線程同步,死鎖問題this

(五)線程通訊spa

 等等.net

 

 

二.示例一:三個售票窗口同時出售20張票;線程

 

程序分析:1.票數要使用同一個靜態值設計

 2.爲保證不會出現賣出同一個票數,要java多線程同步鎖。

設計思路:1.建立一個站臺類Station,繼承Thread,重寫run方法,在run方法裏面執行售票操做!售票要使用同步鎖:即有一個站臺賣這張票時,其餘站臺要等這張票賣完!

2.建立主方法調用類

 

(一)建立一個站臺類,繼承Thread

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
package com.xykj.threadStation;
 
public class Station extends Thread {
 
// 經過構造方法給線程名字賦值
public Station(String name) {
super(name);// 給線程名字賦值
}
 
// 爲了保持票數的一致,票數要靜態
static int tick = 20;
 
// 建立一個靜態鑰匙
static Object ob = "aa";//值是任意的
 
// 重寫run方法,實現買票操做
@Override
public void run() {
while (tick > 0) {
synchronized (ob) {// 這個很重要,必須使用一個鎖,
// 進去的人會把鑰匙拿在手上,出來後才把鑰匙拿讓出來
if (tick > 0) {
System.out.println(getName() + "賣出了第" + tick + "張票");
tick--;
} else {
System.out.println("票賣完了");
}
}
try {
sleep(1000);//休息一秒
} catch (InterruptedException e) {
e.printStackTrace();
}
 
}
}
 
}
 來自CODE的代碼片
snippet_file_0.txt

 

 

 

(二)建立主方法調用類

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
package com.xykj.threadStation;
 
public class MainClass {
/**
* java多線程同步鎖的使用
* 示例:三個售票窗口同時出售10張票
* */
public static void main(String[] args) {
//實例化站臺對象,併爲每個站臺取名字
Station station1=new Station("窗口1");
Station station2=new Station("窗口2");
Station station3=new Station("窗口3");
 
// 讓每個站臺對象各自開始工做
station1.start();
station2.start();
station3.start();
 
}
 
}
 來自CODE的代碼片
snippet_file_0.txt

 

 

 

程序運行結果:

 

 

 

 

 

能夠看到票數是不會有錯的!

 

 

 

 

三.示例二:兩我的AB經過一個帳戶A在櫃檯取錢和B在ATM機取錢!

 

程序分析:錢的數量要設置成一個靜態的變量。兩我的要取的同一個對象值

 

(一)建立一個Bank類

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
package com.xykj.bank;
 
public class Bank {
 
// 假設一個帳戶有1000塊錢
static int money = 1000;
 
// 櫃檯Counter取錢的方法
public void Counter(int money) {// 參數是每次取走的錢
Bank.money -= money;//取錢後總數減小
System.out.println("A取走了" + money + "還剩下" + (Bank.money));
}
 
// ATM取錢的方法
public void ATM(int money) {// 參數是每次取走的錢
Bank.money -= money;//取錢後總數減小
System.out.println("B取走了" + money + "還剩下" + (Bank.money));
}
 
}
 來自CODE的代碼片
snippet_file_0.txt

 

 

(二)建立一個PersonA類

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
package com.xykj.bank;
 
public class PersonA extends Thread {
// 建立銀行對象
Bank bank;
 
// 經過構造器傳入銀行對象,確保兩我的進入的是一個銀行
public PersonA(Bank bank) {
this.bank = bank;
}
 
//重寫run方法,在裏面實現使用櫃檯取錢
@Override
public void run() {
while (Bank.money >= 100) {
bank.Counter(100);// 每次取100塊
try {
sleep(100);// 取完休息0.1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
 來自CODE的代碼片
snippet_file_0.txt

 

 

 

(三)建立一個PersonB類

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
package com.xykj.bank;
 
public class PersonB extends Thread {
// 建立銀行對象
Bank bank;
 
// 經過構造器傳入銀行對象,確保兩我的進入的是一個銀行
public PersonB(Bank bank) {
this.bank = bank;
}
 
// 重寫run方法,在裏面實現使用櫃檯取錢
@Override
public void run() {
while (Bank.money >= 200) {
bank.ATM(200);// 每次取200塊
try {
sleep(100);// 取完休息0.1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
 
}
}
 來自CODE的代碼片
snippet_file_0.txt

 

 

(四)建立主方法的調用類

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
package com.xykj.bank;
 
public class MainClass {
/**
* 兩我的AB經過一個帳戶A在櫃檯取錢和B在ATM機取錢
* */
public static void main(String[] args) {
// 實力化一個銀行對象
Bank bank = new Bank();
// 實例化兩我的,傳入同一個銀行的對象
PersonA pA = new PersonA(bank);
PersonB pB = new PersonB(bank);
// 兩我的開始取錢
pA.start();
pB.start();
 
}
 
}
 來自CODE的代碼片
snippet_file_0.txt

 

 

  

運行結果:

 

 

 

能夠看到取完就中止運行了。

 

 

 

四.示例三:龜兔賽跑問題

 

龜兔賽跑:20米     //只要爲了看到效果,全部距離縮短了

 要求:

1.兔子每秒0.5米的速度,每跑2米休息10秒,

2.烏龜每秒跑0.1米,不休息 

  3.其中一個跑到終點後另外一個不跑了!

       程序設計思路:

1.建立一個Animal動物類,繼承Thread,編寫一個running抽象方法,重寫run方法,把running方法在run方法裏面調用。

2.建立Rabbit兔子類和Tortoise烏龜類,繼承動物類

3.兩個子類重寫running方法

4.本題的第3個要求涉及到線程回調。須要在動物類建立一個回調接口,建立一個回調對象

 

(一)建立Animal動物類

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
package com.xykj.rabbit_tortoise;
 
public abstract class Animal extends Thread{
 
public double length=20;//比賽的長度
 
public abstract void runing();//抽象方法須要子類實現
 
//在父類重寫run方法,在子類只要重寫running方法就能夠了
@Override
public void run() {
super.run();
while (length>0) {
runing();
}
}
 
//在須要回調數據的地方(兩個子類須要),聲明一個接口
public static interface Calltoback{
public void win();
}
 
//2.建立接口對象
public Calltoback calltoback;
 
}
 來自CODE的代碼片
snippet_file_0.txt

 

 

(二)建立Rabbit兔子類

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
package com.xykj.rabbit_tortoise;
 
public class Rabbit extends Animal {
 
public Rabbit() {
setName("兔子");// Thread的方法,給線程賦值名字
}
 
// 重寫running方法,編寫兔子的奔跑操做
@Override
public void runing() {
// 跑的距離
double dis = 0.5;
length -= dis;//跑完後距離減小
if (length <= 0) {
length = 0;
System.out.println("兔子得到了勝利");
//給回調對象賦值,讓烏龜不要再跑了
if (calltoback != null) {
calltoback.win();
}
}
System.out.println("兔子跑了" + dis + "米,距離終點還有" + (int)length + "米");
 
if (length % 2 == 0) {// 兩米休息一次
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
 來自CODE的代碼片
snippet_file_0.txt

 

 

 

(三)建立Tortoise烏龜類

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 
package com.xykj.rabbit_tortoise;
 
public class Tortoise extends Animal {
 
public Tortoise() {
setName("烏龜");// Thread的方法,給線程賦值名字
}
 
// 重寫running方法,編寫烏龜的奔跑操做
@Override
public void runing() {
// 跑的距離
double dis = 0.1;
length -= dis;
if (length <= 0) {
length = 0;
System.out.println("烏龜得到了勝利");
// 讓兔子不要在跑了
if (calltoback != null) {
calltoback.win();
}
}
System.out.println("烏龜跑了" + dis + "米,距離終點還有" + (int) length + "米");
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
 來自CODE的代碼片
snippet_file_0.txt

 

 

 

 

(四)建立一個讓動物線程中止的類,這裏要實現回調接口

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
package com.xykj.rabbit_tortoise;
 
import com.xykj.rabbit_tortoise.Animal.Calltoback;
 
public class LetOneStop implements Calltoback {
 
// 動物對象
Animal an;
 
// 獲取動物對象,能夠傳入兔子或烏龜的實例
public LetOneStop(Animal an) {
this.an = an;
}
 
//讓動物的線程中止
@Override
public void win() {
// 線程中止
an.stop();
}
 
}
 來自CODE的代碼片
snippet_file_0.txt

 

 

 

 

(五)建立一個主方法調用類,

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
package com.xykj.rabbit_tortoise;
 
public class MainClass {
/**
* 龜兔賽跑:20米
* */
public static void main(String[] args) {
//實例化烏龜和兔子
Tortoise tortoise = new Tortoise();
Rabbit rabbit = new Rabbit();
//回調方法的使用,誰先調用calltoback方法,另外一個就不跑了
LetOneStop letOneStop1 = new LetOneStop(tortoise);
rabbit.calltoback = letOneStop1;//讓兔子的回調方法裏面存在烏龜對象的值,能夠把烏龜stop
LetOneStop letOneStop2 = new LetOneStop(rabbit);
tortoise.calltoback = letOneStop2;//讓烏龜的回調方法裏面存在兔子對象的值,能夠把兔子stop
//開始跑
tortoise.start();
rabbit.start();
 
}
 
}
 來自CODE的代碼片
snippet_file_0.txt

 

 

 

運行結果:

 

 

 

能夠看到結果兔子贏了。

通常來講兔子得到了勝利是在最後輸出的,

可是,因爲線程一直在執行因此會出現:

「兔子跑了0.5米,距離終點還有0米」還沒來得及輸出完,

而「兔子得到了勝利」已經輸出完畢了。

 

 

五.實例四:

在一個KFC內,服務員負責生產食物,消費者負責消費食物;

當生產到必定數量能夠休息一下,直到消費完食物,再立刻生產,一直循環

 

程序涉及到的內容:

1.這設計到java模式思想:生產者消費者模式

2.要保證操做對象的統一性,即消費者和服務者都是跟同一個KFC發生關係的,KFC只能new一次

3.this.notifyAll();和 this.wait();一個是全部喚醒的意思,一個是讓本身等待的意思;

好比本題中,生產者生產完畢後,先全部喚醒(包括消費者和生產者),再讓全部本身(生產者)等待

 這時,消費者開始消費,直到食材不夠,先全部喚醒(包括消費者和生產者),再讓全部本身(消費者)等待

一直執行上面的操做的循環

4.生產者和消費者都要繼承Thread,才能實現多線程的啓動

 

 

程序設計的步驟思路:

1.建立一個食物類Food,有存放/獲取食物的名稱的方法

2.建立一個KFC類,有生產食物和消費食物的方法

3.建立一個客戶類Customer,繼承Thread,重寫run方法,在run方法裏面進行消費食物操做

4.建立一個服務員類Waiter,繼承Thread,重寫run方法,在run方法裏面進行生產食物的操做

5.建立主方法的調用類

 

 

(一)建立一個食物類Food

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
package com.xykj.producer_consumer;
 
public class Food {
String name="";
//經過構造方法傳入食物的名字
public Food(String name) {
this.name=name;
}
//get、set 方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
 來自CODE的代碼片
snippet_file_0.txt

 

 

 

(二)建立一個KFC類

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
package com.xykj.producer_consumer;
import java.util.ArrayList;
import java.util.List;
 
public class KFC {
 
//食物的種類
String[] names = { "薯條", "燒板", "雞翅", "可樂" };
 
//生產的最大值,到達後能夠休息
static final int Max = 20;
 
//存放食物的集合
List<Food> foods = new ArrayList<Food>();
 
// 生產食物的方法
public void prod(int index) {
synchronized (this) {
// 若是食物數量大於20
while (foods.size() > Max) {
System.out.println("食材夠了");
this.notifyAll();//這個喚醒是針對生產者和消費者,有all
try {
String name=Thread.currentThread().getName();
this.wait();//這個喚醒是針對生產者,沒有all
System.out.println("生產者:"+name);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
 
// 開始生產食物食物//有一點要注意的
System.out.println("開始生產食物");
for (int i = 0; i < index; i++) {
Food food = new Food(names[(int) (Math.random() * 4)]);
foods.add(food);
System.out.println("生產了" + food.getName() + foods.size());
}
}
}
 
// 消費食物的方法
public void consu(int index) {
synchronized (this) {
while (foods.size() < index) {
System.out.println("食材不夠了");
this.notifyAll();//這個喚醒是針對生產者和消費者,有all
try {
String name=Thread.currentThread().getName();
this.wait();//這個喚醒是針對消費者,沒有all
System.out.println("消費者:"+name);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 足夠消費
System.out.println("開始消費");
for (int i = 0; i < index; i++) {
Food food = foods.remove(foods.size() - 1);
System.out.println("消費了一個" + food.getName() + foods.size());
}
}
}
}
 來自CODE的代碼片
snippet_file_0.txt

 

 

 

(三)建立一個客戶類Customer

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
package com.xykj.producer_consumer;
 
public class Customers extends Thread{
KFC kfc;
//KFC要傳入,保證每個服務員和用戶在同一個KFC對象內
public Customers(KFC kfc) {
this.kfc=kfc;
}
@Override
public void run() {
int size=(int)(Math.random()*5);//每次要消費的食物的數量
while (true) {
kfc.consu(size);//在消費的方法裏面傳入參數
}
 
}
}
 來自CODE的代碼片
snippet_file_0.txt

 

 

 

 

(四)建立一個服務員類Waiter

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
package com.xykj.producer_consumer;
 
public class Waiter extends Thread{
KFC kfc;
//KFC要傳入,保證每個服務員和用戶在同一個KFC對象內
public Waiter(KFC kfc) {
this.kfc=kfc;
}
@Override
public void run() {
int size=(int)(Math.random()*5)+5;//每次生產的數量
while (true) {
kfc.prod(size);//傳入每次生產的數量
}
 
}
}
 來自CODE的代碼片
snippet_file_0.txt

 

 

 

(五)建立主方法的調用類

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
package com.xykj.producer_consumer;
 
public class MainClass {
/**
* 生產者消費者模式
*
* */
public static void main(String[] args) {
 
// 只實例化一個KFC對象,保證每個服務員和用戶在同一個KFC對象內
KFC kfc = new KFC();
 
//實例化4個客戶對象
Customers c1 = new Customers(kfc);
Customers c2 = new Customers(kfc);
Customers c3 = new Customers(kfc);
Customers c4 = new Customers(kfc);
 
//實例化3個服務員對象
Waiter waiter1 = new Waiter(kfc);
Waiter waiter2 = new Waiter(kfc);
Waiter waiter3 = new Waiter(kfc);
 
//讓全部的對象的線程都開始工做
waiter1.start();
waiter2.start();
waiter3.start();
c1.start();
c2.start();
c3.start();
c4.start();
}
 
}
 來自CODE的代碼片
snippet_file_0.txt

 

 

 

六.示例五:設計四個線程對象對同一個數據進行操做,

  兩個線程執行減操做,兩個線程執行加操做。

 

程序分析:1.建立一個ThreadAddSub類繼承Thread,重寫run方法

   2.在run方法裏面實現加和減的操做,每次操做後睡眠1秒

   3.建立主方法調用類

 

(一)建立一個ThreadAddSub類

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
package com.xykj.add;
 
public class ThreadAddSub extends Thread {
//判斷要進行的操做
boolean operate = true;
//要操做的數
static int sum = 0;
 
// 把操做運算經過構造方法傳進來
public ThreadAddSub(boolean operate) {
super();
this.operate = operate;
}
 
@Override
public void run() {
super.run();
while (true) {
if (operate) {
sum+=5;
System.out.println("加後,sum="+sum);
} else {
sum-=4;
System.out.println("減後,sum="+sum);
}
try {
sleep(500);// 睡眠0.5秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
 
}
}
 來自CODE的代碼片
 

 

 

 

 (二)建立主方法調用類

 

 

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
emptypackage com.xykj.add;
 
public class MainClass {
/**
* (線程同步)
* */
public static void main(String[] args) {
 
//建立一個存放ThreadAddSub對象的數組
ThreadAddSub[] tSub=new ThreadAddSub[4];
for (int i = 0; i < tSub.length; i++) {
 
//把實例化ThreadAddSub對象賦值到數組內
//第一三個是true,二四個是false
tSub[i]=new ThreadAddSub(i%2==0?true:false);
 
//讓線程開始工做
tSub[i].start();
}
 
}
 
}
 
 

 

 

  

 

線程示例總結:

代碼塊鎖是一個防止數據發生錯誤的一個重要手段。

對象的統一性是很是重要的,這要想到對象的傳入問題,

要操做的對象只能new一次,其餘的操做都是對這個傳入的對象進行的,

才能保證數據一致性,完整性和正確性。

 

練習題目:

 

1. (多線程)代碼實現火車站4個賣票窗口同時買票的場景,輸出示例:
窗口1賣票窗口2賣票窗口1賣票...2. (線程同步)代碼實現火車站4個窗口同時賣100張票的代碼邏輯,同一個窗口不能賣同一張張票。3. (線程通訊)小明打算去提款機上取錢,發現卡上沒錢,這時候他告知媽媽去存錢,媽媽存了錢了,告知小明存好了能夠取錢了。(PS:小明分屢次取錢,每次取100,當發現錢不夠100,就等待媽媽存錢,小明他媽每次存2000,當發現錢小於100就存錢,就存錢,而且通知小明去取錢,當大於100就等待小明錢不夠是再存)4. (線程同步)設計四個線程對象對同一個數據進行操做,兩個線程執行減操做,兩個線程執行加操做。5. (線程通訊)製做兩個線程對象,要求用同步塊的方式使第一個線程運行2次,而後將本身阻塞起來,喚醒第二個線程,第二個線程再運行2次,而後將本身阻塞起來,喚醒第一個線程……兩個線程交替執行。6. (線程同步)設計4個線程,其中兩個線程每次對j增長1,另外兩個線程對j每次減小1。7. (線程通訊)子線程循環10次,接着主線程循環100,接着又回到子線程循環10次,接着再回到主線程又循環100,如此循環50次。

相關文章
相關標籤/搜索