zoukankan      html  css  js  c++  java
  • Java多线程的常见例子

    一.相关知识:

    Java多线程程序设计到的知识:

    (一)对同一个数量进行操作

    (二)对同一个对象进行操作

    (三)回调方法使用

    (四)线程同步,死锁问题

    (五)线程通信

     等等

    二.示例一:三个售票窗口同时出售20张票;

    程序分析:1.票数要使用同一个静态值

     2.为保证不会出现卖出同一个票数,要java多线程同步锁。

    设计思路:1.创建一个站台类Station,继承Thread,重写run方法,在run方法里面执行售票操作!售票要使用同步锁:即有一个站台卖这张票时,其他站台要等这张票卖完!

    2.创建主方法调用类

    (一)创建一个站台类,继承Thread

    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();
    }
     
    }
    }
     
    }
    

      

     
     
     

    (二)创建主方法调用类

    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();
     
    }
     
    }
    

      

     
     

    程序运行结果:

     

    可以看到票数是不会有错的!

    三.示例二:两个人AB通过一个账户A在柜台取钱和B在ATM机取钱!

    程序分析:钱的数量要设置成一个静态的变量。两个人要取的同一个对象值

    (一)创建一个Bank类

    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));
    }
     
    }
    

      

     
     
     

    (二)创建一个PersonA类

    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();
    }
    }
    }
    }
    

      

     
     

    (三)创建一个PersonB类

    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();
    }
    }
     
    }
    }
    

      

     
     

    (四)创建主方法的调用类

    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();
     
    }
     
    }
    

      

     
     

      

    运行结果:

    可以看到取完就停止运行了。

    四.示例三:龟兔赛跑问题

    龟兔赛跑: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动物类

    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;
     
    }
    

      

     
     

    (二)创建Rabbit兔子类

    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();
    }
    }
    }
    }
    

      

     
     

    (三)创建Tortoise乌龟类

    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();
    }
    }
    }
    

      

     

    (四)创建一个让动物线程停止的类,这里要实现回调接口

    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();
    }
     
    }
    

      

     
     

    (五)创建一个主方法调用类,

    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();
     
    }
     
    }
    

      

     

    运行结果:

     

    可以看到结果兔子赢了。

    一般来说兔子获得了胜利是在最后输出的,

    但是,由于线程一直在执行所以会出现:

    “兔子跑了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

    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;
    }
    }
    

      

     
     
     

    (二)创建一个KFC类

    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());
                }
            }
        }
    }
    

      

    (三)创建一个客户类Customer

    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);//在消费的方法里面传入参数
    }
     
    }
    }
    

      

     
     

      

    (四)创建一个服务员类Waiter

    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);//传入每次生产的数量
    }
     
    }
    }
    

      

     
     

    (五)创建主方法的调用类

    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();
    }
     
    }
    

      

     
     

    六.示例五:设计四个线程对象对同一个数据进行操作,

      两个线程执行减操作,两个线程执行加操作。

    程序分析:1.创建一个ThreadAddSub类继承Thread,重写run方法

       2.在run方法里面实现加和减的操作,每次操作后睡眠1秒

       3.创建主方法调用类

    (一)创建一个ThreadAddSub类

    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();
    }
    }
     
    }
    }
    

      

     
     

     (二)创建主方法调用类

     
     
    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次。

  • 相关阅读:
    .NET实现Excel文件的读写 未测试
    权限管理设计
    struts1中配置应用
    POJ 2139 Six Degrees of Cowvin Bacon(floyd)
    POJ 1751 Highways
    POJ 1698 Alice's Chance
    POJ 1018 Communication System
    POJ 1050 To the Max
    POJ 1002 4873279
    POJ 3084 Panic Room
  • 原文地址:https://www.cnblogs.com/pureEve/p/6524366.html
Copyright © 2011-2022 走看看