任务平台网站建设营销的方法和技巧
并发编程
1.共享模型-内存
共享变量在多线程间的<可见性>问题与多条指令执行时的<有序性>问题
1.1Java内存模型
JMM它定义了主存、工作内存抽象概念,底层对应着CPU寄存器、缓存、硬件内存CPU指令优化等.
JMM体现在:
- 原子性-保证指令不会受到线程上下文切换的影响
- 可见性-保证指令不会受cpu缓存的影响
- 有序性-保证指令不会受cpu指令并行优化的影响
1.2可见性
问题:通过变量控制while程序不能够停下来
@Slf4j
public class KeJianXingThreadD2 {static boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {while (flag) {}});thread.start();Thread.sleep(3);log.debug("停止thread");flag = false;}
}
分析问题:
- thread从内存读取了flag的值到工作内存中
- thread线程频繁从主内存中读取flag的值,JIT编译器会将flag的值缓存到自己的工作内存中,减少对主内存中flaga的访问,提高效率
- 3秒之后,main线程修改了flag的值,并同步到主内存中,而thread线程一直在读取自己的工作内存的值,一直是旧值,所以线程不会停下来
解决方案:
volatile(关键字)
可以修饰成员变量和静态成员变量,可以避免线程从自己的工作缓存中查找变量,必须到主内存获取它的值,线程操作volatile变量都是直接操作主
public class KeJianXingThreadD2 {volatile static boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(() -> {while (flag) {}});thread.start();Thread.sleep(3);log.debug("停止thread");flag = false;}
}
只能保证可见性 不能保证原子性
1.3有序性
程序执行的顺序按照代码的先后执顺序执行.
编译器和处理器为了提高执行的效率性能,会对执行进行重排序,指令重排序对单线程应用没有影响,但是在多线程环境下可能出现问题.<重排序的代码没有执行完>
解决:volatile关键字解决
1.4volatile原理
- 读屏障
- 写屏障
2.共享模型-无锁
- CAS与volatile
- 原子整数
- 原子引用
- 原子累加器
- Unsafe
2.1CAS与volatile
/*** @program: springboot-demo-liuan* @description:* @author: Mr.Lh* @create: 2023-03-18 21:29**/
public class CasController {public static void main(String[] args) throws InterruptedException {Acout acout = new Acout(10000);acout.demo(acout);}
}class Acout {public AtomicInteger balance;public Acout(int count) {this.balance = new AtomicInteger(count);}public void with(Integer amout) {while (true) {int i = balance.get();int b = i - amout;if (balance.compareAndSet(i, b)) {break;}}}void demo(Acout acout) throws InterruptedException {for (int i = 0; i < 1000; i++) {new Thread(()->{acout.with(10);}).start();}Thread.sleep(1000);System.out.println(acout.balance.get());}
}
关键点是compareAndSet,它的简称是CAS,它必须是原子性的操作
CAS特点
CAS和volatile可以实现无锁并发,适用于线程数小,多核CPU的场景下
- CAS是基于乐观锁的思想
- synchronized是基于悲观锁的思想
- CAS体现的是无锁并发,无阻塞并发
CAS缺点
- ABA问题:A值变成了B,然后又从B值变回了A,而使用CAS并不会感知到这个情况
- 自旋时间过长:单次CAS并一定能够成功,CAS配合循环来使用
原子整数
- AtomicInteger
- AtomicBoolean
- AtomicLong
原子引用
- AtomicReference(会有ABA的问题)
- AtomicStampedReference(版本号解决ABA的问题)
3.线程池
1.线程池状态
ThreadPoolExecutor 使用 int 的高 3 位来表示线程池状态,低 29 位表示线程数量
状态名 | 高 3 位 | 接收新任务 | 处理阻塞队列任务 | 说明 |
---|---|---|---|---|
RUNNING | 111 | Y | Y | |
SHUTDOWN | 000 | N | Y | 不会接收新任务,但会处理阻塞队列剩余 任务 |
STOP | 001 | N | N | 会中断正在执行的任务,并抛弃阻塞队列 任务 |
TIDYING | 010 | - | - | 任务全执行完毕,活动线程为 0 即将进入 终结 |
TERMINATED | 011 | - | - | 终结状态 |
2.构造方法
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
- corePoolSize 核心线程数
- maximumPoolSize 最大线程数
- keepAliveTime 生存时间
- unit 生存时间单位
- workQueue 当核心线程数满时,存放的任务队列
- threadFactory 创建线程的工厂
- handler 拒绝策略
四种策略
- CallerRunsPolicy 让调用者运行任务
- DiscardOldestPolicy 放弃队列中最早的任务,本任务取而代之
- AbortPolicy 让调用者抛出异常 默认策略
- DiscardPolicy 放弃本次任务
工厂创建线程
1.newFixedThreadPool
特点:
- 核心线程数==最大线程数,无需超时时间
- 阻塞队列是无界的,可以任意数量的任务
2.newCachedThreadPool
特点:
- 核心线程数是0,最大线程数无限制,60s回收救急线程,救济线程可以无限的创建
- 队列采用的SynchronousQueue 没有容量,没有线程来取是放不进去的
- 没有上限的一个线程池
3.newSingleThreadExecutor
特点
- 线程数固定为1,会放入无界队列中,任务执行完毕,这唯一的线程也不会被释放
任务调度线程池
//延时执行任务
public class TimeExcetor {public static void main(String[] args) {ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);scheduledExecutorService.schedule(() -> {System.out.println("aaaa");}, 2, TimeUnit.MILLISECONDS);scheduledExecutorService.schedule(() -> {System.out.println("bbbbbb");}, 1, TimeUnit.MILLISECONDS);}
}//定时执行任务
@Slf4j
public class TimeExcetor {public static void main(String[] args) {ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);log.debug("start...");pool.scheduleAtFixedRate(() -> {log.debug("running...");}, 1, 1, TimeUnit.SECONDS);}
}
正确处理执行任务异常
-
主动捕获异常
public static void main(String[] args) {ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);scheduledExecutorService.execute(() -> {try {int i = 10 / 0;} catch (Exception ex) {ex.printStackTrace();}System.out.println("aaa");}); }
-
Future
public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService executorService = Executors.newFixedThreadPool(2);Future<Boolean> submit = executorService.submit(() -> {int i = 10 / 0;return true;});//如果任务执行成功则获取值,如果任务失败了 则会打印出异常信息System.out.println(submit.get()); }
4.JUC
4.1 AQS原理
AbstractQueuedSynchronizer,是一个用于实现锁和同步器的基础框架
特点:
- state state 表示同步状态(分独占锁和共享锁) 使用compareAndSetState机制实现加锁
- Node表示队列元素,每个 Node 对象内都保存着当前线程的引用,这些 Node 构成了一个双向队列
4.2ReentrantReadWriteLock
ReentrantReadWriteLock 同样基于 AQS 实现了一套高效的同步机制。它维护着两个锁,一个读取锁和一个写入锁。读取锁允许多个线程同时共享读操作,而写入锁则只允许一个线程进行写入操作。如果有一个线程已经获取了写入锁,那么其他所有线程都无法获得读取或写入锁,它们只能等待写入锁被释放后才能进行读取或写入操作。
4.3Semaphore
是一种在并发编程中经常使用的同步原语。它可以用来管理一组共享的资源,防止共享资源的并发使用。Semaphore包含一个计数器和两个方法:acquire和release
@Slf4j
public class SemaphoreDemo1 {public static void main(String[] args) {Semaphore semaphore = new Semaphore(2);for (int i = 0; i < 10; i++) {new Thread(()->{try {semaphore.acquire();log.info("获取信号量");Thread.sleep(10002);} catch (InterruptedException e) {throw new RuntimeException(e);}finally {semaphore.release();}}).start();}}
}
4.4CountDownLatch
是一个同步工具,它可以让一个线程等待一组操作的完成。CountDownLatch包含一个计数器和两个方法:countDown和await
countDown方法将计数器的值减一,await方法阻塞线程,直到计数器值为零。每次调用countDown方法都会减少计数器的值,当计数器值为零时,await方法会解除所有等待线程的阻塞状态
@Slf4j
public class CountDownLatchDemo1 {public static void main(String[] args) throws InterruptedException {CountDownLatch countDownLatch = new CountDownLatch(3);new Thread(()->{try {Thread.sleep(1000);countDownLatch.countDown();} catch (InterruptedException e) {throw new RuntimeException(e);}}).start();new Thread(()->{try {Thread.sleep(2222);} catch (InterruptedException e) {throw new RuntimeException(e);}countDownLatch.countDown();}).start();new Thread(()->{try {Thread.sleep(3333);} catch (InterruptedException e) {throw new RuntimeException(e);}countDownLatch.countDown();}).start();//主线程阻塞countDownLatch.await();}
}
4.5CyclicBarrier
循环栅栏,用来进行线程协作,等待线程满足某个计数。构造时设置『计数个数』,每个线程执 行到某个需要“同步”的时刻调用 await() 方法进行等待,当等待的线程数满足『计数个数』时,继续执行
public class CyclicBarrierDemo {public static void main(String[] args) {CyclicBarrier cb = new CyclicBarrier(2); // 个数为2时才会继续执行new Thread(()->{System.out.println("线程1开始.."+new Date());try {cb.await(); // 当个数不足时,等待} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}System.out.println("线程1继续向下运行..."+new Date());}).start();new Thread(()->{System.out.println("线程2开始.."+new Date());try { Thread.sleep(2000); } catch (InterruptedException e) { }try {cb.await(); // 2 秒后,线程个数够2,继续运行} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}System.out.println("线程2继续向下运行..."+new Date());}).start();}
}