JUC(二)集合类的不安全实例优化、callable、JUC3个常用辅助类
JUC(二)
集合类不安全
//java.util.ConcurrentModificationException 并发修改异常!
list不安全
并发下ArrayList不安全
package com.xiaobai.list;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
//java.util.ConcurrentModificationException 并发修改异常!
public class ListTest {
public static void main(String[] args) {
//List list = new ArrayList<>();
//List list = new Vector<>();
//List list = Collections.synchronizedList();
List list = new CopyOnWriteArrayList<>();
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();
}
}
}
解决方案一:使用vector(但是不建议这种使用,因为其发布比ArrayList还早)
List list = new Vector<>();
解决方案二:使用工具类Collections,使ArrayList变的安全
List list = Collections.synchronizedList(new ArrayList<>());
解决方案三:使用JUC包下的CopyOnWriteArrayList<>();
List list = new CopyOnWriteArrayList<>();
CopyOnWrite:写入时复制,简称COW。 计算机程序设计领域的一种优化策略
原谅:多个线程调用的时候,list,读取的时候是固定的,写入的时候存在覆盖问题,所以写入的时候复制一份容器再写入(且写入操作加锁),避免写入覆盖。
set不安全
package com.xiaobai.list;
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 set = new HashSet<>();
//Set set = Collections.synchronizedSet(new HashSet<>());
Set set = new CopyOnWriteArraySet<>();
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();
}
}
}
解决方案一:使用工具类Collections,使HashSet变的安全
Set set = Collections.synchronizedSet(new HashSet<>());
解决方案二:使用JUC包下的CopyOnWriteArraySet<>();
List list = new CopyOnWriteArraySet<>();
hashSet底层是什么?
public HashSet() {
map = new HashMap<>();
}
//add set 本质就是map key 是无法重复的!
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
private static final object PRESENT = new Obecet();//不变的值
HashMap不安全
HashMap详解
详解
ConcurrentHasMap
package com.xiaobai.list;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class MapTest {
public static void main(String[] args) {
//map是这样用的嘛?默认等价于什么?//工作不用map,
//Map map = new HashMap<>();
//默认:HashMap map = new HashMap<>(16,0.75)
//加载因子、初始容量
//Map map = Collections.synchronizedMap(new HashMap());
Map map = new ConcurrentHashMap<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
},String.valueOf(i)).start();
}
}
}
解决方案一:使用工具类Collections,使HashMap<>()变的安全
Map map = Collections.synchronizedMap(new HashMap());
解决方案二:使用JUC包下的ConcurrentHashMap<>();
Map map = new ConcurrentHashMap<>();
Callable
与Runnable的区别:
1、可以有返回值
2、可以抛出异常
3、方法不他,一个是run(),一个是call().
使用例子:
package com.xiaobai.callable;
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 {
//new Thread(new Runnable()).start();
//因为Thread只能执行Runnable,所以Callble需要适配类 FutureTask
MyThread myThread = new MyThread();
FutureTask stringFutureTask = new FutureTask<>(myThread);
new Thread(stringFutureTask,"a").start();
new Thread(stringFutureTask,"b").start();
String m = stringFutureTask.get();
System.out.println(m);
}
}
class MyThread implements Callable {
@Override
public String call() throws Exception {
System.out.println("sdf");
return "dad";
}
}
1、get(),可能需要等待
2、2个线程只输出一句,是因为有缓存
JUC常用的辅助类
CountDownLatch
package com.xiaobai.callable;
import java.util.concurrent.CountDownLatch;
//计数器,再必须执行的任务执行时,使用
public class CountDownLatchDemo {
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()+"运行了");
countDownLatch.countDown();//-1操作
},String.valueOf(i)).start();
}
countDownLatch.await();//等待计数器归零再执行后续
System.out.println("结束");
}
}
CyclicBarrier
package com.xiaobai.callable;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("结束");
});
for (int i = 1; i <= 18 ; 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();
}
}
}
Semaphore
Semaphore:信号量
package com.xiaobai.callable;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDmeo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);//如果达到3个线程,等待
for (int i = 1; i <= 6; i++) {
new Thread(()->{
try {
semaphore.acquire();//获得线程,如果已经满了,等待
System.out.println(Thread.currentThread().getName()+"启动了线程");
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName()+"关闭了线程");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();//释放,然后唤醒等待的线程。
}
},String.valueOf(i)).start();
}
}
}
semaphore.acquire();//获得线程,如果已经满了,等待
semaphore.release();//释放,然后唤醒等待的线程。
作用:
1、多个共享资源相互排斥使用
2、并发限流,控制最大的线程数。