一个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个苹果

浙公网安备 33010602011771号