1、集合类不安全
1.1、List 不安全
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class ListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
}).start();
}
}
}
当多个线程对一个不安全List进行写操作的时候会造成 java.util.ConcurrentModificationException 并发修改异常!
解决方案:
-
List<String> list = new Vector<>();
Vector底层的add方法是加了synchronized修饰的,所以的安全的
-
List<String> list = Collections.synchronizedList(new ArrayList<>());
-
List<String> list = new CopyOnWriteArrayList<>();
CopyOnWriteArrayList 底层使用的是Lock锁,在写入的时候避免覆盖,造成数据问题!
1.2、Set 不安全
public class SetTest {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(set);
}, String.valueOf(i)).start();
}
}
}
解决:
-
Set<String> set = Collections.synchronizedSet(new HashSet<>());
-
Set<String> set = new CopyOnWriteArraySet<>();
- HashSet的底层是什么?
public HashSet() {
map = new HashMap<>(); // HashSet底层就是一个HashMap
} // set 本质就是 map 的 key,因为map的key是无法重复的
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
private static final Object PRESENT = new Object(); // key是set的值,value是一个常量
1.3、Map 不安全
public class MapTest {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 5));
System.out.println(map);
}, String.valueOf(i)).start();
}
}
}
解决:
-
Map<String, String> map = Collections.synchronizedMap(new HashMap<>());
-
Map<String, String> map = new ConcurrentHashMap<>();
扫描二维码关注公众号,回复:
12911399 查看本文章

2、Callable
- 可以有返回值
- 可以抛出异常
- 方法不同,run() / call()
public class TestCallable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> futureTask = new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("调用了call方法");
return 1024;
}
});
new Thread(futureTask, "A").start();
new Thread(futureTask, "B").start(); 打印一次,同一个任务结果会被缓存,效率高
System.out.println(futureTask.get());
}
}
细节:1、有缓存
2、结果要执行完才有,所以可能需要等待,会阻塞!
3、常用的辅助类
3.1、CountDownLatch—减法计数器
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// 总数是6
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "执行完毕");
countDownLatch.countDown(); // 数量 - 1
}, String.valueOf(i)).start();
}
countDownLatch.await(); //等待计数器归零,然后再向下执行
System.out.println("关机");
}
}
原理:
-
countDownLatch.countDown(); // 数量 - 1
-
countDownLatch.await(); //等待计数器归零,然后再向下执行
每次有线程调用, countDown() 数量-1,假设计数器变为0,countDownLatch.await() 就会被唤醒,继续执行!
3.2、CyclicBarrier—加法计数器
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("召唤神龙");
});
for (int i = 1; i <= 9; i++) {
final int temp = i;
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "收集了第" + temp +"个龙珠");
try {
cyclicBarrier.await(); //等待线程数量达到就执行创建时设置的任务
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
3.3、Semaphore—信号量
public class SemaphoreDemo {
public static void main(String[] args) {
//资源个数,允许线程访问的数量! 限流~
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 6; i++) {
new Thread(() -> {
try {
semaphore.acquire(); // 获得资源---P 操作
System.out.println(Thread.currentThread().getName() + "抢到了资源");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "放弃了资源");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(); // 释放资源---V 操作
}
}).start();
}
}
}
原理:
-
semaphore.acquire(); // 获得资源—P 操作,如果已经满了就等待
-
semaphore.release(); // 释放资源—V 操作
作用:多个共享资源互斥的使用!并发限流,控制最大的线程数!
4、ReadWriteLock—读写锁
/**
* 独占锁(写锁) 一次只能被一个线程占有
* 共享锁(读锁) 多个线程可以同时占有
* ReadWriteLock
* 读-读 可以共存!
* 读-写 不能共存!
* 写-写 不能共存!
*/
class MyCacheLock {
private volatile List<String> list = new ArrayList<>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void add(String value) {
readWriteLock.writeLock().lock(); // 写锁,每次只能有一个线程写
try {
System.out.println(Thread.currentThread().getName() + "写入" +value);
list.add(value);
System.out.println(Thread.currentThread().getName() + "写入完毕!");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
public void get(int i) {
readWriteLock.readLock().lock(); //读锁,所有线程都可以访问
try {
System.out.println(Thread.currentThread().getName() + "读取" + list.get(i));
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "读取完毕!");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCacheLock myCacheLock = new MyCacheLock();
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(() -> {
myCacheLock.add(temp + "hello");
}, String.valueOf(i)).start();
}
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(() -> {
myCacheLock.get(temp);
}, String.valueOf(i)).start();
}
}
}
5、阻塞队列—BlockingQueue
5.1、四组API
方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer | put | offer |
移除 | remove | poll | take | poll |
检测队首元素 | element | peek |
-
抛出异常
public class BlockingQueueDemo { public static void main(String[] args) { // 队列的大小 ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3); System.out.println(blockingQueue.add("a")); System.out.println(blockingQueue.add("b")); System.out.println(blockingQueue.add("c")); // IllegalStateException 异常 System.out.println(blockingQueue.add("d")); System.out.println(blockingQueue.remove()); System.out.println(blockingQueue.remove()); System.out.println(blockingQueue.remove()); // NoSuchElementException 异常 System.out.println(blockingQueue.remove()); } }
-
有返回值,没有异常
public class BlockingQueueDemo { public static void main(String[] args) { // 队列的大小 ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3); System.out.println(blockingQueue.offer("a")); System.out.println(blockingQueue.offer("b")); System.out.println(blockingQueue.offer("c")); System.out.println(blockingQueue.offer("d")); //没有异常,返回 false System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); //没有异常,返回 null } }
-
阻塞等待
public class BlockingQueueDemo { public static void main(String[] args) throws InterruptedException { // 队列的大小 ArrayBlockingQueue<Object> 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 class BlockingQueueDemo { public static void main(String[] args) throws InterruptedException { // 队列的大小 ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3); blockingQueue.offer("a"); blockingQueue.offer("b"); blockingQueue.offer("c"); blockingQueue.offer("d", 2, TimeUnit.SECONDS); //等待2秒,没有位置就退出 System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); System.out.println(blockingQueue.poll()); blockingQueue.poll(2, TimeUnit.SECONDS); // 等待两秒,没有数据来就退出 } }
5.2、SynchronizedQueue—同步队列
- SynchronizedQueue不能存储元素,put了一个值,必须从里面take出来,否者不能put进去值
public class SynchronizedQueueDemo {
public static void main(String[] args) {
SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();
new Thread(() -> {
try {
synchronousQueue.put("1");
System.out.println(Thread.currentThread().getName() + "放入了 1");
synchronousQueue.put("2");
System.out.println(Thread.currentThread().getName() + "放入了 2");
synchronousQueue.put("3");
System.out.println(Thread.currentThread().getName() + "放入了 3");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "A").start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "取出了 " + synchronousQueue.take());
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "取出了 " + synchronousQueue.take());
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "取出了 " + synchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "B").start();
}
}