Java并发
基础问题
线程和进程的含义及区别
- 线程
- 进程
线程的状态和变迁
如何创建一个线程?
继承Thread类,实现Runable接口,Callable和Future
start() 和 run() 区别
如下图所示,start()方法只能调用一次,重复调用会抛出IllegalThreadStateException
,run()方法可以重复调用
为什么调用start()会执行run()方法
见下面JDK Thread start()中的注释;总结就是,start()方法会让JVM调用该Thread的run()方法,并在新线程上执行,即主线程调用start()过后返回并继续执行,新的线程将会被创建并用来执行run()方法
start()方法中call了一个native的start0()
native方法是指该方法的实现由非Java语言实现,如C或C++,不提供实现体,是个原生态方法
Java语言本身不能对操作系统底层进行访问和操作,但可以通过JNI(Java Native Interface)接口调用其他语言来实现对底层的访问,JNI是JDK的一部分
为什么不能直接调用run()方法
不会创建新的线程,该run()方法就是个普通的方法将在主线程执行,一般来说直接调用run()是一个bug或者失误
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();
/**
* If this thread was constructed using a separate
* <code>Runnable</code> run object, then that
* <code>Runnable</code> object's <code>run</code> method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of <code>Thread</code> should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
yield()方法有什么作用?
yield()方法只是让线程从RUNNING状态变为READY状态,但是也可能变为READY后又立刻被CPU调度器执行(变为RUNNING);如下图JDK注释说的,yield()只是告诉cpu调度器,我可以让出我的时间片,但是cpu调度器可以忽视这个消息;一般来说这个方法没有很多应该使用的场景,多是用于测试或者debug。
sleep()和yield()方法有什么区别?
sleep()让线程从RUNABLE状态变为TIMED_WAITING状态,让出CPU的时间片,不考虑线程优先级,yield()只会让优先级高的运行;sleep的线程不会丧失任何monitor的所有权
为什么sleep()和yield()方法是静态的?
sleep()和yield()都是谁调谁sleep/yield也都是自愿的,如果是实例方法会造成很多混乱,即我可以让你这个线程sleep
/**
* A hint to the scheduler that the current thread is willing to yield
* its current use of a processor. The scheduler is free to ignore this
* hint.
*
* <p> Yield is a heuristic attempt to improve relative progression
* between threads that would otherwise over-utilise a CPU. Its use
* should be combined with detailed profiling and benchmarking to
* ensure that it actually has the desired effect.
*
* <p> It is rarely appropriate to use this method. It may be useful
* for debugging or testing purposes, where it may help to reproduce
* bugs due to race conditions. It may also be useful when designing
* concurrency control constructs such as the ones in the
* {@link java.util.concurrent.locks} package.
*/
public static native void yield();
/**
* Causes the currently executing thread to sleep (temporarily cease
* execution) for the specified number of milliseconds, subject to
* the precision and accuracy of system timers and schedulers. The thread
* does not lose ownership of any monitors.
*
* @param millis
* the length of time to sleep in milliseconds
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public static native void sleep(long millis) throws InterruptedException;
sleep()和wait()方法有什么区别?
wait()是Object的实例方法,有几个重点:
-
当前线程必须持有该object的锁(monitor)
-
wait()方法使得当前线程阻塞,并释放该object的锁(monitor)
-
当前线程将等待其他线程调用该object的notify()或者notifyAll()的方法重新获得锁并继续执行下去
-
响应中断
-
wait()方法必须用在循环里面
为什么wait()方法必须用在循环里面,即用while(condition not hold)而不是if(condition not hold):
当有多个线程由于相同condition not hold在wait()这行代码处阻塞等待持锁的时候,一旦condition change某个线程拿到锁了从wait()往下执行,又改变了condition,释放锁,由于另外个阻塞的线程是if判断,不再判断condition,拿到锁再处理时会发生错误。假设condition是array size > 0 而wait()之后会进行remove,那么就会出现IndexOutOfArray的问题
显而易见的相同点为:都使得当前线程阻塞了(TIMED_WAITING/WAITING状态)且都响应中断;不同点为:一个是Thread的静态方法,一个是Object的实例方法;wait()需要当前线程持有该object的monitor,sleep是当前线程自愿的行为;wait()调用后当前线程会释放monitor,sleep不会释放任何monitor;sleep自动唤醒,wait需要其他线程调用该object的notify和notifyAll()方法
/**
* Causes the current thread to wait until another thread invokes the
* {@link java.lang.Object#notify()} method or the
* {@link java.lang.Object#notifyAll()} method for this object.
* In other words, this method behaves exactly as if it simply
* performs the call {@code wait(0)}.
* <p>
* The current thread must own this object's monitor. The thread
* releases ownership of this monitor and waits until another thread
* notifies threads waiting on this object's monitor to wake up
* either through a call to the {@code notify} method or the
* {@code notifyAll} method. The thread then waits until it can
* re-obtain ownership of the monitor and resumes execution.
* <p>
* As in the one argument version, interrupts and spurious wakeups are
* possible, and this method should always be used in a loop:
* <pre>
* synchronized (obj) {
* while (<condition does not hold>)
* obj.wait();
* ... // Perform action appropriate to condition
* }
* </pre>
* This method should only be called by a thread that is the owner
* of this object's monitor. See the {@code notify} method for a
* description of the ways in which a thread can become the owner of
* a monitor.
*
* @throws IllegalMonitorStateException if the current thread is not
* the owner of the object's monitor.
* @throws InterruptedException if any thread interrupted the
* current thread before or while the current thread
* was waiting for a notification. The <i>interrupted
* status</i> of the current thread is cleared when
* this exception is thrown.
* @see java.lang.Object#notify()
* @see java.lang.Object#notifyAll()
*/
public final void wait() throws InterruptedException {
wait(0);
}
join()方法怎么使用?
- join()方法不是一个静态方法,是Thread类的实例方法
- 当前线程会等待t.join()的t线程执行完成后才继续执行
- 响应中断
/**
* Waits for this thread to die.
*
* <p> An invocation of this method behaves in exactly the same
* way as the invocation
*
* <blockquote>
* {@linkplain #join(long) join}{@code (0)}
* </blockquote>
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* <i>interrupted status</i> of the current thread is
* cleared when this exception is thrown.
*/
public final void join() throws InterruptedException {
join(0);
}
守护线程和用户线程有什么区别?
- 主线程结束后用户线程还会继续运行,JVM存活
- 如果没有用户线程,都是守护线程,那么JVM结束(所有的线程都会结束)
守护线程是一种特殊的线程,在后台默默地完成一些系统性的服务,比如垃圾回收线程、JIT线程都是守护线程。与之对应的是用户线程,用户线程可以理解为是系统的工作线程,它会完成这个程序需要完成的业务操作。如果用户线程全部结束了,意味着程序需要完成的业务操作已经结束了,系统可以退出了。所以当系统只剩下守护进程的时候,java虚拟机会自动退出
什么是线程安全,有哪些线程安全程度?
操作系统可以保证每个进程只能访问分配给自己的地址空间。而每个进程中都有一块内存空间(堆内存)是公共的,是所有线程都能访问的,就像小区的公园一样。操作系统也会为每个线程分配自己的内存空间(栈内存),只有自己这个线程能访问。
线程的安全其实是内存的安全。
如何保证线程安全?
- 放到栈内存,例如局部变量。但是也就只有自己能访问了,当变量从(方法内的)局部变量变为(类的)成员变量的时候,也就是从栈内存到公共的堆内存,就可能会出现问题
- 人人有份。ThreadLocal,每个Thread都有自己的一个map存储变量。这些变量虽然整体是放到堆内存的,但是由于自己拷贝一份到自己的map,自己处理自己的,就像是本地的一样,也就安全了。
- 只能看不能摸。例如,常量或者只读变量。
- 制定规则,先入为主。例如可重入锁。
- 地广人稀,CAS。
什么是中断
另外一个问题,我们应该
如何安全地结束一个线程?
- 使用退出标志,用一个while()循环,再设置一个boolean标志
- 使用中断
thread.interrupt()
将设置该线程中断位为true,包含两种,一种本质就是1,用while循环加上isInterrupted()来判断;一种通过catch InterruptedException来处理
interrupt()方法只是改变中断状态,不会中断一个正在运行的线程。需要用户自己去监视线程的状态为并做处理。支持线程中断的方法(也就是线程中断后会抛出interruptedException的方法)就是在监视线程的中断状态,一旦线程的中断状态被置为“中断状态”,就会抛出中断异常。这一方法实际完成的是,给受阻塞的线程发出一个中断信号,这样受阻线程检查到中断标识,就得以退出阻塞的状态。
更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,此时调用该线程的interrupt()方法,那么该线程将抛出一个 InterruptedException中断异常(该线程必须事先预备好处理此异常),从而提早地终结被阻塞状态。如果线程没有被阻塞,这时调用 interrupt()将不起作用,直到执行到wait(),sleep(),join()时,才马上会抛出 InterruptedException
/**
* Interrupts this thread.
*
* <p> Unless the current thread is interrupting itself, which is
* always permitted, the {@link #checkAccess() checkAccess} method
* of this thread is invoked, which may cause a {@link
* SecurityException} to be thrown.
*
* <p> If this thread is blocked in an invocation of the {@link
* Object#wait() wait()}, {@link Object#wait(long) wait(long)}, or {@link
* Object#wait(long, int) wait(long, int)} methods of the {@link Object}
* class, or of the {@link #join()}, {@link #join(long)}, {@link
* #join(long, int)}, {@link #sleep(long)}, or {@link #sleep(long, int)},
* methods of this class, then its interrupt status will be cleared and it
* will receive an {@link InterruptedException}.
*
* <p> If this thread is blocked in an I/O operation upon an {@link
* java.nio.channels.InterruptibleChannel InterruptibleChannel}
* then the channel will be closed, the thread's interrupt
* status will be set, and the thread will receive a {@link
* java.nio.channels.ClosedByInterruptException}.
*
* <p> If this thread is blocked in a {@link java.nio.channels.Selector}
* then the thread's interrupt status will be set and it will return
* immediately from the selection operation, possibly with a non-zero
* value, just as if the selector's {@link
* java.nio.channels.Selector#wakeup wakeup} method were invoked.
*
* <p> If none of the previous conditions hold then this thread's interrupt
* status will be set. </p>
*
* <p> Interrupting a thread that is not alive need not have any effect.
*
* @throws SecurityException
* if the current thread cannot modify this thread
*
* @revised 6.0
* @spec JSR-51
*/
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
/**
* Tests whether the current thread has been interrupted. The
* <i>interrupted status</i> of the thread is cleared by this method. In
* other words, if this method were to be called twice in succession, the
* second call would return false (unless the current thread were
* interrupted again, after the first call had cleared its interrupted
* status and before the second call had examined it).
*
* <p>A thread interruption ignored because a thread was not alive
* at the time of the interrupt will be reflected by this method
* returning false.
*
* @return <code>true</code> if the current thread has been interrupted;
* <code>false</code> otherwise.
* @see #isInterrupted()
* @revised 6.0
*/
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
/**
* Tests whether this thread has been interrupted. The <i>interrupted
* status</i> of the thread is unaffected by this method.
*
* <p>A thread interruption ignored because a thread was not alive
* at the time of the interrupt will be reflected by this method
* returning false.
*
* @return <code>true</code> if this thread has been interrupted;
* <code>false</code> otherwise.
* @see #interrupted()
* @revised 6.0
*/
public boolean isInterrupted() {
return isInterrupted(false);
}
/**
* Tests if some Thread has been interrupted. The interrupted state
* is reset or not based on the value of ClearInterrupted that is
* passed.
*/
private native boolean isInterrupted(boolean ClearInterrupted);
interrupted和isInterrupted方法的区别?
如上图所示,interrupted()是线程的静态方法,返回当前线程的中断标记且会重置中断标记;isInterrupted()是实例方法,返回this的中断标记且不会重置中断标记。
可重入锁 & synchronized
可重入锁和synchronized的主要区别
- 机制是不一样的:synchronized是java内置的关键字,在JVM层面实现,系统会监控锁的释放,且自动释放,同步执行完或发生异常将释放锁;lock是JDK代码实现的,需要手动在finally模块释放,并可以非阻塞地获取锁。
- 性能不一样:竞争激烈的情况下lock会比synchronized好;竞争不激烈的情况下synchronized性能好,且synchronized会从偏向锁-->轻量级锁-->重量级锁升级
- 场景范围不一样:使用lock,线程2等待线程1释放锁的时候,可以不用一直等待,synchronized做不到
- 总的来说,lock提供了比synchronized更多的功能:如下图PPT所示
- synchronized 同步格式
synchronized(需要一个任意的对象(锁)){
代码块中放操作共享数据的代码;
}
- 关于Lock
- Lock其实只是一个接口
- ReentrantLock是唯一实现了Lock接口的类
可重入锁的实现?
先看JUC中的[AQS]
什么是synchronized的锁升级?
Java对象在内存中的存储结构包括:请求头,实例数据,填充数据;对象头包含Mark Word(hashCode, GC分代年龄,锁信息),Class Metadata address(指向对象类型数据的指针),Array Length(该对象为数组时,数组的长度)
-
偏向锁
当对象被创建出来的时候,就有了偏向锁的标志位“01”,但状态为0,即被创建的对象的偏向锁不生效;但是,当线程执行到临界区(critical section)的时候,此时会利用CAS操作将线程ID插入到Markword中,并修改偏向锁的标志位,即状态为0,且能知道是哪个线程拥有了偏向锁。
当此线程执行之后,若这个同步块代码又被进入了,则会:
- 判断当前线程是否与Markword当中的线程id一致,若一致则继续执行
- 若不一致,检查对象是否还是可偏向的,即偏向锁的状态
- 如果未偏向,则利用CAS竞争锁,也就是第一次获取锁时的操作
- 如果已经偏向了,可能需要重新偏向,或者大部分时候升级为轻量级锁
偏向锁存在的意义:大部分时候都是同一个线程进入同一个同步块,那么这个时候就不需要再有加锁解锁的开销;偏向锁实际上是偏向第一个拿到锁的线程。
-
升级为轻量级锁
-
锁撤销-有开销
- 在一个安全的点停止拥有锁的线程
- 遍历线程栈,存在锁记录的话,需要修复锁记录和Markword使其变成无锁状态
- 唤醒当前线程,升级成轻量级锁
-
轻量级锁:锁标志位为“00”,此时Markword bitfield被替换为指向LockRecord的指针,之前的bitfield将会复制到创建的这个LockRecord里面,LockRecord有个owner的指针指向对象
-
轻量级锁又分为自旋锁和自适应自旋锁
-
自旋锁:
当有线程竞争锁时,会在原地循环等待,直到锁被释放可以立刻获取锁,而这个原地循环会消耗CPU。适用于同步代码块执行很快的场景。经验表明,大部分同步代码块都执行很快。设置一个原地循环的最大限制次数,如果超过就升级为重量级锁。默认为10次,可以通过-XX:PreBlockSpin来进行更改。
-
自适应自旋锁:
即可动态调整自旋等待的次数而不是个固定值。
-
轻量级锁也被称为非阻塞同步,乐观锁
-
-
升级为重量级锁
- 重量级锁以来对象内部monitor,monitor又依赖于操作系统的mutex锁,此时的标志位为“10”,bitfield将会指向mutex的指针
- 为什么重量级锁开销大?
- 此时,等待的线程会被阻塞,不会消耗CPU但是阻塞或者唤醒一个线程时都需要操作系统来帮忙,这就需要从用户态转换到内核态,这个转换很耗时。
- 重量级锁又被称为阻塞同步,悲观锁
为什么竞争激烈的情况下lock比synchronized性能好?
volatile关键字
volatile是什么意思?原理是什么?
CAS的特点是什么?
监视器(Monitor)和Condition区别
操作系统在面对 进程/线程 间的同步的时候所支持的同步原语中,信号量(semaphore)和互斥量(mutex)是最重要的。在使用mutex进行并发控制时,要非常小心地控制其down和up的操作,否则将引起死锁。在此基础出现了更高层次的同步原语,使我们不需要亲自去操作变量进行阻塞和唤醒,这个更高级的同步原语就是monitor---也是编程语言在语法上提供的语法糖,如何实现属于编译器的工作,不是操作系统的范畴。
-
monitor的基本元素
- 临界区:互斥进入临界区
- monitor对象和锁:monitor object有相应数据结构保存被阻塞的线程,和基于mutex的锁
- 条件变量和定义在monitor对象上的wait和notify操作
-
java中的每一个对象都有一个监视器(Monitor)
-
使用synchronized关键字来圈定临界区,实现互斥的界限
-
monitor object:
synchronized需要指定一个对象与之关联,如果synchronized修饰的是实例方法,那么其实关联的就是this,如果修饰的是类方法,关联的对象就是this.class,这个关联的对象就是monitor object。
锁:
Java对象存储在内存中,分别分为三个部分:对象头,实例数据和对齐填充,在对象头中就保存了锁标识。
-
wait和notify的操作:
这些方法的实现是JVM内部基于C++实现的一套机制,原理如下图:总结就是,waitThread(即调用object.wait()方法的线程)先要获取object的锁(也就是为什么需要配合synchronized关键字使用),如果获取成功,执行到object.wait()这行代码处,该线程就放进了这个object的waitQueue里面,表示在等待中。如果获取失败,该线程将被放进synchronizedQueue中,想要继续尝试获取锁。
当调用object.notifyAll()的方法时,所有等在waitQueue里的线程将移动到synchronizedQueue里面去和那些waitThread没有获取锁的线程一起尝试获取锁。
一旦某个线程获取成功了,就继续往下执行。
-
-
总结:Monitor是一套机制,它界于操作系统和我们实际编程中间,提供并发编程的方式。
J.U.C.
AQS
AQS是什么?
AQS叫做队列同步器(AbstractQueuedSynchronizer),是用来构建锁和同步组件的基础框架。
- AQS有一个内部类Node,每个Node主要就是存一个Thread,Thread的状态,父节点prev和子节点next
static final class Node {
/** Marker to indicate a node is waiting in shared mode */
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
static final Node EXCLUSIVE = null;
/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;
/**
* Status field, taking on only the values:
* SIGNAL: The successor of this node is (or will soon be)
* blocked (via park), so the current node must
* unpark its successor when it releases or
* cancels. To avoid races, acquire methods must
* first indicate they need a signal,
* then retry the atomic acquire, and then,
* on failure, block.
* CANCELLED: This node is cancelled due to timeout or interrupt.
* Nodes never leave this state. In particular,
* a thread with cancelled node never again blocks.
* CONDITION: This node is currently on a condition queue.
* It will not be used as a sync queue node
* until transferred, at which time the status
* will be set to 0. (Use of this value here has
* nothing to do with the other uses of the
* field, but simplifies mechanics.)
* PROPAGATE: A releaseShared should be propagated to other
* nodes. This is set (for head node only) in
* doReleaseShared to ensure propagation
* continues, even if other operations have
* since intervened.
* 0: None of the above
*
* The values are arranged numerically to simplify use.
* Non-negative values mean that a node doesn't need to
* signal. So, most code doesn't need to check for particular
* values, just for sign.
*
* The field is initialized to 0 for normal sync nodes, and
* CONDITION for condition nodes. It is modified using CAS
* (or when possible, unconditional volatile writes).
*/
volatile int waitStatus;
/**
* Link to predecessor node that current node/thread relies on
* for checking waitStatus. Assigned during enqueuing, and nulled
* out (for sake of GC) only upon dequeuing. Also, upon
* cancellation of a predecessor, we short-circuit while
* finding a non-cancelled one, which will always exist
* because the head node is never cancelled: A node becomes
* head only as a result of successful acquire. A
* cancelled thread never succeeds in acquiring, and a thread only
* cancels itself, not any other node.
*/
volatile Node prev;
/**
* Link to the successor node that the current node/thread
* unparks upon release. Assigned during enqueuing, adjusted
* when bypassing cancelled predecessors, and nulled out (for
* sake of GC) when dequeued. The enq operation does not
* assign next field of a predecessor until after attachment,
* so seeing a null next field does not necessarily mean that
* node is at end of queue. However, if a next field appears
* to be null, we can scan prev's from the tail to
* double-check. The next field of cancelled nodes is set to
* point to the node itself instead of null, to make life
* easier for isOnSyncQueue.
*/
volatile Node next;
/**
* The thread that enqueued this node. Initialized on
* construction and nulled out after use.
*/
volatile Thread thread;
/**
* Link to next node waiting on condition, or the special
* value SHARED. Because condition queues are accessed only
* when holding in exclusive mode, we just need a simple
* linked queue to hold nodes while they are waiting on
* conditions. They are then transferred to the queue to
* re-acquire. And because conditions can only be exclusive,
* we save a field by using special value to indicate shared
* mode.
*/
Node nextWaiter;
/**
* Returns true if node is waiting in shared mode.
*/
final boolean isShared() {
return nextWaiter == SHARED;
}
/**
* Returns previous node, or throws NullPointerException if null.
* Use when predecessor cannot be null. The null check could
* be elided, but is present to help the VM.
*
* @return the predecessor of this node
*/
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
Node() { // Used to establish initial head or SHARED marker
}
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
- AQS本身维护一个Node节点的双向链表队列,和一个表征同步状态的state字段
/**
* Head of the wait queue, lazily initialized. Except for
* initialization, it is modified only via method setHead. Note:
* If head exists, its waitStatus is guaranteed not to be
* CANCELLED.
*/
private transient volatile Node head;
/**
* Tail of the wait queue, lazily initialized. Modified only via
* method enq to add new wait node.
*/
private transient volatile Node tail;
/**
* The synchronization state.
*/
private volatile int state;
AQS独占式和共享式的含义?
-
独占式:同一时刻只有一个线程持有同步状态,如ReentrantLock的实现
独占式获取同步状态过程:
- 共享式:共享资源可以被多个线程同时占有,如ReadWriteLock,CountdownLatch
AQS的模板模式是什么?
- AQS只是一个基础框架,
tryAcquire(int arg)
,tryRelease(int arg)
,tryAcquireShared(int arg)
,tryReleaseShared(int arg)
,isHeldExclusively()
都没有具体实现,因为公平锁有公平锁的实现方式,非公平锁有非公平锁的实现方式等,需要子类去实现
todo