- 浏览: 981047 次
文章分类
- 全部博客 (428)
- Hadoop (2)
- HBase (1)
- ELK (1)
- ActiveMQ (13)
- Kafka (5)
- Redis (14)
- Dubbo (1)
- Memcached (5)
- Netty (56)
- Mina (34)
- NIO (51)
- JUC (53)
- Spring (13)
- Mybatis (17)
- MySQL (21)
- JDBC (12)
- C3P0 (5)
- Tomcat (13)
- SLF4J-log4j (9)
- P6Spy (4)
- Quartz (12)
- Zabbix (7)
- JAVA (9)
- Linux (15)
- HTML (9)
- Lucene (0)
- JS (2)
- WebService (1)
- Maven (4)
- Oracle&MSSQL (14)
- iText (11)
- Development Tools (8)
- UTILS (4)
- LIFE (8)
最新评论
-
Donald_Draper:
Donald_Draper 写道刘落落cici 写道能给我发一 ...
DatagramChannelImpl 解析三(多播) -
Donald_Draper:
刘落落cici 写道能给我发一份这个类的源码吗Datagram ...
DatagramChannelImpl 解析三(多播) -
lyfyouyun:
请问楼主,执行消息发送的时候,报错:Transport sch ...
ActiveMQ连接工厂、连接详解 -
ezlhq:
关于 PollArrayWrapper 状态含义猜测:参考 S ...
WindowsSelectorImpl解析一(FdMap,PollArrayWrapper) -
flyfeifei66:
打算使用xmemcache作为memcache的客户端,由于x ...
Memcached分布式客户端(Xmemcached)
Executor接口的定义:http://donald-draper.iteye.com/blog/2365625
ExecutorService接口定义:http://donald-draper.iteye.com/blog/2365738
Future接口定义:http://donald-draper.iteye.com/blog/2365798
FutureTask解析:http://donald-draper.iteye.com/blog/2365980
CompletionService接口定义:http://donald-draper.iteye.com/blog/2366239
ExecutorCompletionService解析:http://donald-draper.iteye.com/blog/2366254
AbstractExecutorService解析:http://donald-draper.iteye.com/blog/2366348
ScheduledExecutorService接口定义:http://donald-draper.iteye.com/blog/2366436
ThreadPoolExecutor解析一(核心线程池数量、线程池状态等) :
http://donald-draper.iteye.com/blog/2366934
ThreadPoolExecutor解析二(线程工厂、工作线程,拒绝策略等):
http://donald-draper.iteye.com/blog/2367064
ThreadPoolExecutor解析三(线程池执行提交任务):
http://donald-draper.iteye.com/blog/2367199
上一篇看了线程池执行提交任务,先回顾一下:
执行任务的过程为,如果工作线程数量小于核心线程池数量,添加工作线程,执行任务;如果添加工作线程失败,则添加任务到队列,并进行双检查,如果在上述期间,线程池关闭,回滚任务,从队列中移除任务;如果任务入队列失败,根据工作线程数量是否大于最大线程池数量,来判断是否应该添加工作线程执行任务;如果工作线程小于最大线程池数量,则CAS操作workCount,成功创建工作线程执行任务。添加工作线程的过程为,如果应该添加工作线程,则CAS更新工作线程数,如果更新成功,则创建工作线程,执行任务。如果线程是已关闭或正在关闭,则添加工作线程失败。如果线程工厂创建线程失败,则返回false,如果由于线程工厂返回null或OutOfMemoryError等原因,执行回滚清除工作。回滚清除工作主要是工作线程和工作线程数。最后检查线程是是否关闭,如果线程池正在运行,或正在关闭且队列不为空,则直接返回,否则及线程池已关闭,检查工作线程是否为0,不为零根据ONLY_ONE判断中断一个空闲线程还是多个。
今天来看一下线程池的关闭:
分四步来看:
1.
2.
3.
4.
下面来看每一点:
1.
//检查线程访问权限
2.
//更新线程池状态为SHUTDOWN
3.
//中断空闲工作线程
//只中断一个空闲工作线程
4.
//线程池关闭hook
我们也把tryTerminate代码贴出来,以便理解:
//执行结束工作
从上面可以看出关闭线程池,首先检查工作线程运行时访问权限,
更新线程状态为SHUTDOWN,中断空闲工作线程,最后尝试关闭线程池。
再看来以及关闭线程池:
来看清空任务队列,并放到tasks集合中
立即关闭线程与关闭线程池的不同是,对于关闭线程池,先前提交的任务将会被工作线程执行,新的线程将会被拒绝;而立即关闭线程,尝试停止正在执行的任务,停止等待任务线程的处理,任务队列将会被排空,并返回任务队列中的任务集。这两个方法都不会等待任务执行完或任务结束。
我们可以用awaitTermination来等待任务执行完
来看一些其他的方法:
总结:
关闭线程池,首先检查工作线程运行时访问权限,更新线程状态为SHUTDOWN,中断空闲工作线程,最后尝试关闭线程池。立即关闭线程与关闭线程池的不同是,对于关闭线程池,先前提交的任务将会被工作线程执行,新的线程将会被拒绝;而立即关闭线程,尝试停止正在执行的任务,停止等待任务线程的处理,任务队列将会被排空,并返回任务队列中的任务集。这两个方法都不会等待任务执行完或任务结束。我们可以用awaitTermination来等待任务执行完。
ExecutorService接口定义:http://donald-draper.iteye.com/blog/2365738
Future接口定义:http://donald-draper.iteye.com/blog/2365798
FutureTask解析:http://donald-draper.iteye.com/blog/2365980
CompletionService接口定义:http://donald-draper.iteye.com/blog/2366239
ExecutorCompletionService解析:http://donald-draper.iteye.com/blog/2366254
AbstractExecutorService解析:http://donald-draper.iteye.com/blog/2366348
ScheduledExecutorService接口定义:http://donald-draper.iteye.com/blog/2366436
ThreadPoolExecutor解析一(核心线程池数量、线程池状态等) :
http://donald-draper.iteye.com/blog/2366934
ThreadPoolExecutor解析二(线程工厂、工作线程,拒绝策略等):
http://donald-draper.iteye.com/blog/2367064
ThreadPoolExecutor解析三(线程池执行提交任务):
http://donald-draper.iteye.com/blog/2367199
上一篇看了线程池执行提交任务,先回顾一下:
执行任务的过程为,如果工作线程数量小于核心线程池数量,添加工作线程,执行任务;如果添加工作线程失败,则添加任务到队列,并进行双检查,如果在上述期间,线程池关闭,回滚任务,从队列中移除任务;如果任务入队列失败,根据工作线程数量是否大于最大线程池数量,来判断是否应该添加工作线程执行任务;如果工作线程小于最大线程池数量,则CAS操作workCount,成功创建工作线程执行任务。添加工作线程的过程为,如果应该添加工作线程,则CAS更新工作线程数,如果更新成功,则创建工作线程,执行任务。如果线程是已关闭或正在关闭,则添加工作线程失败。如果线程工厂创建线程失败,则返回false,如果由于线程工厂返回null或OutOfMemoryError等原因,执行回滚清除工作。回滚清除工作主要是工作线程和工作线程数。最后检查线程是是否关闭,如果线程池正在运行,或正在关闭且队列不为空,则直接返回,否则及线程池已关闭,检查工作线程是否为0,不为零根据ONLY_ONE判断中断一个空闲线程还是多个。
今天来看一下线程池的关闭:
/** * Initiates an orderly shutdown in which previously submitted * tasks are executed, but no new tasks will be accepted. * Invocation has no additional effect if already shut down. * 先前提交的任务将会被工作线程执行,新的线程将会被拒绝。这个方法 不会等待提交的任务执行完,我们可以用awaitTermination来等待任务执行完。 * <p>This method does not wait for previously submitted tasks to * complete execution. Use {@link #awaitTermination awaitTermination} * to do that. * * @throws SecurityException {@inheritDoc} */ public void shutdown() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { //检查线程访问权限 checkShutdownAccess(); //更新线程池状态为SHUTDOWN advanceRunState(SHUTDOWN); //中断空闲工作线程 interruptIdleWorkers(); //线程池关闭hook onShutdown(); // hook for ScheduledThreadPoolExecutor } finally { mainLock.unlock(); } //尝试结束线程池,这个前面以说,这里不再说 tryTerminate(); }
分四步来看:
1.
//检查线程访问权限 checkShutdownAccess();
2.
//更新线程池状态为SHUTDOWN advanceRunState(SHUTDOWN);
3.
//中断空闲工作线程 interruptIdleWorkers();
4.
//线程池关闭hook onShutdown(); // hook for ScheduledThreadPoolExecutor
下面来看每一点:
1.
//检查线程访问权限
checkShutdownAccess();
/** * If there is a security manager, makes sure caller has * permission to shut down threads in general (see shutdownPerm). * If this passes, additionally makes sure the caller is allowed * to interrupt each worker thread. This might not be true even if * first check passed, if the SecurityManager treats some threads * specially. */ 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(); } } }
2.
//更新线程池状态为SHUTDOWN
advanceRunState(SHUTDOWN);
/** * Transitions runState to given target, or leaves it alone if * already at least the given target. * * @param targetState the desired state, either SHUTDOWN or STOP * (but not TIDYING or TERMINATED -- use tryTerminate for that) */ private void advanceRunState(int targetState) { for (;;) { int c = ctl.get(); if (runStateAtLeast(c, targetState) || ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c)))) break; } }
3.
//中断空闲工作线程
interruptIdleWorkers();
/** * Common form of interruptIdleWorkers, to avoid having to * remember what the boolean argument means. */ private void interruptIdleWorkers() { //遍历工作线程集合,中断空闲工作线程,前面已将,这里不再说 interruptIdleWorkers(false); }
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(); } catch (SecurityException ignore) { } finally { w.unlock(); } } if (onlyOne) //如果是只中断一个空闲线程,则结束本次中断空闲线程任务 break; } } finally { mainLock.unlock(); } }
//只中断一个空闲工作线程
private static final boolean ONLY_ONE = true;
4.
//线程池关闭hook
onShutdown(); // hook for ScheduledThreadPoolExecutor /** * Performs any further cleanup following run state transition on * invocation of shutdown. A no-op here, but used by * ScheduledThreadPoolExecutor to cancel delayed tasks. */ void onShutdown() { //待子类扩展 }
我们也把tryTerminate代码贴出来,以便理解:
final void tryTerminate() { //自旋尝试关闭线程池 for (;;) { int c = ctl.get(); //如果线程池正在运行,或正在关闭且任务队列不为空,则返回 if (isRunning(c) || runStateAtLeast(c, TIDYING) || (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty())) return; if (workerCountOf(c) != 0) { // Eligible to terminate //如果工作线程不为空,则中断空闲工作线程 interruptIdleWorkers(ONLY_ONE); return; } final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { //线程池已关闭,任务队列为空,工作线程为0,更新线程池状态为TIDYING if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { try { //执行结束工作 terminated(); } finally { //线程池已结束 ctl.set(ctlOf(TERMINATED, 0)); //唤醒等待线程池结束的线程 termination.signalAll(); } return; } } finally { mainLock.unlock(); } // else retry on failed CAS } }
//执行结束工作
terminated();
/** * Method invoked when the Executor has terminated. Default * implementation does nothing. Note: To properly nest multiple * overridings, subclasses should generally invoke * {@code super.terminated} within this method. */ protected void terminated() { //待子类扩展}
从上面可以看出关闭线程池,首先检查工作线程运行时访问权限,
更新线程状态为SHUTDOWN,中断空闲工作线程,最后尝试关闭线程池。
再看来以及关闭线程池:
/** * Attempts to stop all actively executing tasks, halts the * processing of waiting tasks, and returns a list of the tasks * that were awaiting execution. These tasks are drained (removed) * from the task queue upon return from this method. * 尝试停止正在执行的任务,停止等待任务线程的处理,任务队列将会被排空, 并返回任务队列中的任务集。 这个方法不会等待已执行的任务结束,我们用awaitTermination来等待任务执行完 * <p>This method does not wait for actively executing tasks to * terminate. Use {@link #awaitTermination awaitTermination} to * do that. * * <p>There are no guarantees beyond best-effort attempts to stop * processing actively executing tasks. This implementation * cancels tasks via {@link Thread#interrupt}, so any task that * fails to respond to interrupts may never terminate. * * @throws SecurityException {@inheritDoc} */ public List<Runnable> shutdownNow() { List<Runnable> tasks; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { //检查工作线程权限 checkShutdownAccess(); //更新线程池状态为STOP advanceRunState(STOP); //中断空闲工作线程 interruptWorkers(); //清空任务队列,并放到tasks集合中 tasks = drainQueue(); } finally { mainLock.unlock(); } //尝试结束线程池 tryTerminate(); return tasks; }
来看清空任务队列,并放到tasks集合中
tasks = drainQueue();
/** * Drains the task queue into a new list, normally using * drainTo. But if the queue is a DelayQueue or any other kind of * queue for which poll or drainTo may fail to remove some * elements, it deletes them one by one. */ private List<Runnable> drainQueue() { //这个方法很简单,不再说了 BlockingQueue<Runnable> q = workQueue; List<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; }
立即关闭线程与关闭线程池的不同是,对于关闭线程池,先前提交的任务将会被工作线程执行,新的线程将会被拒绝;而立即关闭线程,尝试停止正在执行的任务,停止等待任务线程的处理,任务队列将会被排空,并返回任务队列中的任务集。这两个方法都不会等待任务执行完或任务结束。
我们可以用awaitTermination来等待任务执行完
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(); } }
来看一些其他的方法:
/** * Invokes {@code shutdown} when this executor is no longer * referenced and it has no threads. */ //线程池不在被应用时,关闭线程池 protected void finalize() { shutdown(); } //创建一个核心空闲工作线程等待任务 /** * Starts a core thread, causing it to idly wait for work. This * overrides the default policy of starting core threads only when * new tasks are executed. This method will return {@code false} * if all core threads have already been started. * * @return {@code true} if a thread was started */ public boolean prestartCoreThread() { //addWorker,core参数为true使用核心线程池数量,否则最大线程池数量 return workerCountOf(ctl.get()) < corePoolSize && addWorker(null, true); } //确保至少有一个空闲工作线程等待任务 /** * Same as prestartCoreThread except arranges that at least one * thread is started even if corePoolSize is 0. */ void ensurePrestart() { int wc = workerCountOf(ctl.get()); if (wc < corePoolSize) addWorker(null, true); else if (wc == 0) addWorker(null, false); } //创建核心线程池数量的空闲工作线程等待任务 /** * Starts all core threads, causing them to idly wait for work. This * overrides the default policy of starting core threads only when * new tasks are executed. * * @return the number of threads started */ public int prestartAllCoreThreads() { int n = 0; while (addWorker(null, true)) ++n; return n; } //尝试移除任务取消的工作线程 /** * Tries to remove from the work queue all {@link Future} * tasks that have been cancelled. This method can be useful as a * storage reclamation operation, that has no other impact on * functionality. Cancelled tasks are never executed, but may * accumulate in work queues until worker threads can actively * remove them. Invoking this method instead tries to remove them now. * However, this method may fail to remove tasks in * the presence of interference by other threads. */ 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 boolean isShutdown() { return ! isRunning(ctl.get()); } private static boolean isRunning(int c) { return c < SHUTDOWN; } //线程池关闭,但还没有完全结束 /** * Returns true if this executor is in the process of terminating * after {@link #shutdown} or {@link #shutdownNow} but has not * completely terminated. This method may be useful for * debugging. A return of {@code true} reported a sufficient * period after shutdown may indicate that submitted tasks have * ignored or suppressed interruption, causing this executor not * to properly terminate. * * @return true if terminating but not yet terminated */ public boolean isTerminating() { int c = ctl.get(); return ! isRunning(c) && runStateLessThan(c, TERMINATED); } //线程池是否结束 public boolean isTerminated() { return runStateAtLeast(ctl.get(), TERMINATED); } //设置工作线程保活时间 /** * Sets the time limit for which threads may remain idle before * being terminated. If there are more than the core number of * threads currently in the pool, after waiting this amount of * time without processing a task, excess threads will be * terminated. This overrides any value set in the constructor. * * @param time the time to wait. A time value of zero will cause * excess threads to terminate immediately after executing tasks. * @param unit the time unit of the {@code time} argument * @throws IllegalArgumentException if {@code time} less than zero or * if {@code time} is zero and {@code allowsCoreThreadTimeOut} * @see #getKeepAliveTime */ public void setKeepAliveTime(long time, TimeUnit unit) { if (time < 0) throw new IllegalArgumentException(); if (time == 0 && allowsCoreThreadTimeOut()) throw new IllegalArgumentException("Core threads must have nonzero keep alive times"); long keepAliveTime = unit.toNanos(time); long delta = keepAliveTime - this.keepAliveTime; this.keepAliveTime = keepAliveTime; if (delta < 0) //如果保证时间小于当前保证时间,中断空闲工作线程 interruptIdleWorkers(); } //运行工作线程超时等待 /** * Sets the policy governing whether core threads may time out and * terminate if no tasks arrive within the keep-alive time, being * replaced if needed when new tasks arrive. When false, core * threads are never terminated due to lack of incoming * tasks. When true, the same keep-alive policy applying to * non-core threads applies also to core threads. To avoid * continual thread replacement, the keep-alive time must be * greater than zero when setting {@code true}. This method * should in general be called before the pool is actively used. * * @param value {@code true} if should time out, else {@code false} * @throws IllegalArgumentException if value is {@code true} * and the current keep-alive time is not greater than zero * * @since 1.6 */ 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(); } } //设置核心线程池数量 /** * Sets the core number of threads. This overrides any value set * in the constructor. If the new value is smaller than the * current value, excess existing threads will be terminated when * they next become idle. If larger, new threads will, if needed, * be started to execute any queued tasks. * * @param corePoolSize the new core size * @throws IllegalArgumentException if {@code corePoolSize < 0} * @see #getCorePoolSize */ 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,中断空闲工作线程 interruptIdleWorkers(); else if (delta > 0) { // We don't really know how many new threads are "needed". // As a heuristic, prestart enough new workers (up to new // core size) to handle the current number of tasks in // queue, but stop if queue becomes empty while doing so. int k = Math.min(delta, workQueue.size()); //如果核心线程池数量增大,且工作线程未达到核心线程池数量,则添加等待执行的任务数量 //和新增核心线程池数量中最小者数量的空闲工作线程 while (k-- > 0 && addWorker(null, true)) { if (workQueue.isEmpty()) break; } } } //设置最大线程池数量 /** * Sets the maximum allowed number of threads. This overrides any * value set in the constructor. If the new value is smaller than * the current value, excess existing threads will be * terminated when they next become idle. * * @param maximumPoolSize the new maximum * @throws IllegalArgumentException if the new maximum is * less than or equal to zero, or * less than the {@linkplain #getCorePoolSize core pool size} * @see #getMaximumPoolSize */ public void setMaximumPoolSize(int maximumPoolSize) { if (maximumPoolSize <= 0 || maximumPoolSize < corePoolSize) throw new IllegalArgumentException(); this.maximumPoolSize = maximumPoolSize; if (workerCountOf(ctl.get()) > maximumPoolSize) //如果当前工作线程数量大于最大线程池数量,则中断空闲工作线程 interruptIdleWorkers(); }
总结:
关闭线程池,首先检查工作线程运行时访问权限,更新线程状态为SHUTDOWN,中断空闲工作线程,最后尝试关闭线程池。立即关闭线程与关闭线程池的不同是,对于关闭线程池,先前提交的任务将会被工作线程执行,新的线程将会被拒绝;而立即关闭线程,尝试停止正在执行的任务,停止等待任务线程的处理,任务队列将会被排空,并返回任务队列中的任务集。这两个方法都不会等待任务执行完或任务结束。我们可以用awaitTermination来等待任务执行完。
发表评论
-
Executors解析
2017-04-07 14:38 1246ThreadPoolExecutor解析一(核心线程池数量、线 ... -
ScheduledThreadPoolExecutor解析三(关闭线程池)
2017-04-06 20:52 4451ScheduledThreadPoolExecutor解析一( ... -
ScheduledThreadPoolExecutor解析二(任务调度)
2017-04-06 12:56 2117ScheduledThreadPoolExecutor解析一( ... -
ScheduledThreadPoolExecutor解析一(调度任务,任务队列)
2017-04-04 22:59 4987Executor接口的定义:http://donald-dra ... -
ThreadPoolExecutor解析三(线程池执行提交任务)
2017-04-03 12:06 6082Executor接口的定义:http://donald-dra ... -
ThreadPoolExecutor解析二(线程工厂、工作线程,拒绝策略等)
2017-04-01 17:12 3036Executor接口的定义:http://donald-dra ... -
ThreadPoolExecutor解析一(核心线程池数量、线程池状态等)
2017-03-31 22:01 20514Executor接口的定义:http://donald-dra ... -
ScheduledExecutorService接口定义
2017-03-29 12:53 1503Executor接口的定义:http://donald-dra ... -
AbstractExecutorService解析
2017-03-29 08:27 1072Executor接口的定义:http: ... -
ExecutorCompletionService解析
2017-03-28 14:27 1586Executor接口的定义:http://donald-dra ... -
CompletionService接口定义
2017-03-28 12:39 1062Executor接口的定义:http://donald-dra ... -
FutureTask解析
2017-03-27 12:59 1325package java.util.concurrent; ... -
Future接口定义
2017-03-26 09:40 1193/* * Written by Doug Lea with ... -
ExecutorService接口定义
2017-03-25 22:14 1159Executor接口的定义:http://donald-dra ... -
Executor接口的定义
2017-03-24 23:24 1672package java.util.concurrent; ... -
简单测试线程池拒绝执行任务策略
2017-03-24 22:37 2025线程池多余任务的拒绝执行策略有四中,分别是直接丢弃任务Disc ... -
JAVA集合类简单综述
2017-03-23 22:51 920Queue接口定义:http://donald-draper. ... -
DelayQueue解析
2017-03-23 11:00 1733Queue接口定义:http://donald-draper. ... -
SynchronousQueue解析下-TransferQueue
2017-03-22 22:20 2133Queue接口定义:http://donald-draper. ... -
SynchronousQueue解析上-TransferStack
2017-03-21 22:08 3048Queue接口定义:http://donald-draper. ...
相关推荐
基于springboot+Javaweb的二手图书交易系统源码数据库文档.zip
Linux课程设计.doc
课程考试资源描述 本资源是为应对各类课程考试而精心准备的综合性学习包。它包含了多门学科的考试指南、历年真题、模拟试题以及详细的答案解析。这些资源旨在帮助学生系统复习课程内容,理解考试要点,提高解题技巧,从而在考试中取得优异成绩。 资源中不仅包含了基础的考试资料,还特别加入了考试技巧讲解和备考策略分析。学生可以通过这些资源了解不同题型的解题方法和思路,学会如何在有限的时间内高效答题。此外,还有针对弱项科目和难点的专项训练,帮助学生攻克学习瓶颈。 为了确保资源的时效性和准确性,我们会定期更新考试资料和模拟试题,及时反映最新的考试动态和趋势。同时,也提供了在线交流平台,方便学生之间互相讨论、分享学习心得。 项目源码示例(简化版,Python) 以下是一个简单的Python脚本示例,用于生成包含选择题和答案的模拟试题: python import random # 定义选择题题库 questions = [ {"question": "Python的创始人是谁?", "options": ["A. 林纳斯·托瓦兹", "B. 巴纳姆", "C. 比尔·盖茨", "D.
基于 MySQL+Django 实现校园食堂点餐系统。 主要环境: PowerDesigner MySQL Workbench 8.0 CE Python 3.8 Django 3.2.8 BootStrap 3.3.7 Django-simpleui
基于SpringBoot的同城宠物照看系统源码数据库文档.zip
GEE训练教程
基于springboot+Web的心理健康交流系统源码数据库文档.zip
微信小程序 kotlin 实践微信插件助手, 目前支持抢红包(支持微信最新版本 7.0.0及7.0.3).zip
N32G45X运放电路检测电压
梦幻西游道人是梦幻西游里面的一个NPC,主要是刷全服最实惠的高级兽决和其他很好用的比较贵的东西,在长安城、傲来国、长寿村中的任意一个场景出现,一般会出现30分钟,不过东西一般都被秒刷。 梦幻西游道人出现时间解析如下: 1.梦幻西游道人出现时间一直都保持着一年出现两次的规律,即2、3月份的元宵节期间来一次,9月份的教师节期间出现一次。 2.云游道人每个整点(0:00至7:00不出现)会在长安城、傲来国、长寿村中的任意一个场景出现,每次出现后停留时间为30分钟。
tables-3.7.0-cp38-cp38-win_amd64.whl
基于springboot旧物回收管理系统源码数据库文档.zip
MariaDB数据库管理系统是MySQL的一个分支,主要由开源社区在维护,采用GPL授权许可 MariaDB的目的是完全兼容MySQL,包括API和命令行,使之能轻松成为MySQL的代替品。在存储引擎方面,使用XtraDB(英语:XtraDB)来代替MySQL的InnoDB。 本文档介绍了MariaDB 10.1的集群部署,至少三台机器做成集群,每台可以同时提供读和写,感兴趣的小伙伴们可以参考一下
内容概要:本文档全面介绍了JavaScript作为一种轻量级的、解释型的语言及其在前端开发中的广泛应用。从JavaScript的基本概念出发,详尽讲解了基础语法(如变量、数据类型、运算符、流程控制)、函数和闭包、对象和原型、DOM操作(如获取、修改、添加和删除元素)、事件处理(如事件监听器、事件对象)、AJAX与Fetch API、ES6+的新特性(如箭头函数、模板字符串、解构赋值)以及前端框架和库(React、Vue、Angular)。除此之外,文章还涉及了代码优化技巧(如减少DOM操作、选择适当的算法和数据结构、使用工具提升代码性能),并对JavaScript的应用场景和发展趋势进行了展望。 适用人群:适用于初学者或具有少量编程经验的学习者,旨在帮助他们系统掌握JavaScript基础知识和前沿技术。 使用场景及目标:通过本教程的学习,读者不仅可以学会基本语法,还能理解并掌握高级概念和技术,如DOM操纵、事件处理机制、异步编程及最新的ECMAScript规范。这不仅有助于改善用户体验、增强网站互动性和响应速度,也能有效提升自身的编码水平和项目开发能力。 其他说明:此文档不仅涵盖了JavaScript的传统功能,还有现代前端技术和最佳实践指导,确保读者能够紧跟行业发展步伐,成为合格甚至优秀的Web开发人员。
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过严格测试运行成功才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。
基于springboot高考志愿智能推荐系统源码数据库文档.zip
经典-FPGA时序约束教程
mcu交互实验整体文件
Collins COBUILD (CN).mdx
自定义springboot starter