JUC并发编程(二)
文章目录
七. Callable
1.可以有返回值,可以抛出异常,方法不同
例子:
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 Runable()).start();
//new Thread(new FutureTask<V>()).start();
//new Thread(new FutureTask<>(Callable)).start();
new Thread().start(); //如何启动Callable
MyThread thread = new MyThread();
FutureTask futureTask = new FutureTask(thread); //适配类
new Thread(futureTask,"A").start();
Integer o = (Integer)futureTask.get(); //返回callable的返回结果
//get可能会产生阻塞,把它放到最后,或者使用异步通信
System.out.println(o);
}
}
class MyThread implements Callable<Integer>{
@Override
public Integer call(){
System.out.println("call");
return 1024;
}
}
八. 常用辅助类(必会)
8.1.CountDownLatch 减法计数器
import java.util.concurrent.CountDownLatch;
//减法计数器
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
//总数是六的倒计时
CountDownLatch countDownLatch = new CountDownLatch(5);
for (int i = 1; i <= 5; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" Go Out");
countDownLatch.countDown();//数量减一
},String.valueOf(i)).start();
}
countDownLatch.await();//等待计数器归零 ,然后再向下执行
System.out.println("close door");
}
}
原理:
countDownLatch.countDown();//数量减一
countDownLatch.await();//等待计数器归零
每次有线程调用 countDown() ,计数器数量将减一,计数器变为零则countDownLatch.await();就会被唤醒继续执行
8.2 CyclicBarrier 加法计数器
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("end");
});
for (int i = 1; i <= 7; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"++");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
8.3 Semaphore 信号量
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo {
public static void main(String[] args) {
//线程数量 实例:停车位 有限的情况下拥有了秩序 如限流
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6; i++) {
new Thread(()->{
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();//释放
}
},String.valueOf(i)).start();
}
}
}
原理:
semaphore.acquire();//获取 假设如果已经满了,就等待资源被释放为止
semaphore.release();//释放 将当前的信号量释放+1,然后唤醒等待的线程
作用:多个共享资源互斥使用,并发限流,控制最大线程数
九. 读写锁 ReadWriteLock
无锁实例:
import java.util.HashMap;
import java.util.Map;
/**
* ReadWriteLock
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
MyCacheLock myCacheLock = 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();
}
}
}
//自定义缓存,一 :set 二:put
//无锁
class MyCache{
private volatile Map<String,Object> map = new HashMap<>();
//存 写
public void put(String key,Object value){
System.out.println(Thread.currentThread().getName()+"写入.");
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()+"读取完毕.");
}
}
结果:
1写入.
4写入.
3写入.
3写入完毕.
2写入.
1写入完毕.
5写入.
4写入完毕.
5写入完毕.
2写入完毕.
3读取3
3读取完毕.
1读取1
2读取2
2读取完毕.
4读取4
1读取完毕.
4读取完毕.
5读取5
5读取完毕.
加锁实例:
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* ReadWriteLock
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCacheLock myCacheLock = new MyCacheLock();
//加锁,写入操作
for (int i = 1; i <= 5; i++) {
final int temp = i;
new Thread(()->{
myCacheLock.put(temp+"",temp+"");
},String.valueOf(i)).start();
}
//加锁,读操作
for (int i = 1; i <= 5; i++) {
final int temp = i;
new Thread(()->{
myCacheLock.get(temp+"");
},String.valueOf(i)).start();
}
}
}
//自定义缓存,一 :set 二:put
//加锁
class MyCacheLock{
private volatile Map<String,Object> map = new HashMap<>();
//读写锁,更加细腻的操作
private ReadWriteLock readWriteLock= new ReentrantReadWriteLock();
//存 写入的时候,只希望同时只有一个线程写入
public void put(String key,Object value){
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"写入.");
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()+"读取");
Object o = map.get(key);
System.out.println(Thread.currentThread().getName()+"读取完毕.");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
结果:
1写入.
1写入完毕.
2写入.
2写入完毕.
4写入.
4写入完毕.
3写入.
3写入完毕.
5写入.
5写入完毕.
5读取
1读取
4读取
1读取完毕.
3读取
3读取完毕.
2读取
2读取完毕.
5读取完毕.
4读取完毕.
讲解:
一:ReadWriteLock分为三种情况
读-读 可以共存
读-写 不能共存
写-写 不能共存
二:独占锁(写锁) 一次只能被一个线程占有
共享锁(读锁) 多个线程可以同时占有
十. 阻塞队列
阻塞
队列
分类:
BlockingDeque 阻塞队列
AbstractQueue 非阻塞队列
Deque 双端队列
10.1 BlockingQueue(阻塞队列)
什么情况下需要用到阻塞队列:多线程线程处理,线程池
使用队列:添加,删除 四组API
1.抛出异常
2.不会抛出异常
3.阻塞等待
4.超时等待
方式 | 抛出异常 | 有返回值,不抛异常 | 阻塞 等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer | put | offer(带参) |
移除 | remove | poll | take | poll(带参) |
检测队首元素 | element | peek | 无 | 无 |
抛出异常
//抛出异常
import java.util.concurrent.ArrayBlockingQueue;
public class Test {
public static void main(String[] args) {
//Collection
test1();
}
public static void test1(){
//队列的大小为3
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
//添加第四个数据,会抛出异常Queue full
//System.out.println(blockingQueue.add("d"));
//判断队首
System.out.println(blockingQueue.element());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
//移除空数据 抛出异常.NoSuchElementException
//System.out.println(blockingQueue.remove());
}
}
---------------结果---------------
true
true
true
a
a
b
c
有返回值,不抛异常
//有返回值,不抛异常
import java.util.concurrent.ArrayBlockingQueue;
public class Test {
public static void main(String[] args) {
//Collection
test2();
}
public static void test2(){
//队列大小3
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
//不会抛出异常,返回false
System.out.println(blockingQueue.offer("d"));
//判断队首
System.out.println(blockingQueue.peek());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
//不抛出异常,返回null
System.out.println(blockingQueue.poll());
}
}
---------------结果---------------
true
true
true
false
a
a
b
c
null
阻塞 等待
//阻塞 等待(一直等待)
import java.util.concurrent.ArrayBlockingQueue;
public class Test {
public static void main(String[] args) throws InterruptedException {
//Collection
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());
}
}
超时等待
//阻塞 等待(超时退出)
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) throws InterruptedException {
//Collection
test4();
}
public static void test4() throws InterruptedException {
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", 2, TimeUnit.SECONDS));//两秒后退出
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
//没有元素,阻塞等待 (超时退出)
System.out.println(blockingQueue.poll(2,TimeUnit.SECONDS));//两秒后退出
}
}
10.2 synchronizedQueue(同步队列)
没有容量
进去一个元素,必须取出来之后,才能再往里放入一个元素
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
public class synchronizedQueueDemo {
public static void main(String[] args) {
//同步队列
SynchronousQueue synchronousQueue= new SynchronousQueue<>();
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+" put 1 ");
synchronousQueue.put("1");
System.out.println(Thread.currentThread().getName()+" put 2 ");
synchronousQueue.put("2");
System.out.println(Thread.currentThread().getName()+" put 3 ");
synchronousQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"T1").start();
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + " take " + synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + " take " + synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + " take " + synchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
},"T2").start();
}
}
---------------结果---------------
T1 put 1
T2 take 1
T1 put 2
T2 take 2
T1 put 3
T2 take 3
十一. 线程池(重点)
池化技术
程序的运行,本质:占用系统的资源!优化资源的使用!=>池化技术
线程池,连接池,内存池,对象池,常量池…
线程池的好处:
1.降低资源的消
2.提高响应速度
3.方便管理
线程复用,控制最大并发数,管理线程
必会的一些东西:三大方法,七大参数,四种拒绝策略
11.1三大方法
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Demo01 {
public static void main(String[] args) {
//Executors 工具类,三大方法
ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个
//ExecutorService threadPool = Executors.newFixedThreadPool(5); //创建一个固定的线程池的大小
//ExecutorService threadPool = Executors.newCachedThreadPool(); //可变的
try {
for (int i = 0; i < 7; i++) {
//不用new thread,而是从线程池取
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"--");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完要关闭
threadPool.shutdown();
}
}
}
---------------依次执行三种方法结果---------------
---------------newSingleThreadExecutor()---------------
pool-1-thread-1--
pool-1-thread-1--
pool-1-thread-1--
pool-1-thread-1--
pool-1-thread-1--
pool-1-thread-1--
pool-1-thread-1--
---------------Executors.newFixedThreadPool(5)---------------
pool-1-thread-1--
pool-1-thread-4--
pool-1-thread-1--
pool-1-thread-3--
pool-1-thread-2--
pool-1-thread-4--
pool-1-thread-5--
---------------Executors.newCachedThreadPool()---------------
pool-1-thread-2--
pool-1-thread-5--
pool-1-thread-4--
pool-1-thread-3--
pool-1-thread-1--
pool-1-thread-6--
pool-1-thread-7--
11.2 七大参数
源码分析
三个不同的方法,调用的都是ThreadPoolExecutor
在ThreadPoolExecutor中可以看到,这七个参数
int corePoolSize 核心线程大小
int maximumPoolSize 最大核心线程池大小
long keepAliveTime 空闲线程存活时间
TimeUnit unit 超时单位
BlockingQueue workQueue, 阻塞队列
ThreadFactory threadFactory, 线程工厂,创建线程的
RejectedExecutionHandler handler 拒绝策略
所以在上面不允许使用Executor来创建线程,避免资源耗尽
接下来我们手动创建一个线程池,来
11.4四种拒绝策略
这四条就是我们说的四种拒绝策略
//第一种拒绝策略
import java.util.concurrent.*;
public class Demo02 {
public static void main(String[] args) {
//自定义线程池
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
3, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
//第一种拒绝策略
new ThreadPoolExecutor.AbortPolicy());//如果超出8个线程,不处理,线程就会报错,抛出异常
try {
//最大承载 = Deque + max
//超出最大承载就会抛出:RejectedExecutionException异常,被拒绝策略异常
for (int i = 0; i < 9; i++) {
//不用new thread,而是从线程池取
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"--");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完要关闭
threadPool.shutdown();
}
}
}
---------------结果---------------
pool-1-thread-2--
pool-1-thread-4--
pool-1-thread-2--
pool-1-thread-3--
pool-1-thread-1--
pool-1-thread-2--
pool-1-thread-4--
pool-1-thread-5--
java.util.concurrent.RejectedExecutionException:
//第二种拒绝策略
import java.util.concurrent.*;
public class Demo02 {
public static void main(String[] args) {
//自定义线程池
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
3, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
//第二种拒绝策略
new ThreadPoolExecutor.CallerRunsPolicy()); //那里来的去那里
try {
//最大承载 = Deque + max
//超出最大承载就会抛出:RejectedExecutionException异常,被拒绝策略异常
for (int i = 0; i < 9; i++) {
//不用new thread,而是从线程池取
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"--");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完要关闭
threadPool.shutdown();
}
}
}
---------------结果---------------
pool-1-thread-1--
pool-1-thread-5--
pool-1-thread-3--
pool-1-thread-2--
pool-1-thread-3--
pool-1-thread-4--
main--
pool-1-thread-5--
pool-1-thread-1--
//第三种拒绝策略
import java.util.concurrent.*;
public class Demo02 {
public static void main(String[] args) {
//自定义线程池
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
3, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
//第三种拒绝策略
new ThreadPoolExecutor.DiscardPolicy()); //队列满了,丢掉任务,不会抛出异常
try {
//最大承载 = Deque + max
//超出最大承载就会抛出:RejectedExecutionException异常,被拒绝策略异常
for (int i = 0; i < 9; i++) {
//不用new thread,而是从线程池取
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"--");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完要关闭
threadPool.shutdown();
}
}
}
---------------结果---------------
pool-1-thread-2--
pool-1-thread-4--
pool-1-thread-3--
pool-1-thread-1--
pool-1-thread-3--
pool-1-thread-4--
pool-1-thread-5--
pool-1-thread-2--
抛弃队列里最老的那个,代替他的位置进入队列里
//第四种拒绝策略
import java.util.concurrent.*;
public class Demo02 {
public static void main(String[] args) {
//自定义线程池
ExecutorService threadPool = new ThreadPoolExecutor(
2,
5,
3, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
//第四种拒绝策略
new ThreadPoolExecutor.DiscardOldestPolicy()); //试探最早的线程是否结束,如果结束就跟进,没结束不处理,不会抛出异常
try {
//最大承载 = Deque + max
//超出最大承载就会抛出:RejectedExecutionException异常,被拒绝策略异常
for (int i = 0; i < 9; i++) {
//不用new thread,而是从线程池取
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"--");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完要关闭
threadPool.shutdown();
}
}
}
---------------结果---------------
pool-1-thread-1--
pool-1-thread-4--
pool-1-thread-3--
pool-1-thread-2--
pool-1-thread-3--
pool-1-thread-4--
pool-1-thread-5--
pool-1-thread-1--
小结与扩展
最大线程到底该如何定义:(用于调优)
1.CPU密集型,几核,就是几,可以保持CPU的效率最高
获取cpu核心数:
Runtime.getRuntime().availableProcessors();
2.IO密集型
判断,你的程序中,十分耗用资源的IO线程有多少个
大约创建两倍IO程序的线程数
import java.util.concurrent.*;
public class Demo02 {
public static void main(String[] args) {
//自定义线程池
ExecutorService threadPool = new ThreadPoolExecutor(
2,
Runtime.getRuntime().availableProcessors(),//获取核心数
3, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
//第一种拒绝策略
//new ThreadPoolExecutor.AbortPolicy());//如果超出8个线程,不处理,线程就会报错,抛出异常
//第二种拒绝策略
//new ThreadPoolExecutor.CallerRunsPolicy()
//第三种拒绝策略
new ThreadPoolExecutor.DiscardPolicy()); //队列满了,不会抛出异常
//第四种拒绝策略
//new ThreadPoolExecutor.DiscardOldestPolicy()); //试探最早的线程是否结束,如果结束就跟进,没结束不处理,不会抛出异常
try {
//最大承载 = Deque + max
//超出最大承载就会抛出:RejectedExecutionException异常,被拒绝策略异常
for (int i = 0; i < 9; i++) {
//不用new thread,而是从线程池取
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"--");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//线程池用完要关闭
threadPool.shutdown();
}
}
}