Java多线程的应用实例

本文转自:http://www.cnblogs.com/pureEve/p/6524366.html  根据自己理解做了部分修改。


一.相关知识:

 

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

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

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

(三)回调方法使用

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

(五)线程间通信


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

 

程序分析:

1.票数要使用同一个静态值

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


设计思路:

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

2.创建主方法调用类

 

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

package com.crp.Multithreading.examples.stationForTickes;

import lombok.extern.slf4j.Slf4j;

/**
 * com.crp.Multithreading.examples
 *
 * @author chenrunping
 * @create 2017-08-24 13:31
 **/
@Slf4j
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) {
                    log.info(getName() + "卖出了第" + tick + "张票");
                    tick--;
                } else {
                    log.info("票卖完了");
                }
            }
            try {
                sleep(1000);//休息一秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }


}

(二)创建主方法调用类

package com.crp.Multithreading.examples.stationForTickes;

/**
 * com.crp.Multithreading.examples.stationForTickes
 三个售票窗口同时出售20张票;
 程序分析:1.票数要使用同一个静态值

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

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

 2.创建主方法调用类
 * @author chenrunping
 * @create 2017-08-24 14:10
 **/
public class Main {

    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机取钱!

 

程序分析:两个人要取的同一个对象(账户),账户的金额是一定的,两个人同事对账户操作,至账户余额不足。


思路设计:1.创建一个Accout类,账户类有属性存款总金额,有方法取钱  2.创建一个Person类,继承Thread类,重写run方法,调用Accout类的取钱方法。


(一)创建一个Accout类

package com.crp.Multithreading.examples.bankForMoney2;

import lombok.extern.slf4j.Slf4j;

/**
 * com.crp.Multithreading.examples.bankForMoney
 *
 * @author chenrunping
 * @create 2017-08-24 14:05
 **/
@Slf4j
public class Account {
    int totalMoney;

    public Account(int money) {
        this.totalMoney = money;
    }

    //取钱方法
    public void getMoney(int money, String name) {
        totalMoney -= money;//取钱后总数减少
        log.info("{}取走了{},还剩下{}", name, money, totalMoney);
    }
}


(二)创建一个Person类

package com.crp.Multithreading.examples.bankForMoney2;

import lombok.extern.slf4j.Slf4j;

/**
 * com.crp.Multithreading.examples.bankForMoney
 *
 * @author chenrunping
 * @create 2017-08-24 14:23
 **/
@Slf4j
public class Person extends Thread{
    // 创建银行对象
    Account account;
    String name;
    int money;

    // 通过构造器传入银行对象,确保两个人进入的是一个银行
    public Person(Account account, String name, int money, String threadName) {
        super(threadName);
        this.account = account;
        this.name = name;
        this.money = money;

    }

    //重写run方法,在里面实现使用柜台取钱
    @Override
    public void run() {
        while (account.totalMoney >= money) {
            account.getMoney(money,name);//
            try {
                sleep(1000);//
            } catch (InterruptedException e) {
                log.info("异常!",e);
            }
        }
    }


}


(三)创建一个Main类

package com.crp.Multithreading.examples.bankForMoney2;

/**
 * com.crp.Multithreading.examples.bankForMoney
 *程序分析:两个人要取的同一个账户
 * @author chenrunping
 * @create 2017-08-24 14:12
 **/
public class Main {

    /**
     * 两个人AB通过一个账户A取钱
     */
    public static void main(String[] args) {
        // 实例化一个账户对象
        Account account = new Account(1000);
        // 实例化两个人,传入同一个银行的对象
        Person pA = new Person(account,"pA",100,"personA");
        Person pB = new Person(account,"pB",250,"personB");
        // 两个人开始取钱
        pA.start();
        pB.start();

    }

}

运行结果:



示例三:龟兔赛跑问题

 

龟兔赛跑:100米

 要求:

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

2.乌龟每秒跑1米,不休息 

3.其中一个跑到终点后另一个不跑了!

程序设计思路:

1.创建一个Animal动物类,继承Thread,编写一个running抽象方法,重写run方法,把running方法在run方法里面调用。

2.创建Rabbit兔子类和Tortoise乌龟类,继承动物类

3.两个子类重写running方法

4.本题的第3个要求涉及到线程回调。需要在动物类创建一个回调接口,创建一个回调对象

 

(一)创建Animal动物类


package com.crp.Multithreading.examples.rabbitAndTortoise;

/**
 * com.crp.Multithreading.examples.rabbitAndTortoise
 *
 * @author chenrunping
 * @create 2017-08-24 14:55
 **/
public abstract class Animal extends Thread{

    public double length=100;//比赛的长度

    boolean isExit = false;

    public abstract void runing();//抽象方法需要子类实现

    //在父类重写run方法,在子类只要重写running方法就可以了
    @Override
    public void run() {
        while (length > 0 && !isExit) {
            runing();
        }
    }

    //在需要回调数据的地方(两个子类需要),声明一个接口
    public static interface Calltoback{
        public void win();
    }

    //2.创建接口对象
    public Calltoback calltoback;

}

(二)创建Rabbit兔子类


package com.crp.Multithreading.examples.rabbitAndTortoise;

/**
 * com.crp.Multithreading.examples.rabbitAndTortoise
 *
 * @author chenrunping
 * @create 2017-08-24 14:55
 **/
public class Rabbit extends Animal {

    public Rabbit() {
        setName("兔子");// Thread的方法,给线程赋值名字
    }

    // 重写running方法,编写兔子的奔跑操作
    @Override
    public void runing() {
        // 跑的距离
        double dis = 5;
        length -= dis;//跑完后距离减少
        System.out.println("兔子跑了" + dis + "米,距离终点还有" + (int) length + "");
        if (length <= 0) {
            length = 0;
            System.out.println("兔子获得了胜利");
            //给回调对象赋值,让乌龟不要再跑了
            calltoback.win();
        }
        if (length % 10 == 0) {// 10米休息一次
            try {
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

(三)创建Tortoise乌龟类


package com.crp.Multithreading.examples.rabbitAndTortoise;

/**
 * com.crp.Multithreading.examples.rabbitAndTortoise
 *
 * @author chenrunping
 * @create 2017-08-24 14:57
 **/
public class Tortoise extends Animal {

    public Tortoise() {
        setName("乌龟");// Thread的方法,给线程赋值名字
    }

    // 重写running方法,编写乌龟的奔跑操作
    @Override
    public void runing() {
        // 跑的距离
        double dis = 1;
        length -= dis;
        System.out.println("乌龟跑了" + dis + "米,距离终点还有" + (int) length + "");
        if (length <= 0) {
            length = 0;
            System.out.println("乌龟获得了胜利");
               // 让兔子不要在跑了
                calltoback.win();
        }

        try {
            sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

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


package com.crp.Multithreading.examples.rabbitAndTortoise;

/**
 * com.crp.Multithreading.examples.rabbitAndTortoise
 *
 * @author chenrunping
 * @create 2017-08-24 14:57
 **/
public class LetOneStop implements Animal.Calltoback {

    // 动物对象
    Animal an;

    // 获取动物对象,可以传入兔子或乌龟的实例
    public LetOneStop(Animal an) {
        this.an = an;
    }

    //让动物的线程停止
    @Override
    public void win() {
       // 线程停止(这里定义一个开关用于终止线程,属于安全终止线程的一个方法)
        an.isExit = true;
    }

}

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


package com.crp.Multithreading.examples.rabbitAndTortoise;

/**
 * com.crp.Multithreading.examples.rabbitAndTortoise
 *
 * @author chenrunping
 * @create 2017-08-24 14:57
 **/
public class Main {

    /**
     * 龟兔赛跑:100     */
    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();

    }

}

运行结果:

乌龟跑了1.0米,距离终点还有99米
兔子跑了5.0米,距离终点还有95米
兔子跑了5.0米,距离终点还有90米
乌龟跑了1.0米,距离终点还有98米
乌龟跑了1.0米,距离终点还有97米
乌龟跑了1.0米,距离终点还有96米
乌龟跑了1.0米,距离终点还有95米
乌龟跑了1.0米,距离终点还有94米
乌龟跑了1.0米,距离终点还有93米
乌龟跑了1.0米,距离终点还有92米
乌龟跑了1.0米,距离终点还有91米
乌龟跑了1.0米,距离终点还有90米
兔子跑了5.0米,距离终点还有85米
兔子跑了5.0米,距离终点还有80米
。。。。。。。。。。。。

。。。。。。。。。。。。
乌龟跑了1.0米,距离终点还有20米
兔子跑了5.0米,距离终点还有15米
兔子跑了5.0米,距离终点还有10米
乌龟跑了1.0米,距离终点还有19米
乌龟跑了1.0米,距离终点还有18米
乌龟跑了1.0米,距离终点还有17米
乌龟跑了1.0米,距离终点还有16米
乌龟跑了1.0米,距离终点还有15米
乌龟跑了1.0米,距离终点还有14米
乌龟跑了1.0米,距离终点还有13米
乌龟跑了1.0米,距离终点还有12米
乌龟跑了1.0米,距离终点还有11米
乌龟跑了1.0米,距离终点还有10米
兔子跑了5.0米,距离终点还有5米
兔子跑了5.0米,距离终点还有0米
兔子获得了胜利


Process finished with exit code 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.crp.Multithreading.examples.productorForCustomer;

import lombok.Data;

/**
 * com.crp.Multithreading.examples.productorForCustomer
 *
 * @author chenrunping
 * @create 2017-08-24 16:54
 **/
@Data
public class Food {

    String name="";
    //通过构造方法传入食物的名字
    public Food(String name) {
        this.name=name;
    }

}

(二)创建一个KFC类


package com.crp.Multithreading.examples.productorForCustomer;

import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;

/**
 * com.crp.Multithreading.examples.productorForCustomer
 *
 * @author chenrunping
 * @create 2017-08-24 16:56
 **/
@Slf4j
public class KFC {

    //食物的种类
    String[] names = { "薯条", "烧板", "鸡翅", "可乐" };

    //生产的最大值,到达后可以休息
    static final int Max = 20;

    //存放食物的集合
    List<Food> foods = new ArrayList<Food>();

    // 生产食物的方法
    public void prod(int index) {
        synchronized (this) {

            // 开始生产食物食物//有一点要注意的
            System.out.println("开始生产食物");
            for (int i = 0; i < index; i++) {
                Food food = new Food(names[(int) (Math.random() * 4)]);
                foods.add(food);
                log.info("{}生产了{},食物合计:{}个。" ,Thread.currentThread().getName(),food.getName(),foods.size());
                if(foods.size() >= Max){
                    break;
                }
                try {
                    Thread.sleep(100);
                }catch (Exception e){
                    log.info("异常!",e);
                }
            }

            // 如果食物数量大于等于20
            while (foods.size() >= Max) {
                System.out.println("食材够了");
                this.notifyAll();//这个唤醒是针对生产者和消费者,有all
                try {
                    String name=Thread.currentThread().getName();
                    this.wait();//这个等待是针对生产者,没有all
                    log.info("生产者:{}开始休息等待!",name);
                } catch (InterruptedException e) {
                    log.info("异常!",e);
                }
            }


        }
    }

    // 消费食物的方法
    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
                    log.info("消费者:{}开始休息等待!",name);
                } catch (InterruptedException e) {
                    log.info("异常!",e);
                }
            }
            // 足够消费
            System.out.println("开始消费");
            for (int i = 0; i < index; i++) {
                Food food = foods.remove(foods.size() - 1);
                System.out.println("消费了一个" + food.getName() + foods.size());
                if(foods.size() < index){
                    break;
                }
                try {
                    Thread.sleep(1000);
                }catch (Exception e){
                    log.info("异常!",e);
                }
            }
        }
    }
}


(三)创建一个客户类Customer


package com.crp.Multithreading.examples.productorForCustomer;

/**
 * com.crp.Multithreading.examples.productorForCustomer
 *
 * @author chenrunping
 * @create 2017-08-24 17:03
 **/
public class Customers extends Thread{
    KFC kfc;
    //KFC要传入,保证每一个服务员和用户在同一个KFC对象内
    public Customers(KFC kfc) {
        this.kfc=kfc;
    }
    @Override
    public void run() {
        int size=2;//每次要消费的食物的数量
        while (true) {
            kfc.consu(size);//在消费的方法里面传入参数
        }

    }
}


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


package com.crp.Multithreading.examples.productorForCustomer;

/**
 * com.crp.Multithreading.examples.productorForCustomer
 *
 * @author chenrunping
 * @create 2017-08-24 17:04
 **/
public class Waiter extends Thread{
    KFC kfc;
    //KFC要传入,保证每一个服务员和用户在同一个KFC对象内
    public Waiter(KFC kfc) {
        this.kfc=kfc;
    }
    @Override
    public void run() {
        int size=5;//每次生产的数量
        while (true) {
            kfc.prod(size);//传入每次生产的数量
        }

    }
}


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


package com.crp.Multithreading.examples.productorForCustomer;

/**
 * com.crp.Multithreading.examples.productorForCustomer
 *
 * @author chenrunping
 * @create 2017-08-24 16:54
 **/
public class Main {

    /**
     * 生产者消费者模式
     */
    public static void main(String[] args) {

        // 只实例化一个KFC对象,保证服务员和用户在同一个KFC对象内
        KFC kfc = new KFC();

        Customers c1 = new Customers(kfc);
        Customers c2 = new Customers(kfc);
        Customers c3 = new Customers(kfc);
        Customers c4 = new Customers(kfc);
        Customers c5 = new Customers(kfc);

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

}


猜你喜欢

转载自blog.csdn.net/qq_30490591/article/details/77550557