【AQS】独占锁的释放
上一次写独占锁的获取,写到当前线程被挂起之后就结束了,那么什么时候才能唤醒这个线程呢?这次来分析独占锁的释放。JAVA中的内置锁在线程退出临界区时会自动释放锁,而ReentrantLock之类的显示锁,需要手动释放,一定要写到finally代码块中!!因为锁的释放没有公平与非公平之说,所以直接写在了AQS中:
12345678910111213141516171819202122/** * Releases in exclusive mode. Implemented by unblocking one or * more threads if {@link #tryRelease} returns true. * This method can be used to implement method {@link Lock#unlock}. * * @param arg the release argument. This value is conveyed to * {@link #tryR ...
【AQS】独占锁的获取
AQS中独占锁的获取由acquire(int arg)方法实现:
123456789101112131415161718/** * Acquires in exclusive mode, ignoring interrupts. Implemented * by invoking at least once {@link #tryAcquire}, * returning on success. Otherwise the thread is queued, possibly * repeatedly blocking and unblocking, invoking {@link * #tryAcquire} until success. This method can be used * to implement method {@link Lock#lock}. * 描述了独占的方式获取锁的流程,忽略获取锁的过程中的中断, * * @param arg the ...
【AQS】独占锁与共享锁
引言AQS的源码中涉及到的锁有独占锁与共享锁,独占锁就是只有一个线程可以获取锁,共享锁就是同时可以有多个线程获取锁。代码层体现下AQS的state属性,为0,则锁没有被线程所持有,独占锁时为1,共享锁时大于0,代表获取此锁的现成的数量。AQS的内部类Node定义了两个常量SHARED和EXCLUSIVE,他们分别标识AQS队列中等待线程的锁的获取模式。
1234/** * The synchronization state. */private volatile int state;
JUC中ReentrantLock与CyclicBarrier为独占锁,CountDownLatch与Semaphore为共享锁,ReentrantReadWriteLock中writeLock为独占锁,ReadLock为共享锁。
独占锁与共享锁的区别:
独占功能当锁被头节点获取后,只有头节点获取锁,其余节点的线程继续沉睡,等待锁被释放后,才会唤醒下一个节点的线程。
共享功能只要头节点获取锁成功,就在唤醒自身节点对应的线程的同时,继续唤醒AQS队列中的下一个节点的线程,每个节点在唤醒自身的同时还会唤醒 ...
【AQS】核心实现
引言AQS(AbstractQueuedSynchronizer)是Java并发工具的基础,采用乐观锁,通过CAS与自旋轻量级的获取锁。下面简单分析一下他的核心实现:###核心属性锁相关属性:
12345678910/** * The synchronization state. * 同步状态,0代表锁没有被占用,大于0代表锁被占用且表示锁被重入的次数 */ private volatile int state;/***存储当前获取锁的线程,继承自AbstractOwnableSynchronizer*/private transient Thread exclusiveOwnerThread;
同步队列相关属性:
123456789/** * 对首,只是一个标志位,不存储线程 */private transient volatile Node head;/** * 队尾,新增加的等待线程入队节点 */private transient volatile Node tail;
Node节点中重要属性:
123456789101112131415161718192 ...
【JMM】Thread源码分析之线程中断
引言线程中断是一个比较重要的概念,可以说操作系统是由中断驱动的。中断的概念:是指在程序执行过程中,遇到急需处理的事件时,暂时终止现行程序在CPU上的运行,转而执行相应的事件处理程序,待处理完成后,再返回断点或调度其他程序执行。JAVA中的线程中断,是不会像操作系统一样处理中断的,因为JAVA中的每个线程,只是保留了中断标志位,要根据标志位来处理中断。
Thread源码中,关于中断的代码:
设置中断线程设置中断其实就是设置线程中断标识,没有做到暂时终止先行程序在CPU上的运行。
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556/** * Interrupts this thread. * 中断当前线程,其实就是将当前线程设置中断标志位,JAVA设置中断唯一的口子 * * <p> Unless the current thread is interrupting itself, which ...
【JMM】Thread源码分析之线程状态及常用方法
线程的状态转换通过源码分析,线程的状态大部分都是用虚拟机设置的,只有在线程新建而未启动的时候,线程的状态值为0,处于State.NEW状态。
NEW新建状态,线程对象新建时的状态。
RUNNABLE就绪状态,调用了线程的start()方法后,随时等待CPU调度。
BLOCKED阻塞状态,等待synchronized 锁。
WAITING当调用了Object.wait,Thread.join,LockSupport.park方法后(没有超时设定),挂起当前线程。
TIMED_WAITING调用了Thread.sleep,Object.wait,Thread.join,LockSupport.parkUntil,LockSupport.parkNanos有超时设定的方法,挂起当前线程一段时间。
TERMINATED线程执行结束。
常用方法currentThread()获取当前线程的引用,静态方法。
1234567/** * Returns a reference to the currently executing thread object. * 返回对当前正在执行的线程对象 ...
【JMM】内存模型之伪共享(False Sharing)
引言在对称多处理器(SMP)系统中,每个处理器均有一个本地高速缓存。内存系统必须保证高速缓存的一致性。当不同处理器上的线程修改驻留在同一高速缓存行中的变量时就会发生假共享,结果导致高速缓存行无效,并强制执行更新,进而影响系统性能。如图:
线程0和线程1会用到不同变量,它们在内存中彼此相邻,并驻留在同一高速缓存行。高速缓存行被加载到CPU0和CPU1的高速缓存中(灰色箭头)。尽管这些线程修改的是不同变量(红色和蓝色箭头),高速缓存行仍会无效,并强制内存更新以维持高速缓存的一致性。
缓存系统中是以缓存行(cacheline)为单位存储的。缓存行是2的整数幂个连续字节,一般为32-256个字节。最常见的缓存行大小是64个字节。一个Java的long类型是8字节,因此在一个缓存行中可以存8个long类型的变量。所以,如果你访问一个long数组,当数组中的一个值被加载到缓存中,它会额外加载另外7个,这会带来一些优势。但是也有伪共享问题,比如两个线程,修改long数组的第一个与第七个,会频发发生缓存失效,影响性能。解决办法就是填充,在JDK8中提供了@sun.misc.Contended注解来避免 ...
【JMM】Thread源码分析之创建和启动线程
引言可以通过继承Thread类,覆写run方法方式或者继承自Runnable接口创建一个线程。Thread类也实现了Runnable接口:此接口由注解@FunctionalInterface修饰,也就是可以使用lambda表达式,其中的run方法,如果直接调用,那就是普通的方法,如果通过现成的start()方法调用,就会创建一个新的线程调用这个run()方法,即使用Java多线程特性。
123456789101112131415@FunctionalInterfacepublic interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that sepa ...
【JMM】线程之状态(Windows+Linux+Java)
Windows线程状态Windows系统中,线程可以初于上图中7中状态之一:
初始态线程在创建过程中处于初始态,创建完成后,此线程被放入就绪队列。
就绪态线程已经获得除CPU以外所有资源,处于等待调度执行的状态。内核的调度程序维护所有就绪线程队列,并按照优先级次序进行调度。
准备态已选中在特定处理器上运行的下一个线程,此线程处于准备态并等待线程描述表切换。如果准备态的线程优先级足够高,可以从运行线程那里抢占处理器,否则将等待直至运行线程进入等待态或时间片用完。系统中的每个处理器上只能有一个处于准备态的线程。
运行态内核执行线程切换,准备态线程进入运行态并开始执行,直至它被抢占、或用完时间片、或阻塞、或终止时为止。在前两种情况下,线程就转换为就绪态。
等待态线程进入等待态的原因有:出现阻塞事件;等待同步信号;环境子系统要求线程挂起。等待事件条件满足且所有资源可用时,线程就转换为就绪态。
过渡态线程完成等待后准备运行,但此时资源不可用,线程就转换为过渡态。例如,线程的内核堆栈被调出主存,当资源可用时(内核堆栈被调入主存),过渡态线程进入就绪态。
终止态线程可以被自己或其他线程终止,此时线 ...
【JMM】线程之简介与分类
简介:操作系统中引入进程是为了使多个程序并发执行,改善资源利用率,进程是操作系统中进行除处理器外的资源分配和保护的基本单位。线程是进程中能够并发执行的实体,共享进程的主存空间和资源,是处理器调度和分配的基本单位。
线程组成成分:
线程的唯一标识符及线程状态信息。
未运行时所保存的线程上下文,可以把线程看成进程中的一个独立的程序计数器。
核心栈,在核心态工作时保存参数,在函数调用时的返回地址等。
用于存放线程局部变量和用户栈的私有存储区。
线程分类
内核级线程线程的管理工作由内核完成,优点是:速度快,系统执行效率高。缺点,用户态和内核态模式切换开销大。
用户级线程线程的管理由应用程序管理,优点,线程切换无需使用内核特权,可以使用特定的调度算法。缺点:用户级线程阻塞会引起整个进程的阻塞。
混合式线程Solaris即支持用户级也支持内核级线程。
JAVA中的线程分类
守护线程为用户线程服务的线程:如垃圾回收,内存管理等线程。新建的线程最初都是用户级线程,可以通过setDaemon()方法设置成守护线程。
用户线程一般用户使用的线程,通过继承Thread类后者实现Runnable接口等实现 ...