文章目录
1. 集合类不安全
1.1 List不安全
代码如下:
//java.util.ConcurrentModificationException 并发修改异常
public class ListTest {
public static void main(String[] args) {
// 并发下ArrayList不安全
/**
* 解决方案:
* 1. List<String> list = new Vector<>();
* 2. List<String> list = Collections.synchronizedList(new ArrayList<>());
* 3. List<System> list = new CopyOnWriteArrayList<>();
*/
// CopyOnWrite写入时复制 COW 计算机程序设计领域的一种优化策略
// 多个线程调用的时候,list读取的时候,固定的,写入(覆盖)
// 在写入的时候避免覆盖造成数据问题
// 读写分离
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 1; i <= 10; i ++ ) {
new Thread(()-> {
list.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(list);
}, String.valueOf(i)).start();
}
}
}
运行结果如下:
1.2 Set不安全
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 <= 30; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0, 5));
System.out.println(set);
}, String.valueOf(i)).start();
}
}
}
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 Object(); //不变得值!
1.3 Map不安全
public class MapTest {
public static void main(String[] args) {
// map是这样用的吗 不是,工作中不用HashMap
// 默认等价于什么 new HashMap<>(16,0.75);
// Map<String, String> map = 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();
}
}
}
2. Callable(简单)
- 可以有返回值
- 可以抛出异常
- 方法不同,run() / call()
测试代码如下:
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// new Thread(new Thread()).start();
// new Thread(new FutureTask<V>).start();
// new Thread().start(); // 怎么启动Callable
MyThread thread = new MyThread();
FutureTask futureTask = new FutureTask(thread); // 适配类
new Thread(futureTask, "A").start();
String o = (String) futureTask.get(); // 获取Callable 的返回结果。 这个get方法可能会产生阻塞,把它放到最后或者用异步通信来处理
System.out.println(o);
}
}
class MyThread implements Callable<String> {
@Override
public String call() {
System.out.println("call()");
return "123";
}
}
运行结果如下:
call()
123
细节:
- 有缓存
- 结果可能需要等待会阻塞
3. 常用的辅助类(必会)
3.1 CountDownLatch
测试代码如下:
// 计数器
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// 总数是6,必须要执行任务的时候,再使用
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
}, String.valueOf(i)).start();
}
countDownLatch.await(); // 等待计数器归0,然后再向下执行
System.out.println("close door");
}
}
运行结果如下:
原理:
countDownLatch.countDown();
// 数量 -1
countDownLatch.await()
// 等待计数器归0,然后再向下执行
每次有线程调用 countDown()
数量-1,假设计数器变为0, countDownLatch.await()
就会被唤醒,继续执行。
3.2 CyclicBarrier
CyclicBarrier
即 加法计数器,测试代码如下:
public class CyclicBarrierDemo {
public static void main(String[] args) {
/**
* 集齐7颗龙珠召唤神龙
*/
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
System.out.println("召唤神龙成功!");
});
for (int i = 1; i <= 7; i ++ ) {
final int temp = i;
// lambda 不能直接取到i,必须借助于temp
new Thread(()-> {
System.out.println(Thread.currentThread().getName() + "收集" + temp + "个龙珠");
try {
cyclicBarrier.await(); // 等待计数器变成7
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
运行结果如下:
3.3 Semaphore
Semaphore
即信号量。
测试代码如下:
public class SemaphoreDemo {
// 6辆车、3个车位
public static void main(String[] args) {
// 线程数量;停车位!限流!
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6; i ++ ) {
new Thread(()-> {
// acquire() 得到
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "抢到车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
// release() 释放
}, String.valueOf(i)).start();
}
}
}
运行结果如下:
原理:
semaphore.acquire();
获得,假设如果已经满了,等待被释放为止。
semaphore.release();
释放,会将当前的信号量释放+1,然后唤醒等待的线程。
作用:多个共享资源互斥的使用!并发限流,控制最大的线程数。
4. 读写锁
旧版:即自定义缓存,测试代码如下:
public class ReadWriteLock {
public static void main(String[] args) {
MyCache myCache = new MyCache();
// 只写入
for (int i = 1; i <= 5; i ++ ) {
final int temp = i;
new Thread(()-> {
myCache.put(temp + "", temp + "");
}, String.valueOf(i)).start();
}
// 读取
for (int i = 1; i <= 5; i ++ ) {
final int temp = i;
new Thread(()-> {
myCache.get(temp + "");
}, String.valueOf(i)).start();
}
}
}
/**
* 自定义缓存
*/
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
// 存,写
public void put(String key, Object value) {
System.out.println(Thread.currentThread().getName() + "写入" + key);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入完毕");
}
// 取,读
public void get(String key) {
System.out.println(Thread.currentThread().getName() + "读取" + key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取完毕");
}
}
运行结果如下:
出现的问题:在一个线程写入的过程中会有其他线程的插入,可以用加锁的缓存来解决该问题,相关代码如下:
/**
* 独占锁(写锁) 一次只能被一个线程占有
* 共享锁(读锁) 多个线程可以同时占有
* 读——读 可以共存
* 读——写 不能共存
* 写——写 不能共存
*/
public class ReadWriteLock {
public static void main(String[] args) {
//MyCache myCache = new MyCache();
MyCacheLock myCache = new MyCacheLock();
// 只写入
for (int i = 1; i <= 5; i ++ ) {
final int temp = i;
new Thread(()-> {
myCache.put(temp + "", temp + "");
}, String.valueOf(i)).start();
}
// 读取
for (int i = 1; i <= 5; i ++ ) {
final int temp = i;
new Thread(()-> {
myCache.get(temp + "");
}, String.valueOf(i)).start();
}
}
}
/**
* 加锁的
*/
class MyCacheLock {
private volatile Map<String, Object> map = new HashMap<>();
// 读写锁:更加细粒度的控制
private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//存,写的时候,只希望同时只有一个线程写
public void put(String key, Object value) {
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "写入" + key);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入完毕");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
// 取,读的时候,所有人都可以读取
public void get(String key) {
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "读取" + key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取完毕");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
/**
* 自定义缓存
*/
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
// 存,写
public void put(String key, Object value) {
System.out.println(Thread.currentThread().getName() + "写入" + key);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入完毕");
}
// 取,读
public void get(String key) {
System.out.println(Thread.currentThread().getName() + "读取" + key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取完毕");
}
}
运行结果如下:
5. 阻塞队列
5.1 阻塞队列BlockingQueue
什么情况下会使用阻塞队列:多线程并发处理,线程池!
使用队列: 添加、移除
5.2 BlockingQueue四组API
方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer() | put() | offer(, ,) |
删除 | remove | poll() | take() | poll(,) |
检测队首元素 | element | peek() |
- 抛出异常的测试代码如下:
public class Test {
public static void main(String[] args) {
test1();
}
/**
* 抛出异常
*/
public static void test1() {
// 队列的大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
System.out.println("---------------");
//IllegalStateException: Queue full 抛出异常!
//System.out.println(blockingQueue.add("d"));
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
//java.util.NoSuchElementException 抛出异常!
//System.out.println(blockingQueue.remove());
}
}
运行结果如下:
2. 有返回值,没有异常的测试代码如下:
public class Test {
public static void main(String[] args) {
test2();
}
/**
* 有返回值,没有异常
*/
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"));
System.out.println(blockingQueue.offer("d")); // 不抛出异常,false
System.out.println(blockingQueue.peek()); // 弹出队首元素
System.out.println("--------");
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll()); // 不抛出异常,null
}
}
运行结果如下:
3. 等待,阻塞(一直阻塞) 的测试代码如下:
public class Test {
public static void main(String[] args) throws InterruptedException {
test3();
}
/**
* 等待,阻塞(一直阻塞)
*/
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()); // 没有这个元素,一直阻塞
}
}
运行结果如下:
4. 等待,阻塞(等待超时) 测试代码如下:
public class Test {
public static void main(String[] args) throws InterruptedException {
test4();
}
/**
* 等待,阻塞(等待超时)
*/
public static void test4() throws InterruptedException {
// 队列的大小
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
blockingQueue.offer("a");
blockingQueue.offer("b");
blockingQueue.offer("c");
//blockingQueue.offer("d", 2, TimeUnit.SECONDS); // 等待超过2秒就退出
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)); // 等待超过2秒就退出
}
}
运行结果如下:
5.3 SynchronousQueue同步队列
SynchronousQueue
是没有容量的,进去一个元素,必须等待取出来之后,才能再往里面放入一个元素。
添加操作 put
、删除操作take
。
测试代码如下:
/**
* 同步队列
* 和其他的BlockQueue不一样,SynchronousQueue不存储元素
* put 了一个元素,必须从里面take取出来,否则不能put进去值
*/
public class SynchronousQueueDemo {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue = new SynchronousQueue<>(); // 同步队列
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName() + " put 1");
blockingQueue.put("1");
System.out.println(Thread.currentThread().getName() + " put 2");
blockingQueue.put("2");
System.out.println(Thread.currentThread().getName() + " put 3");
blockingQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "T1").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + " take " + blockingQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + " take " + blockingQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + " take " + blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "T2").start();
}
}
运行结果如下:
6. 线程池(重点)
线程池:3大方法、7大参数、4种拒绝策略。
线程池的好处:
- 降低资源的消耗。
- 提高响应的速度。
- 方便管理。
线程复用、可以控制最大并发数、管理线程。
线程池:三大方法
3大方法的测试如下:
Executors.newSingleThreadExecutor()
方法的测试如下:
// Executors 工具类、3大方法
public class Demo01 {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor(); // 单个线程
//ExecutorService threadPool = Executors.newFixedThreadPool(5); // 创建一个固定的线程池的大小
//ExecutorService threadPool = Executors.newCachedThreadPool(); // 可伸缩的,遇强则强,遇弱则弱
try {
for (int i = 0; i < 10; i++) {
// 使用线程池之后,使用线程池来创建线程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName() + " 创建成功");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
Executors.newFixedThreadPool(5)
方法的测试如下:
// Executors 工具类、3大方法
public class Demo01 {
public static void main(String[] args) {
//ExecutorService threadPool = Executors.newSingleThreadExecutor(); // 单个线程
ExecutorService threadPool = Executors.newFixedThreadPool(5); // 创建一个固定的线程池的大小
//ExecutorService threadPool = Executors.newCachedThreadPool(); // 可伸缩的,遇强则强,遇弱则弱
try {
for (int i = 0; i < 10; i++) {
// 使用线程池之后,使用线程池来创建线程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName() + " 创建成功");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
Executors.newCachedThreadPool()
方法的测试如下:
// Executors 工具类、3大方法
public class Demo01 {
public static void main(String[] args) {
//ExecutorService threadPool = Executors.newSingleThreadExecutor(); // 单个线程
//ExecutorService threadPool = Executors.newFixedThreadPool(5); // 创建一个固定的线程池的大小
ExecutorService threadPool = Executors.newCachedThreadPool(); // 可伸缩的,遇强则强,遇弱则弱
try {
for (int i = 0; i < 10; i++) {
// 使用线程池之后,使用线程池来创建线程
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName() + " 创建成功");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
线程池:七大参数
源码分析:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, //21亿 OOM
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
//本质: ThreadPoolExecutor()
public ThreadPoolExecutor(int corePoolSize, //核心线程池大小
int maximumPoolSize, //最大核心线程池大小
long keepAliveTime, //超时了没有人调用就会释放
TimeUnit unit, //超时单位
BlockingQueue<Runnable> workQueue, //阻塞队列
ThreadFactory threadFactory,//线程工厂:创建线程的,一般不用动
RejectedExecutionHandler handler//拒绝策略) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
手动创建一个线程池,相关代码如下:
//Executors 工具类 3大方法
/**
* 1. new ThreadPoolExecutor.AbortPolicy()); //银行满了 还有人进来,不处理这个人的,并且抛出异常
* 2. new ThreadPoolExecutor.CallerRunsPolicy()); //哪来的去哪里!
* 3. new ThreadPoolExecutor.DiscardPolicy()); //队列满了,丢掉任务,不会抛出异常
* 4. new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了,尝试和最早的竞争,也不会抛出异常
*/
public class Demo02 {
public static void main(String[] args) {
//自定义线程池!工作ThreadPoolExecutor
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了,尝试和最早的竞争,也不会抛出异常
try {
//最大承载:Deque + max
// 超过 RejectedExecutionException
for (int i = 1; i <= 9; i++) {
//使用了线程池之后,使用线程池来创建线程
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
运行结果如下:
四种拒绝策略
4种拒绝策略:
/**
* 1. new ThreadPoolExecutor.AbortPolicy()); //银行满了 还有人进来,不处理这个人的,并且抛出异常
* 2. new ThreadPoolExecutor.CallerRunsPolicy()); //哪来的去哪里!
* 3. new ThreadPoolExecutor.DiscardPolicy()); //队列满了,丢掉任务,不会抛出异常
* 4. new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了,尝试和最早的竞争,也不会抛出异常
*/
小结和扩展
了解:IO密集型、CPU密集型:(调优)
public class Demo03 {
public static void main(String[] args) {
//自定义线程池!工作ThreadPoolExecutor
//最大线程池该如何定义
//1、CPU密集型,几核CPU就定义为几 可以保证cpu的效率最高
//2、IO密集型 判断你程序中十分耗IO线程,
// 程序 15个大型任务 io十分占用资源!
//获取CPU核数
System.out.println(Runtime.getRuntime().availableProcessors());
ExecutorService threadPool = new ThreadPoolExecutor(
2,
Runtime.getRuntime().availableProcessors(),
3,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了,尝试和最早的竞争,也不会抛出异常
try {
//最大承载:Deque + max
// 超过 RejectedExecutionException
for (int i = 1; i <= 9; i++) {
//使用了线程池之后,使用线程池来创建线程
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
创作不易,如果有帮助到你,请给题解点个赞和收藏,让更多的人看到!!!
关注博主不迷路,内容持续更新中。