synchronized:jvm实现,重量级锁
- 对于普通同步方法,锁对象为当前实例对象
- 对于静态同步方法,所对象为当前类对象
- 对于同步代码块,所对象为括号内配置的对象
jvm基于进入和退出实现同步方法和同步代码块
- 同步代码块的实现方式为monitorenter和monitorexit实现:每个对象都有一个monitor与之关联,monitorenter指令在编译后被插入到同步代码块的开始位置,monitorexit指令被插入到代码块的结束位置和异常处,当线程执行到monitorenter指令时,会获得当前指定对象的monitor,即获得锁
锁的四种状态:按照级别由低到高
-
无锁
-
偏向锁
对象头中存储线程的ID,下次指定线程进入时就不用再进行获得锁和释放锁,当有其他线程竞争当前锁时,会触发锁撤销,其他线程才可以获得锁
-
轻量级锁
线程获得锁时,会在线程的栈帧创建存放锁记录的空间,复制对象头的Mark Word信息到锁记录空间,并将对象的Mark Word指向当前线程的锁记录空间地址,如果其他线程竞争当前锁,,其他线程会自旋方式获得锁,并且当线程释放锁时其他有线程竞争锁时,该所会升级为重量级锁
-
重量级锁
当其他线程需要获取该锁时,该所已被其他线程持有,会导致当前线程阻塞,其他线程释放锁时会通知其他要获得该锁的线程,让他们进行竞争
原子操作
-
处理器层面实现
-
java实现:
JDK1.5之后的并发包提供一系列的原子操作类,AtomicInteger.AtomicBoolean等,调用compareAndSet可以在设置数据时与原数据进行比较,相同则进行设置,但是出现的问题就是ABA问题,当一个值被从A修改为B,B再被修改为A,下一次compareANdSet会认为当前值没有改变过,有一种狸猫换太子的感觉,所以为了解决ABA问题,可以添加版本号,每次修改,都将版本号+1,当compareAndSet时对比版本号和原始值是否一致,一致再进行替换,JDK就提供了AtomicStampedReference类,调用该类compareAndSet需要设置4个参数(预期的引用值,更新后的引用值,预期的标记,更新后的标记)
重排序
- 编译器优化重排序:编译器级别
- 指令集并行重排序:处理器级别,如果不存在依赖性,可以将机器指令并行执行
- 内存系统重排序:处理器级别
如果不希望在处理器编译器优化,可以通过编译器插入内存屏障,达到禁止特定类型的指令重排
线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
10, // 核心线程数
20, // 最大线程数= 核心线程数+工作线程数
30, // 工作线程保活时间
TimeUnit.MINUTES, // 保活时间单位
new ArrayBlockingQueue<>(10), // 任务队列:当核心线程和工作线程都在处理任务时,其他任务放到该队列
new MyThreadFactory(), // 创建线程的工场:实现ThreadFactory接口进行实现
new ThreadPoolExecutor.DiscardPolicy() // 饱和策略
);
class MyThreadFactory implements ThreadFactory{
@Override
public Thread newThread(Runnable r) {
return new Thread(r,"线程-"+r.toString());
}
}
// 线程池创建构造函数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
- corePoolSize:核心线程数,不受保活时间影响
- maximumPoolSize:最大线程数=核心线程数+工作线程数,当核心线程数和最大线程数设置一样,表示工作线程数为0,即保活时间参数没用
- keepAliveTime:工作线程的保活时间
- unit:保活时间的单位
- workQueue:任务队列,当工作线程和核心线程都在处理任务时,还有新任务,就会添加到这个队列
- ArrayBlockingQueue:基于数组实现的先进先出队列
- LinkedBlockingQueue:基于链表的先进先出队列
- SynchronousQueue:不存储任务的队列,当任务要添加时,必须等到一个线程调用了移除操作,否则添加操作一致阻塞
- PriorityBlockingQueue:一个具有优先级的无限阻塞队列
- threadFactory:线程创建工场
- handler:饱和策略,当队列和线程都满了的时候,再添加任务如何进行处理
- AbortPolicy:直接抛出异常
- CallRunsPolicy:使用调用者线程来进行处理线程
- DiscardOldestPolicy:丢弃最旧的任务,也就是队列最前面的任务
- DiscardPolicy:直接丢弃过来的任务
几种JDK实现的线程池
-
FixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
-
核心线程数和最大线程数都设置为传入的值,即线程池只有核心线程
-
线程无过期时间
-
使用链表阻塞队列
-
-
SingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
- 核心线程数和最大线程数都为1
- 无过期时间
- 使用链表阻塞队列
-
CachedThreadPool
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
- 核心线程数为0,对大线程数无限
- 过期时间60秒,即如果线程空闲60s就自动销毁
- 不存储任务的队列,每次添加任务就是用之前的线程,要是之前的线程都不空闲,就创建新的线程
-
ScheduledThreadPool
public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue()); }
- 核心线程数为传入参数,最大线程数无限
- 当工作线程执行完任务后便进行销毁
- 使用延时队列
网友评论