
java并发编程
SuNew_bee
这个作者很懒,什么都没留下…
展开
-
CycilcBarrier
CycilcBarrier的应用场景为多个线程进行不同阶段的任务,当所有线程到达await()后指定的任务才会被执行。CycilcBarrier的结构 CycilcBarrier内部包含几个属性public class CyclicBarrier { /** The lock for guarding barrier entry */ private final ReentrantLock lock = new ReentrantLock(); /** Condit.原创 2021-12-08 13:24:07 · 249 阅读 · 0 评论 -
CountDownLatch
类结构 public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); } 构造函数用传入的count初始化状态数,CountDownLatch内只有一个Sync类型的属性。Sync结构private static final class Syn.原创 2021-12-08 10:54:21 · 190 阅读 · 0 评论 -
Semaphore
Semaphore的结构 构造函数: public Semaphore(int permits) { sync = new NonfairSync(permits); } public Semaphore(int permits, boolean fair) { sync = fair ? new FairSync(permits) : new NonfairSync(permits); } Semaphore内部有一个Sync类.原创 2021-12-07 22:59:43 · 327 阅读 · 0 评论 -
ConcurrentHashMap
ConcurrentHashMap源码剖析原创 2021-12-06 14:25:58 · 421 阅读 · 0 评论 -
StampedLock
为什么要引入StampedLcok 重入锁、读写锁、StampedLock的并发度对比。: StampedLock读和写不互斥,并发度更高。StampedLock用乐观读提高并发度。 基本用法:public class Point { private double x, y; private final StampedLock stampedLock = new StampedLock(); void move(double deltaX, double del.原创 2021-08-01 17:54:42 · 142 阅读 · 0 评论 -
happen-before与volatile、final
重排序与内存可见性问题 重排序有三种:(1)编译器重排序:编译器可以对没有先后依赖关系的语句重新排序。(2)CPU指令重排序:对没有依赖关系的指令重新排序。(3)CPU内存重排序:指令执行顺序和CPU缓存写入主内存的顺序不一致。 内存可见性问题主要是第三种重排序造成的。以下面例子为例:线程1:X = 1 ; a = Y线程2:Y = 1 ; b = X理论上来看,最后可能出现三种情况:1、a = 0,b = 1。2、a = 1,b = 1。3、a = 1,b = 0。但是,a和b都为0也.原创 2021-06-27 20:47:52 · 277 阅读 · 0 评论 -
JVM对synchronized的优化和锁状态
锁偏向 如果一个线程获得了锁,那么锁就进入了偏向模式。当这个线程再次请求锁时,不用再进行同步操作,减少了锁申请的操作。在没有锁竞争或者竞争少的情况下,有较好的性能,而在锁竞争大的情况下,可能每次获取锁的线程都不同,所以效果不好。轻量级锁 进入偏向锁失败后,JVM不会立即挂起线程,会进入轻量级锁优化。将对象头部作为指针,指向持有锁的线程堆栈,判断一个对象是否持有锁 。如果获取轻量级锁失败,当前的锁请求会膨胀为重量级锁。自旋锁 线程获取锁失败后,系统假设在不久后线程能获取到锁,JVM.原创 2021-06-24 18:31:57 · 134 阅读 · 0 评论 -
线程池的4种拒绝策略
线程池在提交任务的时候如果线程池的状态不是运行状态或者线程数达到maxPoolSize,会执行拒绝策略reject(Runnable command)。// 策略1:让调用者执行线程public static class CallerRunsPolicy implements RejectedExecutionHandler { public CallerRunsPolicy() { } public void rejectedExecution(Runnable原创 2021-06-23 23:01:06 · 251 阅读 · 0 评论 -
线程池的执行过程
private final class Worker extends AbstractQueuedSynchronizer implements Runnable{ Worker(Runnable firstTask) { setState(-1); // 初始状态设为-1 this.firstTask = firstTask; this.thread = getThreadFactory().newThread(this)原创 2021-06-23 21:30:33 · 206 阅读 · 0 评论 -
线程池的任务提交
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { // 如果线程数小于corePoolSize则创建新线程 if (addWorker(原创 2021-06-23 20:18:45 · 452 阅读 · 0 评论 -
线程池的结构与关闭原理
线程池实现原理 线程池的原理是:调用方的线程向线程池的队列中提交任务,线程池的线程从队列中取任务进行处理。实现一个线程池需要考虑的问题 1、队列设置多长?如果是无界队列可能会因为队列过长导致内存耗尽,如果是有界队列,队列满了该怎么处理。 2、线程池中的线程数是固定的还是动态变化的? 3、提交任务时,是放入队列还是创建新线程。 4、没有任务时线程是进入睡眠还是阻塞,如果阻塞该怎么唤醒?问题4通常有3种做法:(1)不使用阻塞队列,使用线程安全的队列,没有阻塞–唤醒机制。当队列为空时原创 2021-06-23 19:42:42 · 289 阅读 · 1 评论 -
Condition与Lock
Condition与Lock的方法 Condition与Lock是两个接口,以上是它们内部定义的方法。Lock中有一个newCondition方法,所以Condition都是从Lock中创建出来的。Condition实现原理 以读写锁为例来看原理:public class ReentrantLock implements Lock, java.io.Serializable { ... public Condition newCondition() { return sy原创 2021-06-22 22:51:13 · 971 阅读 · 1 评论 -
ReentrantLock基于AQS的实现
阻塞队列的结构 最开始时,head = tail = NULL,当加入队列时,会先创建一个空节点,然后再往里面插入节点,所以当head == tail时,说明队列为空。lock()的实现 acquire()的实现:tryAcquire()用来再次尝试获取锁,acquierQueued()用来将线程放进阻塞队列。/unlock()。 从读写锁的构造函数可以看到读锁和写锁公用一个Sync实例。 在ReentrantLock中把state变量拆分成两部分,低16位记录写锁,高16位记录读锁。因为一次CAS操作无法同时操作两个int变量,所以把state拆分,用一个变量记录两种锁状态。通过sharedCount(state)和exclusiveCount(state)可以判断是读进程还是写进程持有锁原创 2021-06-02 00:11:57 · 240 阅读 · 1 评论 -
AQS(队列同步器)
AQS称作队列同步器(AbstractQueuedSynchronizer)。实现一把具有阻塞和唤醒功能的锁需要的核心要素 1、需要一个state变量标记锁的状态,该变量起码有0和1两个状态,对state操作需要用CAS确保线程安全。在源码中,ASQ类中定义了一个volatile 的state属性。 2、记录哪个线程持有锁。在源码中,AQS的父类AbstractOwnableSynchronizer中定义了一个transient Thread exclusiveOwnerThread属性来原创 2021-05-28 17:07:54 · 177 阅读 · 0 评论 -
Striped64与LongAdder
LongAdder 在AtmoicLong中,多个线程对变量进行CAS操作的话,每次自旋只有一次线程能修改成功,在并发量高的条件下,效率还是有些不够。为了提高性能,LongAdder将一个变量拆分成多个Cell,每个Cell都有一个Long变量,在并发量大的时候,不同的线程先将变量的计算分摊到多个Cell上,将所有Cell的值加起来就是最终的值。LongAdder只保证了最终一致性 因为在对Cell求和的时候是没有上锁的,所以有可能求和时有别的线程对Cell中的值进行修改,求和得出的结果并不是原创 2021-05-28 12:42:16 · 148 阅读 · 0 评论 -
ABA问题与解决
ABA问题 CAS是基于值做比较的,如果一个线程将一个值从A改为B,又从B改为A,那么CAS会认为该值没有被修改过。也就是说ABA问题使我们无法判断出一个值是否经过修改,在某些业务场景下就会出现问题。解决方法 我们无法判断一个值是否修改过是因为该值前后是一样的,我们可以通过一个版本号来比较。AtomicStampedReference就是带有版本号的CAS。为什么没有AtmoicStampedInteger或AtmoicStampedLong 因为CAS没有办法同时比较两个变量,在带有版原创 2021-05-28 12:23:47 · 970 阅读 · 0 评论 -
信号量Semaphore
信号量可以看做是对锁的扩展,锁限制了一次只有一个线程能获取共享资源,而信号量却可以使多个线程访问同一资源。Semaphore的构造函数有: &emsppublic Semaphore(int permits); &emsppublic Semaphore(int permits, boolean fair); &emsppermits指定最多能有多少个线程访问信号量,fair是指定是否公平。信号量的逻辑方法:public void acquire(); 获取资源,可响原创 2021-05-24 22:18:33 · 90 阅读 · 0 评论 -
ReentrantLock重入锁
重入锁的概念 ReentrantLock的意思为重入锁,重入锁完全可以替代synchronized。 ReentrantLock与synchronized相比,它有显示的上锁释放锁的操作过程,所以比Synchronized灵活很多。上锁、释放锁的方式:public ReentrantLock lock = new ReentrantLock();lock.lock();//上锁lock.unlock();//释放锁 为什么叫重入锁呢,因为对于同一个线程来说,只要获取到重入锁那么就可以重原创 2021-05-24 21:37:23 · 342 阅读 · 0 评论 -
CAS操作
CAS操作 锁的一个很大缺点是,当线程没有获取到锁时会被阻塞挂起,而线程调度会带来较大的开销。CAS是CompareAndSwap,是JDK提供的非阻塞的原子性操作,通过硬件保证操作的原子性。以compareAndSwapLong为例:boolean compareAndSwapLong(Object obj, long valueOffset, long expect, long update){ ... ...} obj是对象的内存位置,valueOffset是对象的变量的偏移原创 2021-02-23 17:53:24 · 242 阅读 · 0 评论 -
线程安全、内存可见性问题与sychronized、volatile关键字
线程安全问题 线程安全是线程同步问题。图中是一个非常典型的读写不同步的问题,线程A的写操作没有被B及时看见,所以B的读写数据均为异常的。 线程安全问题最常见的解决方案就是给共享变量加synchronized锁。内存可见性问题 java内存模型如图所示。当线程工作时,将主线程中的变量复制到自己的工作内存中,线程读写实际是操作自己工作内存中的变量。实际工作时线程的工作内存: 图片展示的是一个双核CPU架构,每个核都有自己的一级缓存,有点有二级缓存。 内存可见性问题的出现:1、当原创 2021-02-23 17:21:34 · 197 阅读 · 0 评论 -
ThreadLocal和InheritableThreadLoacl
ThreadLocal 创建一个ThreadLocal变量后,每个线程会复制一个变量到自己的本地内存。访问这个变量的每个线程实际上是访问自己变量的一个副本。用法 创建一个对象:ThreadLocal< T > localVariable = new ThreadLocal<>(); 在线程内部使用:localVariable.set()设置值,localVariable.get()获取值。ThreadLocal原理 Thread类中有一个threadLocals原创 2021-02-23 15:08:14 · 156 阅读 · 0 评论 -
守护线程与用户线程
java线程分为守护线程和用户线程。当最后一个用户线程结束时,JVM会正常退出,守护线程结束与否不影响JVM的退出。 设置守护线程:调用线程的setDaemon(true);方法将线程设置为守护线程。...原创 2021-02-23 14:41:36 · 98 阅读 · 0 评论 -
线程中断
void interrupt()方法 调用某个线程的interrupt()方法是设置该线程的中断标志位true,仅是设置中断标志,并不是使线程中断。若线程被阻塞挂起时调用该方法,则会抛出InterruptedException异常。boolean isInterrupted()方法 检测当前线程是否被中断(中断标志),并返回结果boolean interrupted()方法 检测当前线程是否被中断(中断标志),并返回结果。如果当前线程被中断则清除中断标志。该方法是静态方法,能直接通过Thre原创 2021-02-23 14:37:31 · 133 阅读 · 0 评论 -
线程通知与等待
wait方法 一个线程调用一个共享变量的wait()方法时,该线程会被阻塞挂起。调用wait的前提是该线程有获取共享变量的监视器锁。 获取监视器锁:(1)synchronize(共享变量){…}(2)在在共享变量的方法前加synchronize关键字,调用该方法。若线程没有获取监视器锁而调用了wait()则会抛出IllegalMonitorStateException异常。停止线程阻塞并返回:(1)其他线程调用共享对象的notify和notifyAll方法(2)其他线程调用该线程的i原创 2021-02-23 14:13:32 · 148 阅读 · 0 评论