一、同步容器与并发容器
1、同步容器
1)类型 :Vector、HashTable--JDK提供的同步容器类
Collections.synchronizedXXX 本质是对相应的容器进行包装
2)缺点:单独使用里面的方法时是线程安全的,但是在复合操作中则需添加额外的锁来保证线程安全
(如在Iterator迭代容器或for-each遍历容器中,需在迭代过程持有容器的锁,但容器较大时,又会降低性能;或者使用“克隆”容器的方式,使用线程封闭,但在创建副本的时候也会降低性能)
3)示例:往同步容器中添加1000个元素,开启4个线程,删除其中其中一个元素
public class VectorDemo {
public static void main(String[] args) {
Vector<String> stringVector = new Vector<>();
for (int i = 0; i < 1000; i++) {
stringVector.add("demo" + i);
}
Iterator<String> stringIterator = stringVector.iterator();
for (int i = 0; i < 4; i++) {
new Thread(() -> {
synchronized (stringIterator) {
while (stringIterator.hasNext()) {
String next = stringIterator.next();
if (next.equals("demo2")) {
stringIterator.remove();
}
}
}
}).start();
}
}
}
2、并发容器
针对上面由于使用锁带来的并发访问性能问题,出现了并发容器,主要有这三大系列:CopyOnWrite\Concurrent\BlockingQueue
示例:同样是往并发容器中添加1000个元素,开启4个线程,删除其中一个元素
public class demo {
public static void main(String[] args) {
CopyOnWriteArrayList<String> strings = new CopyOnWriteArrayList<>();
for(int i=0;i<1000;i++){
strings.add("demo"+i);
}
for (int i =0;i<4;i++){
new Thread(()->{
strings.forEach(e->{
if (e.equals("demo2")){
strings.remove(e);
}
});
}).start();
}
}
}
二、LinkedBlockingQueue的使用
在并发编程中,LinkedBlockingQueue使用的非常频繁。因其可以作为生产者消费者的中间商
add 实际上调用的是offer,区别是在队列满的时候,add会报异常
offer 对列如果满了,直接入队失败
put(); 在队列满的时候,会进入阻塞的状态
remove(); 直接调用poll,唯一的区别即使remove会抛出异常,而poll在队列为空的时候直接返回null
poll(); 在队列为空的时候直接返回null
take(); 在队列为空的时候,会进入等待的状态
public class Demo2 {
public static void main(String[] args) throws InterruptedException {
LinkedBlockingQueue<String> strings = new LinkedBlockingQueue<>();
//往队列里存元素
strings.add("111");
strings.offer("111");
strings.put("111");
//从队列中取元素
String remove = strings.remove();
strings.poll();
strings.take();
}
}