一个Java 多线程程序实例

 

需求:仓库中一共有50个苹果,张三和李四同时不停地吃仓库中的苹果,吃到没有苹果为止,输出每次吃完一个苹果还剩几个苹果。

EatApple类,包含共有的苹果数量属性,以及吃苹果的方法:

class AppleEater implements Runnable{
    //仓库初始库存为10
    int remain = 10;
    public void run() {
        //调用吃苹果的方法
        eat();
    }
    public void eat() {
        while(true) {
            if(remain > 0) {
                remain--;
                System.out.println(Thread.currentThread().getName()+"吃了一个苹果,还剩"+remain+"个");
            }else {
                System.out.println("苹果没了,"+Thread.currentThread().getName()+"想吃也没得吃了");
                break;
            }
        }
    }
}

测试类:

public class Main {    
    public static void main(String[] args) {
        System.out.println("一开始有10个苹果");
        //创建一个实现类对象
        AppleEater ae = new AppleEater();
        //创建两个新线程,并传入Runnable实现类对象,调用start方法开启线程
        new Thread(ae).start();
        new Thread(ae).start();
    }
}

目前输出结果是这样的:

 

一开始有10个苹果
Thread-1吃了一个苹果,还剩8个
Thread-1吃了一个苹果,还剩7个
Thread-1吃了一个苹果,还剩6个
Thread-0吃了一个苹果,还剩6个
Thread-0吃了一个苹果,还剩4个
Thread-0吃了一个苹果,还剩3个
Thread-0吃了一个苹果,还剩2个
Thread-0吃了一个苹果,还剩1个
Thread-0吃了一个苹果,还剩0个
苹果没了,Thread-0想吃也没得吃了
Thread-1吃了一个苹果,还剩5个
苹果没了,Thread-1想吃也没得吃了

 

可见在异步中,会产生错误的输出结果,原因是多个线程访问了同一个属性。

修改方法一:使用synchronized代码块

将AppleEater类修改如下:

class AppleEater implements Runnable{
    //仓库初始库存为10
    int remain = 10;
    public void run() {
        //调用吃苹果的方法
        eat();
    }
    //定义锁对象
    Object lock = new Object();
    public void eat() {
        while(true) {
            synchronized(lock) {
                if(remain > 0) {
                    remain--;
                    System.out.println(Thread.currentThread().getName()+"吃了一个苹果,还剩"+remain+"个");
                }else {
                    System.out.println("苹果没了,"+Thread.currentThread().getName()+"想吃也没得吃了");
                    break;
                }
            }
        }
    }
}

输出结果为:

一开始有10个苹果
Thread-0吃了一个苹果,还剩9个
Thread-1吃了一个苹果,还剩8个
Thread-0吃了一个苹果,还剩7个
Thread-1吃了一个苹果,还剩6个
Thread-0吃了一个苹果,还剩5个
Thread-1吃了一个苹果,还剩4个
Thread-0吃了一个苹果,还剩3个
Thread-0吃了一个苹果,还剩2个
Thread-1吃了一个苹果,还剩1个
Thread-1吃了一个苹果,还剩0个
苹果没了,Thread-0想吃也没得吃了
苹果没了,Thread-1想吃也没得吃了

现在如果想记录每个线程吃了多少个苹果,可以用Callable接口

重新设计AppleEater类:

class AppleEater implements Callable<Integer>{
    //仓库初始库存为10
    int remain = 10;

    public Integer call() throws Exception {
        return eat();
    }
    //定义锁对象
    Object lock = new Object();
    public int eat() {
        int count = 0;
        while(true) {
            synchronized(lock) {
                if(remain > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    remain--;
                    count++;
                    System.out.println(Thread.currentThread().getName()+"吃了一个苹果,还剩"+remain+"个");
                }else {
                    System.out.println("苹果没了,"+Thread.currentThread().getName()+"想吃也没得吃了");
                    break;
                }
            }
        }
        return count;
    }
    
}

 

测试类

public static void main(String[] args) throws InterruptedException, ExecutionException {
        System.out.println("一开始有10个苹果");
        //创建线程池对象
        ExecutorService es = Executors.newFixedThreadPool(2);
        //创建Callable接口实现类AppleEater对象;
        
        AppleEater ae=new AppleEater();
        Future<Integer> f1 = es.submit(ae);
        Future<Integer> f2 = es.submit(ae);
        
        System.out.println("线程1吃了"+f1.get()+"个苹果");
        System.out.println("线程2吃了"+f2.get()+"个苹果");
        //关闭线程池对象
        es.shutdown();
    }

输出结果:

一开始有10个苹果
pool-1-thread-1吃了一个苹果,还剩9个
pool-1-thread-2吃了一个苹果,还剩8个
pool-1-thread-1吃了一个苹果,还剩7个
pool-1-thread-2吃了一个苹果,还剩6个
pool-1-thread-2吃了一个苹果,还剩5个
pool-1-thread-1吃了一个苹果,还剩4个
pool-1-thread-1吃了一个苹果,还剩3个
pool-1-thread-2吃了一个苹果,还剩2个
pool-1-thread-2吃了一个苹果,还剩1个
pool-1-thread-1吃了一个苹果,还剩0个
苹果没了,pool-1-thread-1想吃也没得吃了
苹果没了,pool-1-thread-2想吃也没得吃了
线程1吃了5个苹果
线程2吃了5个苹果

 

posted @ 2021-03-01 13:15  lucascube  阅读(385)  评论(0)    收藏  举报