目录
一、并发同步器-设计同步器的意义
Java锁体系:
二、synchronized使用与原理详解
Java线程生命状态:
2.1、synchronized使用示例:
2.2、synchronized底层原理
2.2.1、对象的内存布局
2.2.2、锁的膨胀升级过程
三、Lock
3.1、ReentrantLock源码
3.2、ReentrantReadWriterlock源码
四、AbstractQueuedSynchronizer(AQS)
juc并发编程包依赖于AQS的内部实现:
AQS框架-管理状态:
4.1、同步等待队列
4.2、条件等待队列
4.3、公平锁
4.4、非公平锁
4.5、重入锁
4.6、不可重入锁
4.7、读写锁
多线程编程中,有可能会出现多个线程同时访问同一个共享、可变资源的情况,这个资源我们称之其为临界资源;这种资源可能是:对象、变量、文件等。
共享:资源可以由多个线程同时访问。
可变:资源可以在其生命周期内被修改。
引出的问题: 由于线程执行的过程是不可控的,所以需要采用同步机制来协同对 对象可变状态的访问。
1、synchronized直接修饰普通方法,会把整个实例锁住。如果是new两个实例,则互不干扰。
/**
* synchronized直接修饰普通方法,会把整个实例锁住。如果是new两个实例,则互不干扰。
*/
public class MyObject {
//synchronized修饰为同步方法,如果先调用method1,则4秒后才会调用method2
private synchronized void method1(){
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果不用synchronized修饰,则可以直接异步调用,没有影响
private void method2(){
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
//创建一个对象,t1走完才会走t2.
MyObject myObject=new MyObject();
Thread t1=new Thread (new Runnable() {
@Override
public void run() {
myObject.method1();
}
},"t1");
Thread t2=new Thread (new Runnable() {
@Override
public void run() {
myObject.method1();
}
},"t2");
t1.start();
t2.start();
//创建两个对象,t3和t1\t2互不干扰。
MyObject myObject02=new MyObject();
Thread t3=new Thread (new Runnable() {
@Override
public void run() {
myObject02.method1();
}
},"t2");
t3.start();
}
}
2、synchronized修饰的代码块传入this也属于对象锁。但能减小锁粒度,只同步代码块。对于对象锁(this),如果是同一个实例,就会锁住、按顺序访问,但是如果是不同实例,就可以同时访问。多例的是锁不住的。
//如下,m1和m2锁的不是一个对象,互不干扰。且只锁代码块。this是当前对象。
class ThisLock2 {
private final Object LOCK = new Object();
public void m2() {
synchronized (LOCK) {
try {
System.out.println(Thread.currentThread());
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void m1() {
synchronized (this) {
try {
System.out.println(Thread.currentThread());
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3、类锁相关
package my.mark.mybaibaoxiang.concurrent.jmmAndVolatile.lock;
/**
* @author twotiger-wxm.
* 验证对象锁和类锁互不干扰,类锁只是一个概念上的东西,并不是真实存在的。
* 就相当于下方示例中,
* testClassLock.duixiangLock();和TestClassLock.classLock();
* 调用的是两个实例。
* 每个类只有一个类锁,多线程调用同一个类锁是能锁住的。
*/
public class TestClassLock {
public static synchronized void classLock(){
int i=0;
while (i++<500){
System.out.println("类锁=="+i+Thread.currentThread().getName());
/*try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
}
public synchronized void duixiangLock(){
int i=0;
while (i++<500){
System.out.println("对象锁=="+i+Thread.currentThread().getName());
/*try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
}
public static void main(String[] args) {
TestClassLock testClassLock = new TestClassLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
testClassLock.duixiangLock();
//TestClassLock.classLock();//TODO 如果两个线程都用类锁,是能锁住的,执行完一个才会执行另一个。
}
},"t-duixiang");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
TestClassLock.classLock();
}
},"t-class");
t1.start();
t2.start();
}
}
package my.mark.mybaibaoxiang.concurrent.jmmAndVolatile.lock;
/**
* @author twotiger-wxm.
* 逃逸分析验证
*/
public class StackAllocTest {
/**
* 进行两种测试
* 关闭逃逸分析,同时调大堆空间,避免堆内GC的发生,如果有GC信息将会被打印出来
* VM运行参数:-Xmx4G -Xms4G -XX:-DoEscapeAnalysis -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
*
* 开启逃逸分析
* VM运行参数:-Xmx4G -Xms4G -XX:+DoEscapeAnalysis -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError
*
* 执行main方法后
* jps 查看进程
* jmap -histo 进程ID
* 关闭逃逸分析的,显示有50万个实例。开启逃逸分析的只有八万多个。即开启逃逸分析很多对象没放堆。
*/
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i < 500000; i++) {
alloc();
}
long end = System.currentTimeMillis();
//查看执行时间
System.out.println("cost-time " + (end - start) + " ms");
try {
Thread.sleep(100000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
private static TulingStudent alloc() {
//Jit(即时编译just-in-time)编译时会对代码进行 逃逸分析
//并不是所有对象存放在堆区,有的一部分存在线程栈空间
TulingStudent student = new TulingStudent();
return student;
}
static class TulingStudent {
private String name;
private int age;
}
}
package my.mark.mybaibaoxiang.concurrent.jmmAndVolatile.lock.aqs;
/*
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
/*
*
*
*
*
*
* Written by Doug Lea with assistance from members of JCP JSR-166
* Expert Group and released to the public domain, as explained at
* http://creativecommons.org/publicdomain/zero/1.0/
*/
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* A reentrant mutual exclusion {@link Lock} with the same basic
* behavior and semantics as the implicit monitor lock accessed using
* {@code synchronized} methods and statements, but with extended
* capabilities.
*
* A {@code ReentrantLock} is owned by the thread last
* successfully locking, but not yet unlocking it. A thread invoking
* {@code lock} will return, successfully acquiring the lock, when
* the lock is not owned by another thread. The method will return
* immediately if the current thread already owns the lock. This can
* be checked using methods {@link #isHeldByCurrentThread}, and {@link
* #getHoldCount}.
*
*
The constructor for this class accepts an optional
* fairness parameter. When set {@code true}, under
* contention, locks favor granting access to the longest-waiting
* thread. Otherwise this lock does not guarantee any particular
* access order. Programs using fair locks accessed by many threads
* may display lower overall throughput (i.e., are slower; often much
* slower) than those using the default setting, but have smaller
* variances in times to obtain locks and guarantee lack of
* starvation. Note however, that fairness of locks does not guarantee
* fairness of thread scheduling. Thus, one of many threads using a
* fair lock may obtain it multiple times in succession while other
* active threads are not progressing and not currently holding the
* lock.
* Also note that the untimed {@link #tryLock()} method does not
* honor the fairness setting. It will succeed if the lock
* is available even if other threads are waiting.
*
*
It is recommended practice to always immediately
* follow a call to {@code lock} with a {@code try} block, most
* typically in a before/after construction such as:
*
*
{@code
* class X {
* private final ReentrantLock lock = new ReentrantLock();
* // ...
*
* public void m() {
* lock.lock(); // block until condition holds
* try {
* // ... method body
* } finally {
* lock.unlock()
* }
* }
* }}
*
* In addition to implementing the {@link Lock} interface, this
* class defines a number of {@code public} and {@code protected}
* methods for inspecting the state of the lock. Some of these
* methods are only useful for instrumentation and monitoring.
*
*
Serialization of this class behaves in the same way as built-in
* locks: a deserialized lock is in the unlocked state, regardless of
* its state when serialized.
*
*
This lock supports a maximum of 2147483647 recursive locks by
* the same thread. Attempts to exceed this limit result in
* {@link Error} throws from locking methods.
*
* @since 1.5
* @author Doug Lea
* 悲观锁
*/
public class ReentrantLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = 7373984872572414699L;
/**
* 内部调用AQS的动作,都基于该成员属性实现
*/
private final Sync sync;
/**
* ReentrantLock锁同步操作的基础类,继承自AQS框架.
* 该类有两个继承类,1、NonfairSync 非公平锁,2、FairSync公平锁
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* 加锁的具体行为由子类实现
*/
abstract void lock();
/**
* 尝试获取非公平锁
*/
final boolean nonfairTryAcquire(int acquires) {
//acquires = 1
final Thread current = Thread.currentThread();
int c = getState();
/**
* 不需要判断同步队列(CLH)中是否有排队等待线程
* 判断state状态是否为0,不为0可以加锁
*/
if (c == 0) {
//unsafe操作,cas修改state状态
if (compareAndSetState(0, acquires)) {
//独占状态锁持有者指向当前线程
setExclusiveOwnerThread(current);
return true;
}
}
/**
* state状态不为0,判断锁持有者是否是当前线程,
* 如果是当前线程持有 则state+1
*/
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//加锁失败
return false;
}
/**
* 释放锁
*/
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
/**
* 判断持有独占锁的线程是否是当前线程
*/
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
//返回条件对象
final ConditionObject newCondition() {
return new ConditionObject();
}
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
/**
* 非公平锁
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* 加锁行为
*/
final void lock() {
/**
* 第一步:直接尝试加锁
* 与公平锁实现的加锁行为一个最大的区别在于,此处不会去判断同步队列(CLH队列)中
* 是否有排队等待加锁的节点,上来直接加锁(判断state是否为0,CAS修改state为1)
* ,并将独占锁持有者 exclusiveOwnerThread 属性指向当前线程
* 如果当前有人占用锁,再尝试去加一次锁
*/
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
//AQS定义的方法,加锁
acquire(1);
}
/**
* 父类AbstractQueuedSynchronizer.acquire()中调用本方法
*/
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
/**
* 公平锁
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* 重写aqs中的方法逻辑
* 尝试加锁,被AQS的acquire()方法调用
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
/**
* 与非公平锁中的区别,需要先判断队列当中是否有等待的节点
* 如果没有则可以尝试CAS获取锁
*/
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
//独占线程指向当前线程
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
/**
* 默认构造函数,创建非公平锁对象
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* 根据要求创建公平锁或非公平锁
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
/**
* 加锁
*/
public void lock() {
sync.lock();
}
/**
* 尝试获去取锁,获取失败被阻塞,线程被中断直接抛出异常
*/
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
/**
* 尝试加锁
*/
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
/**
* 指定等待时间内尝试加锁
*/
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
/**
* 尝试去释放锁
*/
public void unlock() {
sync.release(1);
}
/**
* 返回条件对象
*/
public Condition newCondition() {
return sync.newCondition();
}
/**
* 返回当前线程持有的state状态数量
*/
public int getHoldCount() {
return sync.getHoldCount();
}
/**
* 查询当前线程是否持有锁
*/
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
/**
* 状态表示是否被Thread加锁持有
*/
public boolean isLocked() {
return sync.isLocked();
}
/**
* 是否公平锁?是返回true 否则返回 false
*/
public final boolean isFair() {
return sync instanceof FairSync;
}
/**
* Returns the thread that currently owns this lock, or
* {@code null} if not owned. When this method is called by a
* thread that is not the owner, the return value reflects a
* best-effort approximation of current lock status. For example,
* the owner may be momentarily {@code null} even if there are
* threads trying to acquire the lock but have not yet done so.
* This method is designed to facilitate construction of
* subclasses that provide more extensive lock monitoring
* facilities.
*
* @return the owner, or {@code null} if not owned
*/
protected Thread getOwner() {
return sync.getOwner();
}
/**
* 判断队列当中是否有在等待获取锁的Thread节点
*/
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
/**
* 当前线程是否在同步队列中等待
*/
public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
/**
* Returns an estimate of the number of threads waiting to
* acquire this lock. The value is only an estimate because the number of
* threads may change dynamically while this method traverses
* internal data structures. This method is designed for use in
* monitoring of the system state, not for synchronization
* control.
*
* @return the estimated number of threads waiting for this lock
*/
public final int getQueueLength() {
return sync.getQueueLength();
}
/**
* 返回Thread集合,排队中的所有节点Thread会被返回
*/
protected Collection getQueuedThreads() {
return sync.getQueuedThreads();
}
/**
* 条件队列当中是否有正在等待的节点
*/
public boolean hasWaiters(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
}
/**
* Returns an estimate of the number of threads waiting on the
* given condition associated with this lock. Note that because
* timeouts and interrupts may occur at any time, the estimate
* serves only as an upper bound on the actual number of waiters.
* This method is designed for use in monitoring of the system
* state, not for synchronization control.
*
* @param condition the condition
* @return the estimated number of waiting threads
* @throws IllegalMonitorStateException if this lock is not held
* @throws IllegalArgumentException if the given condition is
* not associated with this lock
* @throws NullPointerException if the condition is null
*/
public int getWaitQueueLength(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
}
/**
* Returns a collection containing those threads that may be
* waiting on the given condition associated with this lock.
* Because the actual set of threads may change dynamically while
* constructing this result, the returned collection is only a
* best-effort estimate. The elements of the returned collection
* are in no particular order. This method is designed to
* facilitate construction of subclasses that provide more
* extensive condition monitoring facilities.
*
* @param condition the condition
* @return the collection of threads
* @throws IllegalMonitorStateException if this lock is not held
* @throws IllegalArgumentException if the given condition is
* not associated with this lock
* @throws NullPointerException if the condition is null
*/
protected Collection getWaitingThreads(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
}
/**
* Returns a string identifying this lock, as well as its lock state.
* The state, in brackets, includes either the String {@code "Unlocked"}
* or the String {@code "Locked by"} followed by the
* {@linkplain Thread#getName name} of the owning thread.
*
* @return a string identifying this lock, as well as its lock state
*/
public String toString() {
Thread o = sync.getOwner();
return super.toString() + ((o == null) ?
"[Unlocked]" :
"[Locked by thread " + o.getName() + "]");
}
}