Tabla de contenido
Sincronización de campo, sincronización de clase interna
Clases internas NonfairSync, FairSync, 2 constructores
Método bloqueo, bloqueo Interrumpidamente, 2 intentos Bloquear, desbloquear, nuevo Condición
方法 getHoldCount, isHeldByCurrentThread, isLocked, isFair, getOwner
Método 3 hasXXX, 4 getXXX, toString
Introducción
package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;
import java.util.Collection;
/**
* 可重入互斥锁具有与使用同步方法和语句访问的隐式监视器锁
* 相同的基本行为和语义,但具有扩展功能。
*
* <p>ReentrantLock由最后成功锁定,但还没有解锁的线程拥有。
* 调用lock的线程将在锁没有被另一个线程拥有时,返回并成功获得锁。
* 如果当前线程已经拥有锁,该方法将立即返回。
* 可以使用方法isHeldByCurrentThread和getHoldCount来检查。
*
* <p>这个类的构造函数接受一个可选的公平性参数。
* 当设置为true时,处于争用状态的锁倾向于,授予等待时间最长的线程访问权。
* 否则,这个锁不能保证任何特定的访问顺序。
* 使用由许多线程访问的公平锁的程序可能显示更低的总体吞吐量
* (也就是说,更慢;通常要慢得多),但在获取锁的时间上有更小的变化,并保证没有饥饿。
*
* 但是请注意,锁的公平性并不能保证线程调度的公平性。
* 因此,许多使用了公平锁的线程中的一个可能会连续多次获得它,而其他活动的线程没有进展,也没有当前持有该锁。
* 还要注意,不计时的tryLock()方法不尊重公平性设置。
* 如果锁可用,即使其他线程正在等待,它也会成功。
*
* <p>推荐的做法是总是在调用lock后立即使用try块,最典型的是在before/after结构中,例如:
*
* <pre> {@code
* class X {
* private final ReentrantLock lock = new ReentrantLock();
* // ...
*
* public void m() {
* lock.lock(); // block until condition holds
* try {
* // ... method body
* } finally {
* lock.unlock()
* }
* }
* }}</pre>
*
* <p>除了实现Lock接口之外,这个类还定义了一些公共的和受保护的方法来检查锁的状态。
* 其中一些方法只对检测和监视有用。
*
* <p>该类的序列化行为与内置锁的行为相同:
* 反序列化的锁处于解锁状态,而不管其序列化时的状态如何。
*
* <p>这个锁最多支持同一个线程的2147483647个递归锁。
* 尝试超过此限制将导致锁定方法抛出错误。
*
* <p>Sync继承了AbstractQueuedSynchronizer,在Sync里,实现了tryRelease(父类中的实现是抛出异常),定义了抽象Lock方法。
* NonfairSync和FairSync都继承了Sync,在NonfairSync和FairSync里,实现了各自的lock方法,其中会调用acquire(父类已有的实现),会实现tryAcquire(父类中的实现是抛出异常)。
* AQS可以实现 独占锁和共享锁,但ReentrantLock只使用了独占锁部分。获取锁的方式可以分为 公平和非公平,响应中断和不响应中断。
*
* @since 1.5
* @author Doug Lea
*/
public class ReentrantLock implements Lock, java.io.Serializable
Sincronización de campo, sincronización de clase interna
private static final long serialVersionUID = 7373984872572414699L;
/** 提供所有实现机制的同步器 */
private final Sync sync;
/**
* 这个锁的同步控制基础。
* 子类分为以下的公平和不公平版本。
* 使用AQS状态来表示锁上的持有数。
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* 执行Lock.lock。
* 子类化的主要原因是允许非公平版本的快速路径。
*/
abstract void lock();
/**
* 执行不公平的tryLock。
* 子类中实现tryAcquire,但是两者的tryLock方法都需要不公平的尝试
*
* 再看子类实现nonfairTryAcquire,发现和公平锁的实现tryAcquire几乎一模一样,
* 除了公平锁需要多判断一下hasQueuedPredecessors的返回值,也就是需要判断队列是否等待线程。
*
* 其实非公平锁的原理就是:不管等待队列中是否有等待线程,直接竞争获取state,要是获取成功了,就直接设置当前线程为exclusiveOwnerThread成员了。
* 这不就是,插了所有等待线程的队嘛。也就是说,非公平锁它可以和队列中的head后继的代表线程同时竞争锁。
*
* 但是 非公平锁插队的机会只有在acquire里面第一次执行tryAcquire的时候,
* 一旦这里tryAcquire获取锁失败了,就会进入acquireQueued的死循环,在循环之前会将当前线程包装成node放到队尾去,
* 之后在循环中的每次循环,想要执行获取锁的动作(tryAcquire(arg))必须自己是head的后继才可以(p == head)。
*
* 非公平锁插队的机会只有,acquire方法里第一次执行tryAcquire的时候,如果这次插队失败,那么它也不可能插队成功了。
* 公平锁和非公平锁,在进入acquireQueued之后,使用起来没有任何区别。
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 如果状态是0,cas设置0到1,然后设置独有线程
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 如果线程是自己,设置状态为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())有了这个判断,没有持有锁的线程调用了ReentrantLock.unlock()就会抛出异常。
// 只有持有锁的线程,才会继续往下执行。
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 该函数执行完毕,同步器的状态会变成c。如果c不为0,说明重入的锁还没有返回足够多的次数,返回false;
// 如果c为0,说明独占锁已经被释放,设置ExclusiveOwnerThread成员为null,并返回true。
// 如果状态之后为0,解放线程,否则设置状态为state-1
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
protected final boolean isHeldExclusively() {
// 虽然我们通常必须在所有者之前读取状态,但我们不需要这样做来检查当前线程是否为所有者
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
// newCondition方法直接返回一个ConditionObject
return new ConditionObject();
}
// 从外部类转发的方法
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
/**
* 从流中重新构造实例(即反序列化)。
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // 重置为解锁状态
}
}
Clases internas NonfairSync, FairSync, 2 constructores
/**
* 非公平锁的同步对象
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* 执行锁。尝试立即抢占,一旦失败,执行正常的acquire。
*
* 与公平锁相比,非公平锁的lock实现略有不同。
* 公平锁的lock实现是直接调用acquire(1),而非公平锁的lock实现会先尝试CAS修改state,
* 如果能够将state从0改成1,那么说明当前线程获取锁,既然获取锁,那么便直接插队setExclusiveOwnerThread(Thread.currentThread())。
* 如果CAS操作失败,再走正常流程,调用父类函数acquire(1)。
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
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代表state需要增加1。
acquire(1);
}
/**
* tryAcquire的公平版本。
* 不要授予访问权,除非递归调用或没有等待者或首先。
*
* 简单来说,tryAcquire的实现就是:尝试获得锁一次,成功了返回true,否则false。
* 参数acquires代表你想让同步器的state再增加多少。函数返回值为true,代表你成功获取到了锁。
* 返回true时,同步器的state已经增加了acquires,且Thread exclusiveOwnerThread成员被设置成了当前执行线程。
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 首先获得同步器的state。
int c = getState();
if (c == 0) {
// 如果进入if (c == 0)分支,说明当前state为0,当前锁已经被释放掉了,但此时CHL队列中可能有线程已经在排队了
//(考虑到此版本是公平锁),所以需要通过hasQueuedPredecessors判断队列中是否已经有等待线程(返回true代表有)。
// hasQueuedPredecessors返回true,那么直接返回false,说明tryAcquire失败。
// hasQueuedPredecessors返回false,才会尝试CAS操作设置state。
// 前者设置state使用的是CAS操作,因为此时当前线程还没有获得锁,不一定谁能竞争到,所以用CAS保证只有一个线程能设置state成功;
// 如果CAS操作成功,那么说明当前线程成功抢到了锁,调用setExclusiveOwnerThread设置独占线程;
// 如果CAS操作失败,那么直接返回false,说明此次tryAcquire失败。
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
// 如果进入else if (current == getExclusiveOwnerThread())分支,说明当前state不为0,已经有线程持有锁,
// 但考虑锁可重入,如果当前线程就是AQS的独占线程的话,那么就直接让state再加acquires;
// 如果当前线程根本不是AQS的独占线程,那么直接返回false,说明此次tryAcquire失败。
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
// 后者直接set,是因为当前线程已经获取锁了,此时别的线程都不能修改state,即只有当前线程在写state,所以直接set是安全的。
setState(nextc);
return true;
}
return false;
}
}
/**
* 创建ReentrantLock的实例。这相当于使用ReentrantLock(false)。
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* 使用给定的公平策略创建ReentrantLock的实例。
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
Método bloqueo, bloqueo Interrumpidamente, 2 intentos Bloquear, desbloquear, nuevo Condición
/**
* 获得锁。
*
* <p>如果锁没有被其他线程持有,则获取锁并立即返回,将锁持有计数设置为1。
*
* <p>如果当前线程已经持有锁,则holdcount增加1,该方法立即返回。
*
* <p>如果锁被另一个线程持有,那么当前线程出于线程调度的目的将被禁用,
* 并处于休眠状态,直到获得锁,此时锁持有计数被设置为1。
*/
public void lock() {
sync.lock();
}
/**
* 除非当前线程被中断,否则将获得锁。
*
* <p>如果锁没有被其他线程持有,则获取锁并立即返回,将锁持有计数设置为1。
*
* <p>如果当前线程已经持有该锁,那么持有计数将增加1,该方法将立即返回。
*
* <p>如果锁被另一个线程持有,那么当前线程就会因为线程调度的目的而被禁用,并处于休眠状态,直到有以下两种情况发生:
*
* <ul>
*
* <li>锁是由当前线程获取的;或
*
* <li>其他线程中断当前线程。
*
* </ul>
*
* <p>如果锁是由当前线程获得的,那么锁holdcount被设置为1。
*
* <p>如果当前线程:
*
* <ul>
*
* <li>在进入该方法时设置中断状态;或
*
* <li>在获取锁时被中断,
*
* </ul>
*
* 然后抛出InterruptedException,并清除当前线程的中断状态。
*
* <p>在这个实现中,因为这个方法是一个显式中断点,
* 所以优先响应中断而不是正常的或重入的锁获取。
*
* @throws InterruptedException if the current thread is interrupted
*/
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
/**
* 只有在调用时锁没有被另一个线程持有时才获取该锁。
*
* <p>如果没有被其他线程持有,则获取锁并立即返回值为true,将锁持有计数设置为1。
* 即使这个锁被设置为使用了公平排序策略,调用tryLock()也会立即获取可用的锁,无论是否有其他线程正在等待这个锁。
* 这种“冲撞”行为在某些情况下是有用的,即使它破坏了公平。
* 如果您想尊重这个锁的公平性设置,那么使用tryLock(0, TimeUnit.SECONDS),它几乎是等价的(它也检测中断)。
*
* <p>如果当前线程已经持有该锁,则holdcount增加1,该方法返回true。
*
* <p>如果锁被另一个线程持有,那么这个方法将立即返回值为false。
*
* @return {@code true} if the lock was free and was acquired by the
* current thread, or the lock was already held by the current
* thread; and {@code false} otherwise
*/
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
/**
* 如果在给定的等待时间内其他线程没有持有该锁,并且当前线程没有被中断,则获取该锁。
*
* <p>如果锁没有被其他线程持有,则获取锁并立即返回值true,将锁持有数设置为1。
* 如果这个锁被设置为使用公平排序策略,那么如果有其他线程正在等待这个锁,那么这个可用的锁将不会被获取。
* 这与tryLock()方法相反。
* 如果你想要一个定时tryLock,它允许在一个公平的锁上抢占,那么将定时和非定时的形式组合在一起:
*
* <pre> {@code
* if (lock.tryLock() ||
* lock.tryLock(timeout, unit)) {
* ...
* }}</pre>
*
* <p>如果当前线程已经持有该锁,则持有计数增加1,并且该方法返回true。
*
* <p>如果锁被另一个线程持有,那么当前线程在线程调度中会被禁用,并处于休眠状态,直到以下三种情况之一发生:
*
* <ul>
*
* <li>锁是由当前线程获取的;或
*
* <li>其他线程中断当前线程;或
*
* <li>如果获得了锁,则返回值true,并将锁持有计数设置为1。
*
* </ul>
*
* <p>经过指定的等待时间
*
* <p>如果当前线程:
*
* <ul>
*
* <li>在进入该方法时设置中断状态;或
*
* <li>在获取锁时被中断,
*
* </ul>
* 然后抛出InterruptedException,并清除当前线程的中断状态。
*
* <p>如果指定的等待时间过去了,则返回值false。
* 如果时间小于或等于零,该方法将根本不等待。
*
* <p>在这个实现中,由于这个方法是一个显式的中断点,
* 优先响应中断而不是正常的或重入的锁获取,并报告等待时间的推移。
*
* @param timeout the time to wait for the lock
* @param unit the time unit of the timeout argument
* @return {@code true} if the lock was free and was acquired by the
* current thread, or the lock was already held by the current
* thread; and {@code false} if the waiting time elapsed before
* the lock could be acquired
* @throws InterruptedException if the current thread is interrupted
* @throws NullPointerException if the time unit is null
*/
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
/**
* 试图释放此锁。
*
* <p>如果当前线程是该锁的持有者,则holdcount减少。如果保持计数现在为零,那么锁被释放。
* 如果当前线程不是这个锁的持有者,那么抛出IllegalMonitorStateException。
*
* 释放锁的过程,根本不会区分 公平或不公平、响应中断或不响应中断、超时或不超时。
* 这是因为,这些区别都只是存在于尝试获取锁的方式上而已,既然已经获得了锁,也就不需要有这些区别。
*
* @throws IllegalMonitorStateException if the current thread does not
* hold this lock
*/
public void unlock() {
sync.release(1);
}
/**
* 返回与此锁实例一起使用的条件实例。
*
* <p>当与内置监视锁一起使用时,返回的Condition实例支持与
* 对象监视器方法(wait、notify和notifyAll)相同的用法。
*
* <ul>
*
* <li>当任何await或signal方法被调用时,如果这个锁没有被持有,那么抛出一个IllegalMonitorStateException。
*
* <li>当条件等待方法被调用时,锁被释放,在它们返回之前,锁被重新获取,锁持有计数恢复到该方法被调用时的值。
*
* <li>如果一个线程在等待的时候被中断,那么等待将终止,一个InterruptedException将被抛出,线程的中断状态将被清除。
*
* <li> 等待线程以FIFO顺序被signal。
*
* <li>从等待方法中返回的线程重新获取锁的顺序与初始获取锁的线程相同,
* 这在默认情况下没有指定,但是对于公平锁,优先于那些等待时间最长的线程。
*
* </ul>
*
* @return the Condition object
*/
public Condition newCondition() {
return sync.newCondition();
}
方法 getHoldCount, isHeldByCurrentThread, isLocked, isFair, getOwner
/**
* 查询当前线程对该锁的持有数。
*
* <p> 一个线程对每个锁操作持有一个锁,如果锁操作与解锁操作不匹配的话。
*
* <p>保持计数信息通常只用于测试和调试目的。
* 例如,如果某段代码不应该输入已经持有的锁,那么我们可以断言:
*
* <pre> {@code
* class X {
* ReentrantLock lock = new ReentrantLock();
* // ...
* public void m() {
* assert lock.getHoldCount() == 0;
* lock.lock();
* try {
* // ... method body
* } finally {
* lock.unlock();
* }
* }
* }}</pre>
*
* @return the number of holds on this lock by the current thread,
* or zero if this lock is not held by the current thread
*/
public int getHoldCount() {
return sync.getHoldCount();
}
/**
* 查询当前线程是否持有该锁。
*
* <p>与内置监视器锁的Thread.holdsLock(Object)方法类似,此方法通常用于调试和测试。
* 例如,只有在持有锁时才应该调用的方法可以断言是这样的:
*
* <pre> {@code
* class X {
* ReentrantLock lock = new ReentrantLock();
* // ...
*
* public void m() {
* assert lock.isHeldByCurrentThread();
* // ... method body
* }
* }}</pre>
*
* <p>它也可以用来确保重入锁以不可重入的方式使用,例如:
*
* <pre> {@code
* class X {
* ReentrantLock lock = new ReentrantLock();
* // ...
*
* public void m() {
* assert !lock.isHeldByCurrentThread();
* lock.lock();
* try {
* // ... method body
* } finally {
* lock.unlock();
* }
* }
* }}</pre>
*
* @return {@code true} if current thread holds this lock and
* {@code false} otherwise
*/
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
/**
* 查询该锁是否被任何线程持有。
* 这种方法是设计用于监控系统状态,而不是用于同步控制。
*
* @return {@code true} if any thread holds this lock and
* {@code false} otherwise
*/
public boolean isLocked() {
return sync.isLocked();
}
/**
* 如果该锁的公平性设置为true则返回true。
*
* @return {@code true} if this lock has fairness set true
*/
public final boolean isFair() {
return sync instanceof FairSync;
}
/**
* 返回当前拥有该锁的线程,如果没有,则返回null。
* 当该方法被非所有者线程调用时,返回值反映当前锁状态的最大努力近似值。
* 例如,即使有线程正在试图获取锁,但还没有这样做,所有者也可能暂时为空。
* 这种方法是为了便于构造提供更广泛的锁监控设施的子类。
*
* @return the owner, or {@code null} if not owned
*/
protected Thread getOwner() {
return sync.getOwner();
}
Método 3 hasXXX, 4 getXXX, toString
/**
* 查询是否有线程正在等待获取该锁。
* 注意,因为取消可能在任何时候发生,返回true并不保证任何其他线程会获得这个锁。
* 该方法主要用于监控系统状态。
*
* @return {@code true} if there may be other threads waiting to
* acquire the lock
*/
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
/**
* 查询给定线程是否正在等待获取这个锁。
* 注意,因为取消可能在任何时候发生,一个真正的返回并不保证这个线程将会获得这个锁。
* 该方法主要用于监控系统状态。
*
* @param thread the thread
* @return {@code true} if the given thread is queued waiting for this lock
* @throws NullPointerException if the thread is null
*/
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();
}
/**
* Returns a collection containing threads that may be waiting to
* acquire 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 monitoring facilities.
*
* @return the collection of threads
*/
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
/**
* Queries whether any threads are waiting on the given condition
* associated with this lock. Note that because timeouts and
* interrupts may occur at any time, a {@code true} return does
* not guarantee that a future {@code signal} will awaken any
* threads. This method is designed primarily for use in
* monitoring of the system state.
*
* @param condition the condition
* @return {@code true} if there are any 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 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<Thread> 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() + "]");
}