文章目录
此部分可查看 https://blog.youkuaiyun.com/wang_luwei/article/details/107490131
一、List中的不安全 (ArrayList 是不安全的)
可对比参考:https://www.cnblogs.com/zz-ksw/p/12774371.html
package com.wlw.unsafe;
import java.util.ArrayList;
import java.util.UUID;
//Exception in thread "3" java.util.ConcurrentModificationException 并发修改异常
public class ListTest {
public static void main(String[] args) {
ArrayList<String> arrayList = new ArrayList<String>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
arrayList.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(arrayList);
},String.valueOf(i)).start();
}
}
}
- 解决方案
package com.wlw.unsafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.Condition;
//Exception in thread "3" java.util.ConcurrentModificationException 并发修改异常
/**
* 解决方案:
* 1. List<String> List = new Vector<>();
* 2.List<String> List = Collections.synchronizedList(new ArrayList<>());
* 3. List list = new CopyOnWriteArrayList<String>();(这是juc包下的)
*
* CopyonWrite 写入时复制 cow 计算机程序设计领域的一种优化策略
* 多个线程调用的时候,list是唯一的,在读取的时候是固定的,在写入的时候(可能存在覆盖操作),这种覆盖操作怎么解决?
* 就是在写入的时候复制一份,复制完给调用者,调用者写完之后再放入原本的集合中
* 就是在写入的时候避免覆盖,造成数据问题!
* 读写分离的思想
* CopyonWriteArrayList比vector 优秀在哪里? Vector底层是使用synchronized关键字来实现的:效率特别低下。 CopyOnWriteArrayList使用的是Lock锁,效率会更加高效!
*/
public class ListTest {
public static void main(String[] args) {
//List<String> List = new ArrayList<String>();
List list = new CopyOnWriteArrayList<String>();
for (int i = 0; i < 10; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
二、 set 中的不安全
package com.wlw.unsafe;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/**
*和List一样 并发修改异常 Exception in thread "18" java.util.ConcurrentModificationException
*解决方案:
* 1.Set<String> set = Collections.synchronizedSet(new HashSet<>());
* 2.Set<String> set = new CopyOnWriteArraySet<>(); (juc包下的类)
*/
public class SetTest {
public static void main(String[] args) {
//Set<String> set = new HashSet<>();
//Set<String> set = Collections.synchronizedSet(new HashSet<>());
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,6));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
三、 map中的不安全
package com.wlw.unsafe;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* 多线程使用HashMap()也会 造成 java.util.ConcurrentModificationException (并发修改异常)
* 解决方案:
* 1.Map<String, String> map = Collections.synchronizedMap( new HashMap<>());
* 2. Map<String, String> map = new ConcurrentHashMap<>(); (juc包下的类)
*/
public class MapTest {
public static void main(String[] args) {
//map 是这样用的吗? 不是,工作中不使用 HashMap()
//默认等价什么? new HashMap<>(16,0.75);加载因子为0.75、初始化容量16
//Map<String, String> map = new HashMap<>();
//Map<String, String> map = Collections.synchronizedMap( new HashMap<>());
Map<String, String> map = new ConcurrentHashMap<>();
for (int i = 1; i <30; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
四、阻塞队列(BlockingQueue接口)
-
什么情况我们会使用 阻塞队列呢? 多线程并发处理、线程池!
-
操作:添加、移除 (四组API)
方式 | 抛出异常 | 不会抛出异常,有返回值 | 阻塞 等待 | 超时 等待 |
---|---|---|---|---|
添加 | add | offer | put | offer(e,timenum,timeUnit) |
移除 | remove | poll | take | poll(timenum,timeUnit) |
判断队列首 | element | peek | - | - |
package com.wlw.BQ;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) throws InterruptedException {
test1();
test2();
test3();
test4();
}
//会直接在控制台抛出异常的方法
//抛异常,有返回值
public static void test1(){
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(arrayBlockingQueue.add("a"));
System.out.println(arrayBlockingQueue.add("b"));
System.out.println(arrayBlockingQueue.add("c"));
//最多就能放3个,java.lang.IllegalStateException: Queue full
//System.out.println(arrayBlockingQueue.add("d"));
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
//队列空的时候,再移除就报异常 java.util.NoSuchElementException
System.out.println(arrayBlockingQueue.remove());
}
/**
* 不抛出异常,有返回值
*/
public static void test2(){
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
//添加 一个不能添加的元素 使用offer只会返回false 不会抛出异常
System.out.println(blockingQueue.offer("d"));
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
//弹出 如果没有元素 只会返回null 不会抛出异常
System.out.println(blockingQueue.poll());
}
/**
* 等待 一直阻塞
*/
public static void test3() throws InterruptedException {
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
//一直阻塞 不会返回
blockingQueue.put("a");
blockingQueue.put("b");
blockingQueue.put("c");
//如果队列已经满了, 再进去一个元素 这种情况会一直等待这个队列 什么时候有了位置再进去,程序不会停止
//blockingQueue.put("d");
System.out.println(blockingQueue.take()); //有返回值
System.out.println(blockingQueue.take());
System.out.println(blockingQueue.take());
//如果我们再来一个 这种情况也会等待,程序会一直运行 阻塞
System.out.println(blockingQueue.take());
}
/**
* 等待 超时阻塞
* 这种情况也会等待队列有位置 或者有产品 但是会超时结束
*
*/
public static void test4() throws InterruptedException {
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.offer("a");
blockingQueue.offer("b");
blockingQueue.offer("c");
System.out.println("开始等待");
blockingQueue.offer("d",2, TimeUnit.SECONDS); //超时时间2s 等待如果超过2s就结束等待
System.out.println("结束等待");
System.out.println("===========取值==================");
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println("开始等待");
blockingQueue.poll(2,TimeUnit.SECONDS); //超过两秒 我们就不要等待了
System.out.println("结束等待");
}
}
4.1SynchronousQueue同步队列
-
同步队列 没有容量,也可以视为容量为1的队列;
-
进去一个元素,必须等待取出来之后,才能再往里面放入一个元素;
-
put方法 和 take方法;
-
SynchronousQueue 和 其他的BlockingQueue 不一样 它不存储元素;
-
put了一个元素,就必须从里面先take出来,否则不能再put进去值!
package com.wlw.BQ;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/**
* 同步队列和其他的BlockingQueue不一样,
* SynchronousQueue不存储元素
* put了一个元素,必须从里面先take取出来,否则不能再put进去值!
*/
public class SynchronousQueueDemo {
public static void main(String[] args) {
SynchronousQueue<String> stringSynchronousQueue = new SynchronousQueue<>();
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+"put a");
stringSynchronousQueue.put("a");
System.out.println(Thread.currentThread().getName()+"put b");
stringSynchronousQueue.put("b");
System.out.println(Thread.currentThread().getName()+"put c");
stringSynchronousQueue.put("c");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"T1").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"take a");
stringSynchronousQueue.take();
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"take b");
stringSynchronousQueue.take();
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"take c");
stringSynchronousQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"T2").start();
}
}