- 10个窗口卖票程序的例子—->要考虑是不是线程安全的,要不然可能会出现超卖的问题
- Vector里面是自带锁的(在他的所有方法前面都加上了锁)
使用普通的ArrayList会产生超卖的问题
只使用Vector也是不行的,因为他只在方法上加锁了,在size()和remove()之间是没有加锁的,同样会有超卖的问题
- 不是判断没有加锁,而是在size()和remove()之间是没有加锁的
- 好多线程判断size是大于0的,因为从size判断到remove之间还有一段空闲的时间片他会进行抢占
- 整段代码不是原子的,只用Vector是无法解决这个问题的
- 虽然调用了两个原子方法,但是还得在外面加一把锁
需要在外面再加一把锁,但是这样效率不高,不是最佳解决方案
- Vector —-> List —-> Queue
- Queue主要是为了考虑高并发、多线程使用的
- 单个元素多线程时更多地考虑Queue,不考虑List和Set
- 要不重复的时候还是考虑Set,Set也有Concurrent的Set,读api
- 当有重复的时候,多考虑Queue,少考虑List
- 多线程情况下,少考虑List,多考虑Concurrent开头的这些
- 打印的前后有问题是正常的,但最终还是无重复的卖了1000张票
ConcurrentLinkedQueue
- poll从队头取出元素,假如没有元素了,就会返回空值null
- 1中的取是原子性的取,但实现上不是用synchronized来实现的
- 是用CAS来实现的
- 无锁化实现原子性的操作,效率上会高很多(锁不是重量级锁了)
- 加sleep代码会出问题吗???
- Concurrent与Blocking一般不连用
- 并发就是并发,直接就可以了
- 阻塞就是阻塞,不是并发
- 可以采用这种简单的不加锁的方式,效率会高上很多
这就是容器整体的演化过程
- 这不是替代关系!!!
- 后出现的并不是说在所有情况下的效率都比之前出现的要高
- 归根到底是CAS操作一定比synchronized效率高吗?
- 不一定
- 要看并发量的高低,要看锁定之后那段代码的执行时间
- 不是替代关系,而是中间给你提供了这么多的容器
- 这些容器在使用的时候需要根据实际情况来灵活地去运用他
- 有些简单的,比如只有一个线程,用HashMap就行了,其他可以不考虑、就应该用LinkedList、ArrayList
- 高并发情况下,执行时间比较短,就应该用ConcurrentHashMap、ConcurrentLinkedQueue
- 假如代码执行时间特别长,而且并发量不是特别高时,这种情况用synchronized
- 任何时候,在实际情况下,都需要通过测试、压测来决定到底用哪种容器
如何测试—->设计上的方法—->面向接口编程
- why?
- 真正工作之中设计一个程序,先设计一个接口,这个接口中只包含业务逻辑(取出时间列表)
- 这个业务的具体实现是放到HashTable中还是放到HashMap中还是放到ConcurrentHashMap中,可以写好几个具体的不同的实现——>在不同的并发情况下,采用具体的不同的实现,程序才会更灵活
- 面向接口编程、面向接口设计的灵活之所在
- 可以灵活切换具体实现—->可插拔、解耦
- 写的程序越多,越会发现是贯通的,越学越多,越学越深入的时候,就能更深刻地理解别人为什么这么设计或者为什么这么写的