前言:本文为原创 若有错误欢迎评论!
1.wait、notify、notifyAll
何时使用
在多线程环境下,有时候一个线程的执行,依赖于另外一个线程的某种状态的改变,这个时候,我们就可以使用wait与notify或者notifyAll
wait跟sleep的区别
wait会释放持有的锁,而sleep不会,sleep只是让线程在指定的时间内,不去抢占cpu的资源
notify跟notifyAll的区别
nofity随机唤醒一个等待的线程
notifyAll唤醒所有在该对象上等待的线程
注意点:
2.join
使用场景:
线程A执行到一半,需要一个数据,这个数据需要线程B去执行修改,只有B修改完成之后,A才能继续操作
使用方法:
线程A的run方法里面,调用线程B的“threadB.join()”方法,这个时候,线程A会等待线程B运行完成之后(join的前提时线程已经启动),再接着运行
3.ThreadLocal
ThreadLocal stringThreadLocal=ThreadLocal.withInitial(()->"test");
public void test(){
stringThreadLocal.set(stringThreadLocal.get()+"ttt");
System.out.println(stringThreadLocal.get());
}
ThreadLocal.initialValue(()->[要初始化的值 必须和泛型一致])
ThreadLocal没有被当前线程赋值时 或当前线程刚调用remove()后再调用get(),返回此初始的值
ThreadLocal.get()
获取ThreadLocal中当前线程共享变量的值。
ThreadLocal.set(Object)
设置ThreadLocal中当前线程共享变量的值(和泛型的类型相同)
ThreadLocal.remove
移除ThreadLocal中当前线程共享变量的值
6.Condition
public class Medium{ //线程通信的生产者消费模型
private ReentrantLock lock=new ReentrantLock(false);
private Condition condition1=lock.newCondition();//生产者的
private Condition condition2=lock.newCondition();//消费者的
private int productNum=10;
public void producer(){
lock.lock();
try {
if(productNum<20){
productNum++;
condition1.signalAll();
condition2.signalAll();
Thread.sleep(100);
System.out.println("生产,商品总数"+productNum);
}else {
condition1.await();
condition2.signalAll();
System.out.println("生产满,商品总数"+productNum);
}
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
private void consumer(){
lock.lock();
try {
if(productNum>0){
productNum--;
condition1.signalAll();
condition2.signalAll();
Thread.sleep(100);
System.out.println("消费,商品总数"+productNum);
}else {
condition1.signalAll();
condition2.await();
System.out.println("消费完,商品总数"+productNum);
}
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public static void main(String[] args) {
Medium test = newMedium();
for(int i=0;i<5;i++){
new Thread(()->{
for(;;){
test.consumer();
}
}).start();
}
for(int i=0;i<3;i++){
new Thread(()->{
for(;;){
test.producer();
}
}).start();
}
}
}
await:让线程等待
signal:唤醒其中一个同一condition对应的wait的线程
signalAll:唤醒其中全部同一condition对应的wait的线程
1.基本类型
AtomicBoolean、AtomicInteger、AtomicLong
元老级的原子更新,方法几乎一模一样
DoubleAdder、LongAdder
对Double、Long的原子更新性能进行优化提升
DoubleAccumulator、LongAccumulator
支持自定义运算
LongAccumulator longAccumulator=new LongAccumulator(((left, right) -> left>right?left:right,99);
2.数组类型
AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
int [] arr={1,2,3};
AtomicIntegerArray atomicIntegerArray=new AtomicIntegerArray(arr);
3.更新对象属性
AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicStampedReference、AtomicReferenceFieldUpdater
AtomicReferenceFieldUpdater atomicReferenceFieldUpdater=AtomicReferenceFieldUpdater.newUpdater(User.class,String.class,"name");
4.更新对象引用
AtomicReference 、AtomicMarkableReference(返回boolean类型版本戳)、AtomicStampedReference(返回int类型版本戳)
AtomicReference atomicReference=new AtomicReference<>();
User user1=new User(99,"test1");
User user2=new User(99,"test2");
atomicReference.set(user1);
atomicReference.compareAndSet(user1,user2);//设置的和比较的(第一个)是一个对象才可以实现set
atomicReference.get().getName();
1.同步容器
Vector stringVector = new Vector<>();//本质也是继承了List接口
正确得在迭代中删除(易错)
// 错误删除(在遍历时删除)
// stringVector.forEach(e->{
// if (e.equals("demo3")) {
// stringVector.remove(e);
// }
// });
//正确迭代(用迭代器)
Iterator stringIterator = stringVector.iterator();
while (stringIterator.hasNext()) {
String next = stringIterator.next();
if (next.equals("demo")) {
stringIterator.remove();
}
}
Collections.synchronizedXXX (本质是对相应的容器进行包装 实现线程安全)
List synchronizedList = Collections.synchronizedList(stringVector);
2.并发容器
CopyOnWriteArrayList strings = new CopyOnWriteArrayList<>();
迭代中删除
//错误 并发容器不支持迭代删除
// Iterator iterator = strings.iterator();
// while (iterator.hasNext()) {
// String next = iterator.next();
// if (next.equals("demo2")) {
// iterator.remove();
// }
// }
//正确 用自带得foreach
strings.forEach(e->{
if (e.equals("demo2")) {
strings.remove(e);
}
});
ConcurrentBlockingQueue:基于queue实现的FIFO的队列。队列为空,取操作会被阻塞
ConcurrentLinkedQueue,队列为空,取得时候就直接返回空
LinkedBlockingQueue
在并发编程中LinkedBlockingQueue使用的非常频繁 其可以作为生产者消费者的中间商
LinkedBlockingQueue stringLinkedBlockingQueue = new LinkedBlockingQueue<>();
//往队列里存元素
strings.add("111");
strings.offer("111");
strings.put("111");
//从队列中取元素
String remove = strings.remove();
strings.poll();
strings.take();
并发容器中Quene用法
add:实际上调用的是offer,区别是在队列满的时候,add会报异常
offer:对列如果满了,直接入队失败
put(“111”):在队列满的时候,会进入阻塞的状态
remove():直接调用poll,唯一的区别即使remove会抛出异常,而poll在队列为空的时候直接返回null
poll():队列为空的时候直接返回null
take():在队列为空的时候,会进入等待的状态
1.CountDownLatch
countDownLatch.await(),进入等待的状态
countDownLatch.countDown(),计数器减一
注意:构造方法设置需要完成得个数 然后用await()阻塞住线程 等其他线程就绪用countDown() 使个数减为零时 等待的线程继续执行
CountDownLatch countDownLatch = new CountDownLatch(8);
new Thread(()->{
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("800米比赛结束,准备清空跑道并继续跨栏比赛");
}).start();
for (int i = 0; i < 8; i++) {
int finalI = i;
new Thread(()->{
try {
Thread.sleep(finalI * 1000L);
System.out.println(Thread.currentThread().getName()+"到达终点");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}).start();
}
2.CyclicBarrier–栅栏
cyclicBarrier.await() 进入等待的状态
多个线程一起等待的个数达到构造方法的个数 就都可以往下执行
CyclicBarrier cyclicBarrier = new CyclicBarrier(8);
for (int i = 0; i < 8; i++) {
int finalI = i;
new Thread(() -> {
try {
Thread.sleep(finalI * 1000L);
System.out.println(Thread.currentThread().getName() + "准备就绪");
cyclicBarrier.await();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("开始比赛");
}).start();
}
3.Semaphore–信号量
semaphore.acquire() 相当于获取锁
semaphore.release() 相当于释放锁
相当于在构造函数设置可以获取到锁的线程的数量 然后用acquire获取锁 realse释放锁
Semaphore semaphore = new Semaphore(2);
for (int i = 0; i < 10; i++) {
new Thread(()->{
try {
semaphore.acquire();//获取锁
System.out.println(Thread.currentThread().getName() + "开始执行");
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();//释放锁
}
}).start();
}
4.Exchanger
exchanger.exchange(obj) //交换数据 会把交换的到的值返回
是泛型 用于交换数据
Exchanger stringExchanger = new Exchanger<>();
String str1 = "xdclass";
String str2 = "wiggin";
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "初始值==========>" + str1);
try {
String exchange = stringExchanger.exchange(str1);//交换时先到的线程会等待同一个Exchanger对象的exchange方法调用
System.out.println(Thread.currentThread().getName() + "交換后的数据==========>" + exchange);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "线程1").start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "初始值==========>" + str2);
try {
String exchange = stringExchanger.exchange(str2);
System.out.println(Thread.currentThread().getName() + "交換后的数据==========>" + exchange);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "线程2").start();
Exchanger提供一个同步点,在这个同步点两个线程可以交换彼此的数据。这两个线程通过exchange方法交换数据, 如果第一个线程先执行exchange方法,它会一直等待第二个线程也执行exchange,当同一Exchanger对象两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方,因此该工具类的线程对象是【成对】的。
注意:
并发编程一定要很多时候要注意操作同一: 如同一个锁(不能new了多个锁 然后用不同的锁去lock)、同一个工具类(不同对象的工具类无法达到效果)、同一操作对象(在run方法里调用一个类的方法时 如果不是一个对象的的同一方法 数据不仅不一致 而且各个线程操作的不是同一目标)
1.ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize:核心线程池大小
maximumPoolSize:线程池最大容量
keepAliveTime:当线程数量大于核心时,多余的空闲线程在终止之前等待新任务的最大时间。
unit:时间单位
workQueue:工作队列 nWorks
ThreadFactory:线程工厂
handler:拒绝策略
LinkedBlockingQueue blockingQueue=new LinkedBlockingQueue<>(10); //不设置参数就没有限制任务队列大小 又可能堆积任务(注意 泛型必须时Runnable才是线程的任务)
blockingQueue.put(()->{ //只给任务队列放入一条任务
System.out.println(Thread.activeCount());
});
ThreadPoolExecutor poolExecutor=new ThreadPoolExecutor(10,20,60,TimeUnit.SECONDS,blockingQueue);
poolExecutor.prestartAllCoreThreads();
2.运行机制
按照:核心线程->核心线程+任务队列->核心线程+全部线程->拒绝策略
任务数X
x <= corePoolSize
只启动x个线程
x >= corePoolSize && x < workQueueNums + corePoolSize
会启动 <= corePoolSize 个线程 其他的任务就放到工作队列里
x > corePoolSize + workQueueNums
x <= maximumPoolSize + workQueueNums
会启动x-(workQueueNums )个线程
x > maximumPoolSize + workQueueNums
会启动mSize个线程来执行任务,其余的执行相应的拒绝策略
3.Future与Callable、FutureTask
public class CallableDemo implements java.util.concurrent.Callable {
@Override
public Object call() throws Exception {
System.out.println("callable");
return "success";
}
}
FutureTask stringFutureTask=new FutureTask<>(new CallableDemo());
new Thread(stringFutureTask).start();
System.out.println(stringFutureTask.get());
返回的结果会放在FutureTask中 用get取出返回的结果
ThreadPoolExecutor poolExecutor=new ThreadPoolExecutor(10,20,60,TimeUnit.SECONDS,blockingQueue);
Future future = poolExecutor.submit(new Callable());
System.out.println(future.get());
submit 如果发生异常,不会立即抛出,而是在get的时候,再抛出异常
execute 直接抛出异常
3.Executor框架
ExecutorService executorService = Executors.newCachedThreadPool();
ExecutorService executorService1 = Executors.newFixedThreadPool(2);
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
ExecutorService executorService2 = Executors.newWorkStealingPool();
ExecutorService executorService3 = Executors.newSingleThreadExecutor();
ScheduledExecutorService scheduledExecutorService1 = Executors.newSingleThreadScheduledExecutor();
newCachedThreadPool:创建一个可以根据需要创建新线程的线程池,如果有空闲线程,优先使用空闲的线程
newFixedThreadPool:创建一个固定大小的线程池,在任何时候,最多只有N个线程在处理任务
newScheduledThreadPool:能延迟执行、定时执行的线程池
newWorkStealingPool:工作窃取,使用多个队列来减少竞争
newSingleThreadExecutor:单一线程的线程次,只会使用唯一一个线程来执行任务,即使提交再多的任务,也都是会放到等待队列里进行等待
newSingleThreadScheduledExecutor:单线程能延迟执行、定时执行的线程池
4.线程池拒绝策略
AbortPolicy:该策略直接抛出异常,阻止系统正常工作
CallerRunsPolicy:只要线程池没有关闭,该策略直接在调用者线程中,执行当前被丢弃的任务(叫老板帮你干活)
DiscardPolicy:直接啥事都不干,直接把任务丢弃
DiscardOldestPolicy:丢弃最老的一个请求(任务队列里面的第一个),再尝试提交任务
5.线程池的使用建议
newFixedThreadPool newSingleThreadExecutor
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
newCachedThreadPool newScheduledThreadPool
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM
创建线程池时,核心线程数不要过大
相应的逻辑,发生异常时要处理
即:使用excute不用submit 然后捕获异常处理 不要等反馈了再处理