thread
1.自定义线程与守护线程的区别
- 自定义线程在主线程停止后不会停止,守护线程会通过 thread.setDaemon(true)可以将自定义线程转化为守护线程(在没有用户线程全都是守护线程时jvm停止运行)
Lock接口:
synchronized关键字(自动上锁,与解锁):同步锁可以修饰代码块,方法(被修饰的方法被称为同步方法)、静态方法,类
多线程编程步骤:
1.创建资源类,在资源类中创建属性和方法
2.创建多个线程,调用资源类中的操作方法
3.lock接口,要手动实现上锁与解锁:{
可重入锁:(ReentrantLock)
}
lock锁实现提供比使用同步方法和语句可以获得的更广泛的锁操作,他们允许更加灵活的结构,可能具有非常不同的属性并且可能支持多个关联的条件对象,lock提供了比synchronized更多的功能
区别:
1.lock不是java语言内置的synchronized是java语言关键字是内置的,lock是一个类,通过这个类实现同步方法,lock.lock后面需要将执行代码放入try…catch中,使得不管什么状态都要释放锁,防止发生死锁现象,
2.lock可以让等待的锁的响应线程中断,而synchronized不行,使用synchronized时,等待的线程会一直等待下去,不能响应中断
3.通过lock可以知道有没有成功获取锁,在synchronized无法办到
4.lock可以提高多个线程进行读写操作的效率
线程间通信
多线程编程步骤:
1.创建资源类,在资源类创建属性和操作方法
2.在资源类操作方法:(判断,干活,通知)
3.创建多个线程,调用资源类的操作方法
虚假唤醒:由于wait等待是在哪里睡在哪里醒然后执行下面的代码,在if语句中执行等待线程,唤醒后不再执行if判断【解决:将等待写入while循环中】
线程间定制化通信:
概念:线程按照约定进行输出
方法:添加线程标志位,在第一个线程执行完成后修改标志位为2,让第二个线程执行
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author Dongguo
* @date 2021/9/3 0003-15:01
* @description:
*/
public class ThreadOrderAccessDemo {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
shareResource.printA(i);
}
}, "A").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
shareResource.printB(i);
}
}, "B").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
shareResource.printC(i);
}
}, "C").start();
}
}
class ShareResource {
private int flag = 0;
private Lock lock = new ReentrantLock();
private Condition conditionA = lock.newCondition();
private Condition conditionB = lock.newCondition();
private Condition conditionC = lock.newCondition();
public void printA(int i) {
try {
lock.lock();
while (flag != 0) {
conditionA.await();
}
System.out.println(Thread.currentThread().getName() + "输出,第" + i + "轮开始");
//打印两次
for (int j = 0; j < 2; j++) {
System.out.println("A");
}
//唤醒B
flag = 1;
conditionB.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(int i) {
try {
lock.lock();
while (flag != 1) {
conditionB.await();
}
System.out.println(Thread.currentThread().getName() + "输出,第" + i + "轮开始");
//打印三次
for (int j = 0; j < 3; j++) {
System.out.println("B");
}
//唤醒C
flag = 2;
conditionC.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(int i) {
try {
lock.lock();
while (flag != 2) {
conditionC.await();
}
System.out.println(Thread.currentThread().getName() + "输出,第" + i + "轮开始");
//打印五次
for (int j = 0; j < 5; j++) {
System.out.println("C");
}
//唤醒A
flag = 0;
conditionA.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
集合的线程安全:
集合线程不安全问题:ArrayList集合,在add()方法中没有关键字确保线程安全
解决方案:vector(该类使用synchronizes确保线程安全)
Collections:(该工具类含有SynchronizedList(list list)方法返回,确保线程安全)
CopyOnWriterArrayList:(读时支持并发读取,写时复制技术)
hashset,hashmap集合安全问题解决方案:
copyOnWriteArraySet解决
concurrentHashMap解决
多线程锁Synchronized锁的八种问题:
synchronized实现同步的基础:java中的每一个对象都可以作为锁:
- 对于普通方法,锁是当前实例对象
- 对于劲心态同步方法锁是当前类的class对象
- 对于同步方法块,锁是Synchonized括号里面配置的对象
公平锁与非公平锁:
new ReentrantLock(true)
非公平锁:可能造成线程饿死(但执行效率高)
公平锁:执行平均分配(效率低)
可重入锁:
synchronized[隐式]与lock【显式】都是可重入锁
概念:可实现自由进入的锁(通过同一把锁可以进入内部的各个区域,也叫递归锁)
死锁:两个或者两个以上进程在执行过程中,因为争夺至于那而造成一种相互等待的现象。如果没有外力的干涉,将无法执行下去;
产生原因:1.系统资源不足;2.进程运行推进顺序不合适;3.资源分配不当
死锁代码:
public class DeadLockDemo implements Runnable{
public static int flag = 1;
//static 变量是 类对象共享的
static Object o1 = new Object();
static Object o2 = new Object();
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":此时 flag = " + flag);
if(flag == 1){
synchronized (o1){
try {
System.out.println("我是" + Thread.currentThread().getName() + "锁住 o1");
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + "醒来->准备获取 o2");
}catch (Exception e){
e.printStackTrace();
}
synchronized (o2){
System.out.println(Thread.currentThread().getName() + "拿到 o2");//第24行
}
}
}
if(flag == 0){
synchronized (o2){
try {
System.out.println("我是" + Thread.currentThread().getName() + "锁住 o2");
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + "醒来->准备获取 o2");
}catch (Exception e){
e.printStackTrace();
}
synchronized (o1){
System.out.println(Thread.currentThread().getName() + "拿到 o1");//第38行
}
}
}
}
public static void main(String args[]){
DeadLockDemo t1 = new DeadLockDemo();
DeadLockDemo t2 = new DeadLockDemo();
t1.flag = 1;
new Thread(t1).start();
//让main线程休眠1秒钟,保证t2开启锁住o2.进入死锁
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.flag = 0;
new Thread(t2).start();
}
}
Callable接口:为了实现runable需要实现不返回任何内容的run()方法,而对于callable需要实现在完成时返回结果的call()方法,call()方法可以引发异常而run()不能,为实现必须重写call方法
由于thread中只能实现runable接口,callable无法实现,FutureTask是runable接口的一个实现类其构造方法可以传递callable接口
class thread1 implements Callable{
@Override
public Object call() throws Exception {
return 10;
}
}
FutureTask futureTask = new FutureTask<>(new Thread1());
futureTask原理 :未来任务:单开线程给某个任务,先汇总其他任务,最后进行一起汇总(考试时先做会的,在做不会的,最后交卷)
辅助类:
减少计数:CountDownLatch(CountDownLatch a=new CountDownLatch(6)设置初始值为6,调用a.countDown()方法,是计数器每次减1,执行a.wait()方法是计数器完成后再进行下面操作)
循环栅栏:CyclicBarrier(集齐七颗龙珠可以召唤神龙)
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
public static class Soldier implements Runnable{
private String soldierName;
private final CyclicBarrier cyclic;
Soldier(CyclicBarrier cyclic,String soldierName){
this.cyclic = cyclic;
this.soldierName = soldierName;
}
@Override
public void run() {
try{
//等待其他士兵到齐
cyclic.await();
doWork();
//等待所有士兵完成工作
cyclic.await();
}catch (InterruptedException e){
e.printStackTrace();
}catch (BrokenBarrierException e){
e.printStackTrace();
}
}
void doWork(){
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(soldierName+" :任务完成");
}
}
public static class BarrierRun implements Runnable{
boolean flag;
int N;
public BarrierRun(boolean flag,int N){
this.flag = flag;
this.N = N;
}
@Override
public void run() {
if(flag){
System.out.println("司令:[士兵"+N+"个,任务完成!]");
}else{
System.out.println("司令:[士兵"+N+"个,集合完毕!]");
flag = true;
}
}
}
public static void main(String[] args) {
final int N = 10;
Thread[] allSoldier = new Thread[N];
boolean flag = false;
CyclicBarrier cyclic = new CyclicBarrier(N,new BarrierRun(flag,N));
//设置屏障点,主要是为了执行这个方法
System.out.println("集合队伍!");
for(int i = 0;i < N;i++){
System.out.println("士兵"+i+" 报道!");
allSoldier[i] =new Thread(new Soldier(cyclic,"士兵"+i));
allSoldier[i].start();
}
}
}
信号灯:Semaphore()
读写锁:
悲观锁:能解决并发中的各种问题,不支持并发操作,效率低
乐观锁:通过版本号控制
表锁:
行锁:(会发生死锁)
读锁:共享锁
写锁:独占锁(读可以一起读,写只能一个人写)
volatile(关键字定义一个不断发生变化的属性)
rw ReadWriteLock() 创建读写锁对象 rw.writeLock().lock 在写数据之前添加写锁 rw.readlock().lock()在读数据之前添加读锁
读写锁缺点:造成锁饥饿,一直读,没有写的操作,读的时候不能进行写的操作
读写锁降级:将写入锁降级为读锁(获取写锁、获取读锁、释放写锁、释放读锁),读锁不能升级为写锁(学霸写作业学渣抄)
阻塞队列:略
线程池:
概念:一种线程的使用模式,线程过多会带来调度的开销,进而影响缓存局部性和整体性能,而线程池维护着多个线程,等待着监督管理者分配可并发的任务,避免者处理短时间任务是创建与销毁线程的代价线程池不仅能够保证内核的充分利用,还能防止过分调用
优势:控制运行的线程数量,处理过程中将任务放入队列,在创建后启动这些任务没如果线程数量超过了最大数量,超出的线程排队等待等其他线程执行完毕,再从队列中取出任务来执行
降低资源消耗,提高响应速度,提高线程的可管理性。
java中的线程池是通过Executor框架实现的,该框架中用到了mxecutor,executors,executorService,threadPoolExecutor这几个类
Executors:线程池java创建工具类
Executors.newFixedThreadPool(int nThreads); //创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。
executor=Executors.newCachedThreadPool();//创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程。
executor.execute(()->{sout("hello")});//执行线程创建
参数:
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
(1)corePoolSize:线程池中的常驻核心线程数。
(2)maximumPoolSize:线程池能够容纳同时执行的最大线程数,此值大于等于1。
(3)keepAliveTime:多余的空闲线程存活时间,当空间时间达到keepAliveTime值时,多余的线程会被销毁直到只剩下corePoolSize个线程为止。
(4)unit:keepAliveTime的单位。
(5)workQueue:任务队列,被提交但尚未被执行的任务。
(6)threadFactory:表示生成线程池中工作线程的线程工厂,用户创建新线程,一般用默认即可。
(7)handler:拒绝策略,表示当线程队列满了并且工作线程大于等于线程池的最大显示数(maxnumPoolSize)时如何来拒绝请求执行的runnable的策略。
系统默认的拒绝策略有以下几种:
- AbortPolicy:为线程池默认的拒绝策略,该策略直接抛异常处理。
- DiscardPolicy:直接抛弃不处理。
- DiscardOldestPolicy:丢弃队列中最老的任务。
- CallerRunsPolicy:将任务分配给当前执行execute方法线程来处理。
理解:线程中有常驻线程当业务在一定值类为处理时进行等待,如果大过一定值则开启新的线程,达到最大线程后,则执行拒绝策略(银行办理业务,如果等的人少则继续等待,如果等的人多则添加业务人员,超出业务人员数量则拒绝服务)
自定义线程池:
[](image/55DF3E4D5C91EF85EFCEBB7FA07ACB26.jpg)//自定义线程池
public class ThreadPoolExecutorDemo {
public static void main(String[] args) {
//得到自己电脑cpu是几核的 若CPU密集型 一般maximumPoolSize默认 为自己电脑cpu为几核 +1 或者 +2 若为IO密集型的话就是 CPU核数/1-阻塞系数(0.8~0.9)
System.out.println(Runtime.getRuntime().availableProcessors());
ExecutorService threadPool = new ThreadPoolExecutor(
2,
9,
2L,
TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
Executors.defaultThreadFactory(),
//拒绝访问策略
/**
* new ThreadPoolExecutor.AbortPolicy() :AbortPolicy(默认)
* 直接抛出RejectedExecutionException异常阻止系统正常运行
* DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加人队列中
* 尝试再次提交当前任务。
* DiscardPolicy:该策略默默地丢弃无法处理的任务,不予任何处理也不抛出异常。
* 如果允许任务丢失,这是最好的一种策略。
*/
//CallerRunsPolicy:“调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。
new ThreadPoolExecutor.CallerRunsPolicy()
);
try{
for (int i = 1; i <=10 ; i++) {
threadPool.execute(
new Thread(()->{
System.out.println(Thread.currentThread().getName()+"开始执行工作");
})
);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭线程池
threadPool.shutdown();
}
}
}
Fork/Join分支合并框架:
心得不够:略。。
异步回调:
CompletableFuture future = CompletableFuture.allOf(futureOne, futureTwo);
// 阻塞等待所有任务完成
System.out.println(future.get());
List<CompletableFuture<FileUploadDO>> futureList= request.stream().map(dto -> CompletableFuture.
supplyAsync(() -> doUploadFile(dto.getFileId(),dto.getMathml()), executorService ))
.collect(Collectors.toList());
CompletableFuture<Void> completableFuture=CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0]));
// 像thread,阻塞等待线程完成
completableFuture.join();