1. 10个窗口卖票程序的例子—->要考虑是不是线程安全的,要不然可能会出现超卖的问题
  2. Vector里面是自带锁的(在他的所有方法前面都加上了锁)

使用普通的ArrayList会产生超卖的问题

只使用Vector也是不行的,因为他只在方法上加锁了,在size()和remove()之间是没有加锁的,同样会有超卖的问题

  1. 不是判断没有加锁,而是在size()和remove()之间是没有加锁的
  2. 好多线程判断size是大于0的,因为从size判断到remove之间还有一段空闲的时间片他会进行抢占
  3. 整段代码不是原子的,只用Vector是无法解决这个问题的
  4. 虽然调用了两个原子方法,但是还得在外面加一把锁

需要在外面再加一把锁,但是这样效率不高,不是最佳解决方案

  1. Vector —-> List —-> Queue
  2. Queue主要是为了考虑高并发、多线程使用的
  3. 单个元素多线程时更多地考虑Queue,不考虑List和Set
  4. 要不重复的时候还是考虑Set,Set也有Concurrent的Set,读api
  5. 当有重复的时候,多考虑Queue,少考虑List
  6. 多线程情况下,少考虑List,多考虑Concurrent开头的这些
  7. 打印的前后有问题是正常的,但最终还是无重复的卖了1000张票

ConcurrentLinkedQueue

  1. poll从队头取出元素,假如没有元素了,就会返回空值null
  2. 1中的取是原子性的取,但实现上不是用synchronized来实现的
  3. 是用CAS来实现的
  4. 无锁化实现原子性的操作,效率上会高很多(锁不是重量级锁了)
  5. 加sleep代码会出问题吗???
  6. Concurrent与Blocking一般不连用
    1. 并发就是并发,直接就可以了
    2. 阻塞就是阻塞,不是并发
  7. 可以采用这种简单的不加锁的方式,效率会高上很多

image.png
image.png

这就是容器整体的演化过程

  1. 这不是替代关系!!!
  2. 后出现的并不是说在所有情况下的效率都比之前出现的要高
  3. 归根到底是CAS操作一定比synchronized效率高吗?
    1. 不一定
    2. 要看并发量的高低,要看锁定之后那段代码的执行时间
  4. 不是替代关系,而是中间给你提供了这么多的容器
  5. 这些容器在使用的时候需要根据实际情况来灵活地去运用他
    1. 有些简单的,比如只有一个线程,用HashMap就行了,其他可以不考虑、就应该用LinkedList、ArrayList
    2. 高并发情况下,执行时间比较短,就应该用ConcurrentHashMap、ConcurrentLinkedQueue
    3. 假如代码执行时间特别长,而且并发量不是特别高时,这种情况用synchronized
    4. 任何时候,在实际情况下,都需要通过测试、压测来决定到底用哪种容器


如何测试—->设计上的方法—->面向接口编程

  1. why?
  2. 真正工作之中设计一个程序,先设计一个接口,这个接口中只包含业务逻辑(取出时间列表)
  3. 这个业务的具体实现是放到HashTable中还是放到HashMap中还是放到ConcurrentHashMap中,可以写好几个具体的不同的实现——>在不同的并发情况下,采用具体的不同的实现,程序才会更灵活
  4. 面向接口编程、面向接口设计的灵活之所在
  5. 可以灵活切换具体实现—->可插拔、解耦
  6. 写的程序越多,越会发现是贯通的,越学越多,越学越深入的时候,就能更深刻地理解别人为什么这么设计或者为什么这么写的

任何时候,在实际情况下,都需要通过测试、压测来决定到底用哪种容器