(1)函数位置和JDK中的不一致,看这些之前线程的基础知识要熟悉(线程中断 (对于ThreadPoolExecutor只要看Demo和Demo07两个例子)线程状态 锁),还有这篇文章是线程池,状态啥的都是针对线程池的
(2)计算机中数字以补码的形式存储,正数原码 反码 补码相同。列如8位机中1原码 反码 补码都是0000 0001
-1原码 1000 0001 反码 1111 1110 补码1111 1111
(3)数字的逻辑运算 & | ~ ^,百度一下很好理解
(4)
(仅仅是个人的一点理解 有错误之处 希望告知)
一、变量部分
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
初始值为: 11100000000000000000000000000000 十进制:-536870912
被拆分为两部分:workerCount 表示有效线程数 低29位
runState 表示线程池状态 高3位
//位数值为29
private static final int COUNT_BITS = Integer.SIZE - 3;
//线程池最大线程数 值为536870911
二进制 表示 0001 1111 1111 1111 1111 1111 1111
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 线程池状态
//二进制表示 1110 0000 0000 0000 0000 0000 0000 0000 十进制 -536870912
private static final int RUNNING = -1 << COUNT_BITS;
//二进制表示 0
private static final int SHUTDOWN = 0 << COUNT_BITS;
//二进制表示 0010 0000 0000 0000 0000 0000 0000 0000 十进制 536870912
private static final int STOP = 1 << COUNT_BITS;
//二进制表示 0100 0000 0000 0000 0000 0000 0000 0000 十进制 1073741824
private static final int TIDYING = 2 << COUNT_BITS;
//二进制表示 0110 0000 0000 0000 0000 0000 0000 000 十进制 1610612736
private static final int TERMINATED = 3 << COUNT_BITS;
//阻塞队列 里面保存待执行的任务
private final BlockingQueue workQueue;
private final ReentrantLock mainLock = new ReentrantLock();
private final Condition termination = mainLock.newCondition();
//保存Worker
private final HashSet workers = new HashSet();
//池中同时存在的最大线程数。
private int largestPoolSize;
//完成的任务数
private long completedTaskCount;
//线程工厂
private volatile ThreadFactory threadFactory;
//线程池关闭或者队列满之后的拒绝策略
private volatile RejectedExecutionHandler handler;
//当设置allowCoreThreadTimeOut=true或有效线程大于corePoolSize数时,这个参数才会生效,等待超时的线程,生命周期结束。
private volatile long keepAliveTime;
private volatile boolean allowCoreThreadTimeOut;
//核心线程数
private volatile int corePoolSize;
//最大线程数
private volatile int maximumPoolSize;
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
//修改线程的权限
private static final RuntimePermission shutdownPerm =
new RuntimePermission(“modifyThread”);
//java安全管理器相关的默认是没有开启的,下面也有说一点
private final AccessControlContext acc;
二、构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
三、方法部分
上面已经说过低29为标识工作线程数 高3为是线程池的状态
private static int runStateOf(int c) { return c & ~CAPACITY; } //CAPACITY取反见变量部分(得到高3为即为线程池状态)
private static int workerCountOf(int c) { return c & CAPACITY; }//同理获取到低29 得到线程池工作线程数
private static int ctlOf(int rs, int wc) { return rs | wc; } //这个方法就是为了获取ctl的值 用状态与运行线程数取或 列如: ctlOf(RUNNING, 0)
下面三个方法比较运行状态和获取是否是运行状态
private static boolean runStateLessThan(int c, int s) { return c < s; }
private static boolean runStateAtLeast(int c, int s) { return c >= s;}
private static boolean isRunning(int c) {return c < SHUTDOWN;}
//下面两个方法 修改工作线程数 这里面ctl原子性加1或者减1 存在修改失败情况
private boolean compareAndIncrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect + 1);
}
private boolean compareAndDecrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect - 1);
}
//修改工作线程数 一定修改
private void decrementWorkerCount() {
do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}
//若果线程池当前状态大于等于目标状态或者成功修改运行状态退出循环,否则修改线程池状态
private void advanceRunState(int targetState) {
for (;;) {
int c = ctl.get();
if (runStateAtLeast(c, targetState) ||
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
break;
}
}
//1、尝试到TERMINATED状态 别胡思乱想了 咱们玩不了这个方法 哈哈
final void tryTerminate() {
for (;;) {
int c = ctl.get();//获取ctl里面的值 从上面可以知道这个里面有状态和工作线程数两个信息
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;//若果线程池是运行状态 或者 运行状态大于等于TIDYING状态显然就两种(TIDYING TERMINATED) 或者运行状态是SHUTDOWN 并且workQueue为空直接返回
//代码能执行到这里说明线程池不是TIDYING、TERMINATED、RUNNING状态,只能是SHUTDOWN并且workQueue不为空,或者是STOP状态
if (workerCountOf(c) != 0) { // Eligible to terminate //如果工作线程不为0的话,中断空闲的线程
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { //修改线程池状态为TIDYING,并使工作线程数置0
try {
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0));//修改线程池状态为TERMINATED,并使工作线程数置0
termination.signalAll();//唤醒所有因为等待termination条件阻塞的线程
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt(); 如果onlyOne为true最多中断一个线程,如果线程没有中断并且Worker没有锁定,则中断和Worker相关的线程
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
//Executor终止时调用的方法。 默认实现什么都不做
protected void terminated() { }
//检查有没有modifyThread 可以参考https://www.cnblogs.com/yiwangzhibujian/p/6207212.html文章大概了解一下SecurityManager
private void checkShutdownAccess() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(shutdownPerm);
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
security.checkAccess(w.thread);
} finally {
mainLock.unlock();
}
}
}
//即使处于活动状态,也会中断线程。
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
//中断所有没有被锁定的Worker线程
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
//任务拒绝策略实现
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
void onShutdown() {
}
//就判断一下线程池的状态
final boolean isRunningOrShutdown(boolean shutdownOK) {
int rs = runStateOf(ctl.get());
return rs == RUNNING || (rs == SHUTDOWN && shutdownOK);
}
//大概意思是: 将任务队列放到新列表。 但是,如果队列是DelayQueue或其他类型的队列,但poll或drainTo可能无法删除过期的元素,则会逐个删除它们,放到新列表
private List<Runnable> drainQueue() {
BlockingQueue<Runnable> q = workQueue;
ArrayList<Runnable> taskList = new ArrayList<Runnable>();
q.drainTo(taskList);
if (!q.isEmpty()) {
for (Runnable r : q.toArray(new Runnable[0])) {
if (q.remove(r))
taskList.add(r);
}
}
return taskList;
}
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get(); //获取ctl的值
int rs = runStateOf(c); //获取线程池状态
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false; //(1)这里面前提是线程池不处于RUNNING状态,如果处于运行状态直接跳过
// (2)线程池不在SHUTDOWN状态,直接返回。如果线程池处于SHUTDOWN 状态,firstTask 不为空的话,也是直接返回,不能再添加任务了
// (3)firstTask 为空的话 看一下workQueue,如果workQueue为空的话直接返回,就是说如果处于SHUTDOWN 不接受新任务,但是要处理
//里面的任务
for (;;) {
int wc = workerCountOf(c); //获取线程池中有效的线程数量
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize)) /
return false;
if (compareAndIncrementWorkerCount(c))
break retry;//线程数加1 成功retry循环结束,失败接着往下执行,直到修改成功退出循环
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)//判断线程池状态如果状态修改了,重新执行retry段
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);//创建一个java线程,将任务和线程关联
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) { //(1)如果线程池处于运行状态,条件成立。(2)如果线程池没有处于运行状态,但是如果处于SHUTDOWN 并且firstTask ,则进入循环
if (t.isAlive()) // precheck that t is startable//如果线程t已经开始而且没有结束,抛出异常
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();//获取池中的所有工作线程。
if (s > largestPoolSize)
largestPoolSize = s; //修改池中同时存在的最大线程数。
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();//这边任务启动
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
//如果任务启动失败workers的Workder删除,有效线程数减1,并且尝试将线程池状态置为TERMINATED
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
workers.remove(w);
decrementWorkerCount();
tryTerminate();
} finally {
mainLock.unlock();
}
}
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted //如果工作线程因异常而终止,线程池有效线程数一定减1,
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;//从Woker获取一共完成的任务数
workers.remove(w);//移除woker
} finally {
mainLock.unlock();
}
tryTerminate();//尝试终止线程池
int c = ctl.get();
//如果线程池状态小于STOP,如果线程是异常退出重新创建一个worker, 如果worker线程不是异常退出,进入第二个if执行,
//看是否配置allowCoreThreadTimeOut参数获取最小值min,如果min等于0并且workQueue不为空 min赋值1,如果有效线程数大于等于min直接返回
//如果有效线程数 小于min的话添加worker
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
//根据timed的值决定调用队列的哪个阻塞方法,如果设置了允许核心线程超时,或者有效线程数大于核心线程数条件为真,阻塞keepAliveTime时间,worker线程退出
//否则worker线程阻塞在这等待任务,shutdown尝试中断线程,条件也取决于这里面的阻塞方法
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.//如果线程池状态为STOP,或者线程池状态大于等于SHUTDOWN并且workQueue为空,有效线程数减1,直接返回
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))////有效线程数大于最大线程数并且有效线程数大于1或者有效线程数大于最大线程数并且workQueue为空,有效线程数减1,成功的话直接返回,否则从上面的for循环重复执行
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();//获取任务
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();//如果线程池处于STOP状态,并且当前线程没有中断的话,中断当前线程
//如果当前线程已经处于中断状态,并且线程池处于STOP状态,并且当前线程没有中断什么都不做(即如果线程池处于STOP状态,要保证当前线程处于中断状态)
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {//有效线程数小于corePoolSize新建线程处理任务
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {//执行到这里说明 有效线程数已经大于等于corePoolSize了, 如果线程池是运行状态并且任务成功放到队列里面,条件为真
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))//第二次检测状态,如果线程池不在处于运行状态,并且成功移除任务,执行拒绝策略。
reject(command);
else if (workerCountOf(recheck) == 0)//线程池处于运行状态或者任务移除失败,但是没有有效线程的情况
addWorker(null, false);
}
else if (!addWorker(command, false))//这里面是线程池不处于运行状态或者放入队列失败的情况,若果线程池不在运行状态,command不为空的话,结合addWorker这种情况是拒绝的。如果是放入队列失败的话还是结合addWorker看一下当前的有效线程数是否大于等于最大线程数,如果是直接拒绝,否则创建新worker处理任务
reject(command);
}
//这个里面加入队列里面有任务没有处理完 会继续处理
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();//默认不开启 开启的话需要配置修改线程的权限
advanceRunState(SHUTDOWN);//如果线程池当前状态大于等于SHUTDOWN状态什么都不做,如果处于运行状态则修改成SHUTDOWN状态,有效线程数不变
interruptIdleWorkers();//中断 没有被中断,并且没有被锁定的worker线程(中断空闲的worker线程)
// eg:假如 allowCoreThreadTimeOut = false ,核心线程数3 最大线程数5 任务队列3,第4、5线程就会因为超时而结束。如果队里里面为空,核心线程就会阻塞在take上,此时shutdown,阻塞的线程就会响应中断而结束,如果队列不会空没有中断的线程会继续完成剩下的任务
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();//关闭线程池 这里面也会中断上次没有中断的线程
}
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();//同上
advanceRunState(STOP);//同上 但是线程池修改为STOP状态
interruptWorkers();//中断正在运行的任务线程
tasks = drainQueue();//列表中的任务清空
// eg:假如 allowCoreThreadTimeOut = false ,核心线程数3 最大线程数5 任务队列3,第4、5线程就会因为超时而结束。如果队里里面不为空,就没有阻塞的线程,此时shutdownNow,[运行中的任务不会马上停止](https://blog.csdn.net/chen_zhixiu/article/details/80882970)(可以运行Demo09),advanceRunState(STOP),列表中的任务清空有的线程因为interruptWorkers而结束,还有的线程因为runWorker --->> wt.interrupt();而结束
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
public boolean isShutdown() {
return ! isRunning(ctl.get());
}
public boolean isTerminating() {
int c = ctl.get();
return ! isRunning(c) && runStateLessThan(c, TERMINATED);
}
public boolean isTerminated() {
return runStateAtLeast(ctl.get(), TERMINATED);
}
//等待一定的时间线程池的状态是否大于等于TERMINATED
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (;;) {
if (runStateAtLeast(ctl.get(), TERMINATED))
return true;
if (nanos <= 0)
return false;
nanos = termination.awaitNanos(nanos);
}
} finally {
mainLock.unlock();
}
}
//默认就是shutdown()
protected void finalize() {
SecurityManager sm = System.getSecurityManager();
if (sm == null || acc == null) {
shutdown();
} else {
PrivilegedAction<Void> pa = () -> { shutdown(); return null; };
AccessController.doPrivileged(pa, acc);
}
}
public void setThreadFactory(ThreadFactory threadFactory) {
if (threadFactory == null)
throw new NullPointerException();
this.threadFactory = threadFactory;
}
public ThreadFactory getThreadFactory() {
return threadFactory;
}
public void setRejectedExecutionHandler(RejectedExecutionHandler handler) {
if (handler == null)
throw new NullPointerException();
this.handler = handler;
}
public RejectedExecutionHandler getRejectedExecutionHandler() {
return handler;
}
public void setCorePoolSize(int corePoolSize) {
if (corePoolSize < 0)
throw new IllegalArgumentException();
int delta = corePoolSize - this.corePoolSize;
this.corePoolSize = corePoolSize;
if (workerCountOf(ctl.get()) > corePoolSize) //如果有效线程数大于corePoolSize,中断所有空闲的worker线程
interruptIdleWorkers();
else if (delta > 0) {//说明有效线程数小于corePoolSize情况
int k = Math.min(delta, workQueue.size());//这里面k取决于两者 的大小,并且取最小值
while (k-- > 0 && addWorker(null, true)) {//创建新的worker处理任务队列里面的任务
if (workQueue.isEmpty())
break;
}
}
}
public int getCorePoolSize() {
return corePoolSize;
}
//提前把核心线程初始化好
public boolean prestartCoreThread() {
return workerCountOf(ctl.get()) < corePoolSize &&
addWorker(null, true);
}
void ensurePrestart() {
int wc = workerCountOf(ctl.get());
if (wc < corePoolSize)
addWorker(null, true);
else if (wc == 0)
addWorker(null, false);
}
public int prestartAllCoreThreads() {
int n = 0;
while (addWorker(null, true))
++n;
return n;
}
//就是核心线程也会被中断
public void allowCoreThreadTimeOut(boolean value) {
if (value && keepAliveTime <= 0)
throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
if (value != allowCoreThreadTimeOut) {
allowCoreThreadTimeOut = value;
if (value)
interruptIdleWorkers();
}
}
//这里面设置最大线程数 要是当前最大线程数大于maximumPoolSize
public void setMaximumPoolSize(int maximumPoolSize) {
if (maximumPoolSize <= 0 || maximumPoolSize < corePoolSize)
throw new IllegalArgumentException();
this.maximumPoolSize = maximumPoolSize;
if (workerCountOf(ctl.get()) > maximumPoolSize)
interruptIdleWorkers();
}
//清除任务
public void purge() {
final BlockingQueue<Runnable> q = workQueue;
try {
Iterator<Runnable> it = q.iterator();
while (it.hasNext()) {
Runnable r = it.next();
if (r instanceof Future<?> && ((Future<?>)r).isCancelled())
it.remove();
}
} catch (ConcurrentModificationException fallThrough) {
// Take slow path if we encounter interference during traversal.
// Make copy for traversal and call remove for cancelled entries.
// The slow path is more likely to be O(N*N).
for (Object r : q.toArray())
if (r instanceof Future<?> && ((Future<?>)r).isCancelled())
q.remove(r);
}
tryTerminate(); // In case SHUTDOWN and now empty
}
//任务总数 = 完成的 + 正在执行的 + 队列里面的
public long getTaskCount() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
long n = completedTaskCount;
for (Worker w : workers) {
n += w.completedTasks;
if (w.isLocked())
++n;
}
return n + workQueue.size();
} finally {
mainLock.unlock();
}
}