JUC

线程的状态(Thread.State)

  • NEW:新生
  • RUNNABLE:运行
  • BLOCKED:阻塞
  • WAITING:等待
  • TIMED_WAITING:限时等待
  • TERMINATED:终止

wait/sleep区别

  • 来自不同的类:wait:Object,sleep:Thread
  • 是否释放锁:wait:期间会释放锁,sleep:期间会持有锁而不释放
  • 使用的范围不同:wait:必须在同步代码块中,sleep:任何地方
  • 是否需要捕获异常:wait不需要捕获,sleep必需要捕获

Synchronized 和 Lock 区别

  1. Synchronized 是内置的关键字,Lock是一个类
  2. Synchronized 无法判断锁的状态,Lock可以判断是否获取到了锁
  3. Synchronized 会自动释放锁,Lock必须手动释放,如果不释放,容易死锁
  4. Synchronized 没有超时时间,会一直锁住,Lock可以设置超时时间
  5. Synchronized 可重入锁,不能中断,非公平,Lock 可重入锁,可以判断锁,默认非公平(可以自己设置)
  6. 适合锁少量的代码同步问题,Lock适合锁大量的同步代码

常用辅助类

  • CountDownLatch:倒计时门栓,减法计数器,等待归零后才会放行
    • CountDownLatch(int count):构造方法,count参数初始化计数器值
    • countDownLatch.countDown():计数器减一
    • countDownLatch.await():主线程阻塞,等待计数器归零后继续执行
  • CyclicBarrier:循环屏障,加法计数器,调用await()后,线程等待,当调用此方法的线程数量达到预期值,所有被此方法阻塞的线程继续执行。CyclicBarrier的计数器能够重置,屏障可以重复使用
    • CyclicBarrier(int parties):构造方法,parties参数初始化屏障拦截的线程数量
    • await():使线程阻塞,等待屏障拦截的线程数量到达parties指定的数量
  • Semaphore:信号量,维持一组许可证,获得许可证的执行,其他的等待,(例如:停车场抢车位问题,有空闲车位才允许进入,若车位已满,则其余的车等待直到有空车位),作用:多个共享资源互斥的使用,并发限流,控制最大线程数
    • semaphore.acquire():获得,如果已经满了,等待,直到有许可证被释放为止
    • semaphore.release():释放持有的许可证,然后唤醒等待的线程

ReentrantReadWriteLock:读写锁

适用于读次数多于写次数的场景,读锁为共享锁,写锁为互斥锁
读锁重入时不支持升级,即当前线程如果持有读锁,则在重入获得锁时不能在获得写锁
写锁重入支持降级,即当前线程如果持有写锁,则在重入时可以获得读锁

ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
//读锁
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
readLock.lock();
readLock.unlock();
//写锁
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
writeLock.lock();
writeLock.unlock();

BlockingQueue:阻塞队列接口

1、ArrayBlockingQueue:基于数组
2、LinkedBlockingQueue:基于链表
3、SynchronousQueue:同步队列,容量固定为1,对后续放入的元素阻塞直到前一个被取出
4、DelayQueue:延迟队列

方式 抛出异常 有返回值,不抛出异常 阻塞 等待 超时等待
添加 add offer put offer
移除 remove poll take poll
检测队首元素 element peek - -

线程池

  • 3大方法:不推荐使用这种方式创建线程池,无法精确控制
Executors.newSingleThreadExecutor()	//只有一个线程的线程池,但允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,导致OOM
Executors.newFixThreadPool(int)	//创建一个固定大小的线程池,但允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,导致OOM
Executors.newCachedThreadPool()	//创建一个可伸缩的线程池,自动动态调整大小,允许创建的线程数量为Integer.MAX_VALUE,可能会创建大量的线程,导致OOM
  • 7大参数:推荐使用自定义的方式创建线程池
new ThreadPoolExecutor(
	int corePoolSize	//核心线程池大小
	int maxiMumPoolSize	//最大线程池大小
	long KeepAliveTime:	//核心线程外的其他线程若长时间闲置,则释放线程
	TimeUnit unit:	//线程闲置时间单位
	BlockingQueue<Runnable> workQueue	//阻塞队列
	ThreadFactory threadFactory	//线程工厂:使用默认Excutors.defaultThreadFactory(),一般不用动
	RejectedExecutionHandler handler	//拒绝策略:在线程满负荷运行,且阻塞队列也满时,对新进任务的处理方法
)
  • 4种拒绝策略
new ThreadPoolExecutor.AbortPolicy()	//拒绝,抛出异常
new ThreadPoolExecutor.DiscardPolicy()	//拒绝,且不抛出异常
new ThreadPoolExecutor.CallerRunsPolicy()	//将任务驳回,交给原线程处理
new ThreadPoolExecutor.DiscardOldestPolicy()	//尝试和池中最老的线程竞争,竞争成功则运行,失败则丢弃任务(不会抛出异常)

ForkJoin框架

分而治之,将大任务分成若干小任务,然后统一执行结果

CompletableFuture

异步回调

JMM:Java内存模型

1、保证可见性:volatile,java关键字,被volatile修饰的共享变量,多线程下对其值的修改,能立刻被其他线程感知,volatile可以避免指令重排
2、保证原子性:Atomic,使用java.util.concurrent.atomic包下的类,对其操作可以保证原子性,即不会被打断,在表现上相当于加了同步锁,其实际上使用了UnSafe对象,调用操作系统本地方法,根据地址直接操作内存,非常高效
3、禁止指令重排序

1、公平锁和非公平锁

  • 公平锁:所有线程排队,下来后到执行,不能插队
  • 非公平锁:不遵循先来后到,可以插队,推荐使用(因为公平锁可能导致一个耗时极短的线程被一个耗时很长的线程长时间阻塞,比如4秒和4分钟),默认是非公平锁

2、可重入锁
第一种情况:Synchronized
对于都被Synchronized修饰的两个方法A和B,如果A内部调用了B,则进入A方法的线程可以直接获取到B方法的锁,不需要再通过竞争取得

public class Demo{
	public static void main(String[] args){
		Demo demo = new Demo();
		demo.A();
	}

	public Synchronized void A(){
		System.out.println("AAAA");
		B();	//直接获取B的锁,执行
	}
	
	public Synchronized void B(){
		System.out.println("BBB");
	}
}

第二种情况:Lock
Lock锁的加锁和解锁必须成对出现:两次调用lock(),也必须有两次调用unLock()

public class Demo{
	Lock lock = new ReentrantLock();
	public static void main(String[] args){
		Demo demo = new Demo();
		demo.A();
	}

	public void A(){
		lock.lock();
		try{
			System.out.println("AAAAAA");
			B();	//调用B()方法
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			lock.unlock();
		}
	}
	
	public void B(){
		lock.lock();	//当执行A()的线程执行到此处时,直接获取锁,不会被阻塞
		try{
			System.out.println("BBBBB");
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			lock.unlock();
		}
	}
}

3、自旋锁

public clas SpinlockDemo{
	AtomicRefence<Thread> atomicReference = new AtomicReference<>();
	
	//加锁
	public void lock(){
		Thread thread = Thread.currentThread();
		//如果该原子引用的预期值不为null,则一直循环
		while(! atomicReference.compareAndSet(null, thread)){
		
		}
		//获取锁后,执行业务代码
	}
	
	//解锁
	public void unLock(){
		Thread thread = Thread.currentThread();
		//解锁,将原子引用的值置为null
		atomicReference.compareAndSet(thread, null);
	}
}

ABA问题

当执行campare and swap会出现失败的情况。例如,一个线程先读取共享内存数据值A,随后因某种原因,线程暂时挂起,同时另一个线程临时将共享内存数据值先改为B,随后又改回为A。随后挂起线程恢复,并通过CAS比较,最终比较结果将会无变化。这样会通过检查,这就是ABA问题。 在CAS比较前会读取原始数据,随后进行原子CAS操作。这个间隙之间由于并发操作,最终可能会带来问题

posted @ 2023-05-29 14:56  谭五月  阅读(93)  评论(0)    收藏  举报