摘要:本人邮箱欢迎转载转载请注明网址代码已经全部托管有需要的同学自行下载引言现在让我们来考虑一个问题如果要让多个线程来访问同一份数据会发生什么现象呢比如的火车售票系统比如银行的存取款系统等等都可以会出现多线程访问同一个数据的情况让我们先模拟写一个
引言本人邮箱:
欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco
github: https://github.com/kco1989/kco
代码已经全部托管github有需要的同学自行下载
现在,让我们来考虑一个问题,如果要让多个线程来访问同一份数据,会发生什么现象呢?比如12306的火车售票系统,比如银行的存取款系统等等.都可以会出现多线程访问同一个数据的情况.让我们先模拟写一个售票系统.
编码
首先创建一个Ticket类
增加两个成员变量count-->表示剩余的票,buyedCount-->已经卖出的票,并提供getter方法
增加一个buyTicket方法,用来模拟售票
public class Ticket { private static final int DEFAULT_TICKET_COUNT = 1000; private int count = DEFAULT_TICKET_COUNT; //票的总数 private int buyedCount = 0; public boolean buyTicket(int count) throws InterruptedException { if (this.count - count < 0){ Thread.sleep(10); return false; }else{ this.count = this.count - count; Thread.sleep(1); this.buyedCount = this.buyedCount + count; return true; } } public int getCount() { return count; } public int getBuyedCount() { return buyedCount; } public int getAllCount(){ return count + buyedCount; } }
之后创建一个模拟售票的类TicketRunnable,该类的构造器接收一个Ticket类
public static class TicketRunnable implements Runnable{ private Ticket ticket; private Random random; public TicketRunnable(Ticket ticket) { this.ticket = ticket; random = new Random(); } @Override public void run() { for (int i = 0; i < 5; i ++){ try { int count = random.nextInt(10) + 1; boolean success = ticket.buyTicket(count); System.out.println(String.format("%s打算买%d张票,买票%s了,还剩下%d张票,总共卖掉%d张票, 总票数%d", Thread.currentThread().getName(), count, success ? "成功" : "失败", ticket.getCount(),ticket.getBuyedCount(),ticket.getAllCount())); if (!success){ break; } } catch (InterruptedException e) { e.printStackTrace(); } } } }
最后创建一个main模拟20个售票点同时售票
public static void main(String[] args) throws InterruptedException { Listthreads = new ArrayList<>(); Ticket ticket = new Ticket(); for (int i = 0; i < 20; i ++){ threads.add(new Thread(new TicketRunnable(ticket))); } for (Thread thread : threads){ thread.start(); } }
截取某一次的部分运行结果:
Thread-1打算买2张票,买票成功了,还剩下441张票,总共卖掉558张票, 总票数999 Thread-8打算买1张票,买票成功了,还剩下441张票,总共卖掉552张票, 总票数993 Thread-6打算买1张票,买票成功了,还剩下434张票,总共卖掉559张票, 总票数993 Thread-14打算买7张票,买票成功了,还剩下431张票,总共卖掉566张票, 总票数997 Thread-6打算买3张票,买票成功了,还剩下431张票,总共卖掉569张票, 总票数1000问题
发现程序运行确实有问题
java提供了关键字synchronized可以保证数据同步,在Ticket的buyTicket和getter方法前加上synchronized,之后在运行一下程序,
Thread-13打算买4张票,买票成功了,还剩下457张票,总共卖掉543张票, 总票数1000 Thread-0打算买2张票,买票成功了,还剩下479张票,总共卖掉524张票, 总票数1000 Thread-6打算买4张票,买票成功了,还剩下444张票,总共卖掉556张票, 总票数1000 Thread-0打算买9张票,买票成功了,还剩下444张票,总共卖掉556张票, 总票数1000 Thread-6打算买2张票,买票成功了,还剩下442张票,总共卖掉558张票, 总票数1000
发现程序没有问题了
解决办法在getter方法上加synchronized是因为获取的变量也是公共的数据
synchronized的另外一种用法是在方法体内使用.在上述的例子中,在方法前加synchronized其实等效于synchronized(this){方法体},因为在上述的例子中公共的数据就是Ticket ticket = new Ticket();这个变量,在Ticket类中就相当与变量this
还有不使用synchronized(this){方法体}中的this也可以替换为另外一个公共的变量,如在Ticket类中定义个成员变量Object o = new Object();,然后使用synchronized(o){方法体}也可以保证数据同步.
打个比喻,比如现在有很多人都想进入某一个房间的卧室(至于想干嘛,大家自己脑补),synchronized(对象)中的对象就是一扇门,
而synchronized就是给这扇门加锁.那么不管这扇门是房间最外的大门,或者是卧室的门.只要所有人对这同一个门在同一个时间点仅仅有且只有一个能开门或者关门.那么就能保证进入卧室的人只有一个.
这里举个反例,比如进入卧室有两种渠道,一种是进前门,一种是进后门.(为什么卧室有前后门,肯定是有特殊用户了,哈哈...),那么有些人对前门加锁,另外一些人对后门加锁.这样就不能保证进入卧室的人只有一个了.(悲剧说不定就这样发生了)
在类的静态方法加synchronized,等效于synchronized(类.class){方法体}
另外,我们也在在不修改Ticket的基础上来保证售票数据的同步,只需要将TicketRunnable.run方法改为
public void run() { for (int i = 0; i < 5; i ++){ synchronized (ticket){ try { int count = random.nextInt(10) + 1; boolean success = ticket.buyTicket(count); System.out.println(String.format("%s打算买%d张票,买票%s了,还剩下%d张票,总共卖掉%d张票, 总票数%d", Thread.currentThread().getName(), count, success ? "成功" : "失败", ticket.getCount(),ticket.getBuyedCount(),ticket.getAllCount())); if (!success){ break; } Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } }
这样也能保证售票正常,那在这里能不能把synchronized (ticket){...}改为synchronized (random){...}呢?不能,因为random不是同一个对象,即各个线程只对自己的门加锁,不能保证是对同一个门加锁.
打赏如果觉得我的文章写的还过得去的话,有钱就捧个钱场,没钱给我捧个人场(帮我点赞或推荐一下)
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/67064.html
摘要:线程可以调用的方法进入阻塞,当计数值降到时,所有之前调用阻塞的线程都会释放。注意的初始计数值一旦降到,无法重置。 showImg(https://segmentfault.com/img/remote/1460000016012041); 本文首发于一世流云的专栏:https://segmentfault.com/blog... 一、CountDownLatch简介 CountDow...
时间:2017年07月09日星期日说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学源码:无学习源码:https://github.com/zccodere/s... 第一章:课程简介 1-1 课程简介 课程目标和学习内容 共享变量在线程间的可见性 synchronized实现可见性 volatile实现可见性 指令重排序 as-if-seria...
阅读 3011·2021-11-18 10:02
阅读 3265·2021-11-02 14:48
阅读 3343·2019-08-30 13:52
阅读 501·2019-08-29 17:10
阅读 2046·2019-08-29 12:53
阅读 1350·2019-08-29 12:53
阅读 966·2019-08-29 12:25
阅读 2097·2019-08-29 12:17