集合不安全
1.List不安全
通常我们在单线程的情况下去操作集合是不会出现任何问题的。
但是如果在多线程的情况下去操作集合呢。
下面代码来看一下
package com.ycm.unsafe;
import java.util.ArrayList;
import java.util.UUID;
public class ListTest {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
for (int i = 1; i <= 30; i++) {
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
结果:
出现了ConcurrentModificationException异常,这是在多线程修改集合的时候才会出现的并发修改异常
那么如何来解决呢?
解决方案一:Vector
查看Vector的源码:
是在方法加上了synchronized关键字来解决多线程的问题,但是会带来效率低下的问题
解决方案二:Collections.synchronizedList()
解决方案三:CopyOnWriteArrayList<>()
这是JUC包下的下线程安全类
查看源码:
CopyOnWrite,写入时复制
进行读写分离。在写入的时候避免覆盖,造成数据问题
2.Set不安全
同理和List相同:
package com.ycm.unsafe;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
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 = 1; i <= 50; i++) {
new Thread(()->{
set.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(set);
},String.valueOf(i)).start();
}
}
}
3.map 不安全
map相对于List和Set没有CopyOnWrite类,只能使用ConcurrentHashMap
package com.ycm.unsafe;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class MapTest {
public static void main(String[] args) {
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();
}
}
}
Callable
- 可以有返回值
- 可以抛出异常
- 使用方法call()
将futureTask作为一个中间类
package com.ycm.unsafe;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread myThread = new MyThread();
FutureTask futureTask = new FutureTask(myThread);
new Thread(futureTask, "A").start();
//这里虽然有两个执行。但是因为存在缓存机制,因此只打印一个,效率更高
new Thread(futureTask, "B").start();
String s = (String) futureTask.get(); //这个方法可能会产生阻塞,最好放到最后
System.out.println(s);
}
}
class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
return "hello";
}
}
常用的辅助类
1.CountDownLatch
package com.ycm.util;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"Go out");
countDownLatch.countDown(); //数量-1
}).start();
}
countDownLatch.await(); //等待计时器归零,然后向下执行
System.out.println("CLose door");
}
}
每次有线程调用 countDown() 数量-1,假设计数器变为0,countDownLatch.await() 就会被唤醒,继续执行!
2.Cyclicbarrier
public class CyclicBarrierTest {
public static void main(String[] args) {
//通俗的讲就是加法计数器
CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->{
System.out.println("召唤神龙!");
});
for (int i = 1; i <= 7; 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.Semaphone
作为多个共享资源互斥的使用
public class SemaphoneTest {
public static void main(String[] args) {
//线程数量:停车位:限流
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i <= 6; i++) {
new Thread(()->{
try {
semaphore.acquire(); //获得,如果已经满了,就等待到释放为止
System.out.println(Thread.currentThread().getName()+"抢到车位");
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();//释放,将当前的信号量释放+1,然后唤醒等待的线程
}
},String.valueOf(i)).start();
}
}
}