diff --git "a/second/week_05/70/Executors\345\237\272\347\241\200\345\205\245\351\227\250\345\255\246\344\271\240\347\254\224\350\256\260.adoc" "b/second/week_05/70/Executors\345\237\272\347\241\200\345\205\245\351\227\250\345\255\246\344\271\240\347\254\224\350\256\260.adoc" new file mode 100644 index 0000000000000000000000000000000000000000..52c22598ab4be29a9cad2ece6b69e275173c26bb --- /dev/null +++ "b/second/week_05/70/Executors\345\237\272\347\241\200\345\205\245\351\227\250\345\255\246\344\271\240\347\254\224\350\256\260.adoc" @@ -0,0 +1,145 @@ += Executors基础入门学习笔记 +Doc Writer +v1.0, 2020-04-05 +:toc-title: 目录 +:toc: left + +== 基本要求 +=== 源码学习要求 +- 至少提交2个类的源码分析笔记 + +=== jdk环境要求 +jdk1.8 + +=== 学习后需要至少掌握的知识点 +- Executors与ThreadPoolExecutor的区别 + +=== Executors的newFixedThreadPool方法 +[souce,adoc] +---- + /** + * Creates a thread pool that reuses a fixed number of threads + * operating off a shared unbounded queue. At any point, at most + * {@code nThreads} threads will be active processing tasks. + * If additional tasks are submitted when all threads are active, + * they will wait in the queue until a thread is available. + * If any thread terminates due to a failure during execution + * prior to shutdown, a new one will take its place if needed to + * execute subsequent tasks. The threads in the pool will exist + * until it is explicitly {@link ExecutorService#shutdown shutdown}. + * + * @param nThreads the number of threads in the pool + * @return the newly created thread pool + * @throws IllegalArgumentException if {@code nThreads <= 0} + */ + public static ExecutorService newFixedThreadPool(int nThreads) { + return new ThreadPoolExecutor(nThreads, nThreads, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue()); + } +---- + +=== Executors的newCachedThreadPool方法 +[souce,adoc] +---- + /** + * Creates a thread pool that creates new threads as needed, but + * will reuse previously constructed threads when they are + * available. These pools will typically improve the performance + * of programs that execute many short-lived asynchronous tasks. + * Calls to {@code execute} will reuse previously constructed + * threads if available. If no existing thread is available, a new + * thread will be created and added to the pool. Threads that have + * not been used for sixty seconds are terminated and removed from + * the cache. Thus, a pool that remains idle for long enough will + * not consume any resources. Note that pools with similar + * properties but different details (for example, timeout parameters) + * may be created using {@link ThreadPoolExecutor} constructors. + * + * @return the newly created thread pool + */ + public static ExecutorService newCachedThreadPool() { + return new ThreadPoolExecutor(0, Integer.MAX_VALUE, + 60L, TimeUnit.SECONDS, + new SynchronousQueue()); + } +---- + +=== Executors的newSingleThreadExecutor方法 +[souce,adoc] +---- + /** + * Creates an Executor that uses a single worker thread operating + * off an unbounded queue. (Note however that if this single + * thread terminates due to a failure during execution prior to + * shutdown, a new one will take its place if needed to execute + * subsequent tasks.) Tasks are guaranteed to execute + * sequentially, and no more than one task will be active at any + * given time. Unlike the otherwise equivalent + * {@code newFixedThreadPool(1)} the returned executor is + * guaranteed not to be reconfigurable to use additional threads. + * + * @return the newly created single-threaded Executor + */ + public static ExecutorService newSingleThreadExecutor() { + return new FinalizableDelegatedExecutorService + (new ThreadPoolExecutor(1, 1, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue())); + } + + static class FinalizableDelegatedExecutorService + extends DelegatedExecutorService { + FinalizableDelegatedExecutorService(ExecutorService executor) { + super(executor); + } + protected void finalize() { + super.shutdown(); + } + } + + /** + * A wrapper class that exposes only the ExecutorService methods + * of an ExecutorService implementation. + */ + static class DelegatedExecutorService extends AbstractExecutorService { + private final ExecutorService e; + DelegatedExecutorService(ExecutorService executor) { e = executor; } + public void execute(Runnable command) { e.execute(command); } + public void shutdown() { e.shutdown(); } + public List shutdownNow() { return e.shutdownNow(); } + public boolean isShutdown() { return e.isShutdown(); } + public boolean isTerminated() { return e.isTerminated(); } + public boolean awaitTermination(long timeout, TimeUnit unit) + throws InterruptedException { + return e.awaitTermination(timeout, unit); + } + public Future submit(Runnable task) { + return e.submit(task); + } + public Future submit(Callable task) { + return e.submit(task); + } + public Future submit(Runnable task, T result) { + return e.submit(task, result); + } + public List> invokeAll(Collection> tasks) + throws InterruptedException { + return e.invokeAll(tasks); + } + public List> invokeAll(Collection> tasks, + long timeout, TimeUnit unit) + throws InterruptedException { + return e.invokeAll(tasks, timeout, unit); + } + public T invokeAny(Collection> tasks) + throws InterruptedException, ExecutionException { + return e.invokeAny(tasks); + } + public T invokeAny(Collection> tasks, + long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + return e.invokeAny(tasks, timeout, unit); + } + } +---- diff --git "a/second/week_05/70/ThreadLocal\345\237\272\347\241\200\345\205\245\351\227\250\345\255\246\344\271\240\347\254\224\350\256\260.adoc" "b/second/week_05/70/ThreadLocal\345\237\272\347\241\200\345\205\245\351\227\250\345\255\246\344\271\240\347\254\224\350\256\260.adoc" new file mode 100644 index 0000000000000000000000000000000000000000..314471e1cfbf54bd83f5223da9f6eb6fea9db699 --- /dev/null +++ "b/second/week_05/70/ThreadLocal\345\237\272\347\241\200\345\205\245\351\227\250\345\255\246\344\271\240\347\254\224\350\256\260.adoc" @@ -0,0 +1,263 @@ += ThreadLocal基础入门学习笔记 +Doc Writer +v1.0, 2020-04-05 +:toc-title: 目录 +:toc: left + +== 基本要求 +=== 源码学习要求 +- 至少提交2个类的源码分析笔记 + +=== jdk环境要求 +jdk1.8 + +=== 学习后需要至少掌握的知识点 +- ThreadLocal的结构 +- ThreadLocal的引起内存泄漏的原因? + +=== ThreadLocal的构造方法 +[souce,java] +---- + /** + * Creates a thread local variable. + * @see #withInitial(java.util.function.Supplier) + */ + public ThreadLocal() { + } + +---- + +=== ThreadLocal的set方法 +[souce,java] +---- + + /** + * The entries in this hash map extend WeakReference, using + * its main ref field as the key (which is always a + * ThreadLocal object). Note that null keys (i.e. entry.get() + * == null) mean that the key is no longer referenced, so the + * entry can be expunged from table. Such entries are referred to + * as "stale entries" in the code that follows. + */ + static class Entry extends WeakReference> { + /** The value associated with this ThreadLocal. */ + Object value; + + Entry(ThreadLocal k, Object v) { + super(k); + value = v; + } + } + + + /** + * Sets the current thread's copy of this thread-local variable + * to the specified value. Most subclasses will have no need to + * override this method, relying solely on the {@link #initialValue} + * method to set the values of thread-locals. + * + * @param value the value to be stored in the current thread's copy of + * this thread-local. + */ + public void set(T value) { + Thread t = Thread.currentThread(); + ThreadLocalMap map = getMap(t); + if (map != null) + map.set(this, value); + else + createMap(t, value); + } + + + /** + * Get the map associated with a ThreadLocal. Overridden in + * InheritableThreadLocal. + * + * @param t the current thread + * @return the map + */ + ThreadLocalMap getMap(Thread t) { + return t.threadLocals; + } + + /** + * Set the value associated with key. + * + * @param key the thread local object + * @param value the value to be set + */ + private void set(ThreadLocal key, Object value) { + + // We don't use a fast path as with get() because it is at + // least as common to use set() to create new entries as + // it is to replace existing ones, in which case, a fast + // path would fail more often than not. + + Entry[] tab = table; + int len = tab.length; + int i = key.threadLocalHashCode & (len-1); + + for (Entry e = tab[i]; + e != null; + e = tab[i = nextIndex(i, len)]) { + ThreadLocal k = e.get(); + + if (k == key) { + e.value = value; + return; + } + + if (k == null) { + replaceStaleEntry(key, value, i); + return; + } + } + + tab[i] = new Entry(key, value); + int sz = ++size; + if (!cleanSomeSlots(i, sz) && sz >= threshold) + rehash(); + } + + /** + * Create the map associated with a ThreadLocal. Overridden in + * InheritableThreadLocal. + * + * @param t the current thread + * @param firstValue value for the initial entry of the map + */ + void createMap(Thread t, T firstValue) { + t.threadLocals = new ThreadLocalMap(this, firstValue); + } + + /** + * Construct a new map initially containing (firstKey, firstValue). + * ThreadLocalMaps are constructed lazily, so we only create + * one when we have at least one entry to put in it. + */ + ThreadLocalMap(ThreadLocal firstKey, Object firstValue) { + table = new Entry[INITIAL_CAPACITY]; + int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); + table[i] = new Entry(firstKey, firstValue); + size = 1;2 + setThreshold(INITIAL_CAPACITY); + } + + /** + * The initial capacity -- MUST be a power of two. + */ + private static final int INITIAL_CAPACITY = 16; +---- + +=== ThreadLocal的get方法 +[souce,adoc] +---- + /** + * Returns the value in the current thread's copy of this + * thread-local variable. If the variable has no value for the + * current thread, it is first initialized to the value returned + * by an invocation of the {@link #initialValue} method. + * + * @return the current thread's value of this thread-local + */ + public T get() { + Thread t = Thread.currentThread(); + ThreadLocalMap map = getMap(t); + if (map != null) { + ThreadLocalMap.Entry e = map.getEntry(this); + if (e != null) { + @SuppressWarnings("unchecked") + T result = (T)e.value; + return result; + } + } + return setInitialValue(); + } +---- + +=== ThreadLocal的remove方法 +[souce,adoc] +---- + /** + * Removes the current thread's value for this thread-local + * variable. If this thread-local variable is subsequently + * {@linkplain #get read} by the current thread, its value will be + * reinitialized by invoking its {@link #initialValue} method, + * unless its value is {@linkplain #set set} by the current thread + * in the interim. This may result in multiple invocations of the + * {@code initialValue} method in the current thread. + * + * @since 1.5 + */ + public void remove() { + ThreadLocalMap m = getMap(Thread.currentThread()); + if (m != null) + m.remove(this); + } + + + /** + * Remove the entry for key. + */ + private void remove(ThreadLocal key) { + Entry[] tab = table; + int len = tab.length; + int i = key.threadLocalHashCode & (len-1); + for (Entry e = tab[i]; + e != null; + e = tab[i = nextIndex(i, len)]) { + if (e.get() == key) { + e.clear(); + expungeStaleEntry(i); + return; + } + } + } + + /** + * Expunge a stale entry by rehashing any possibly colliding entries + * lying between staleSlot and the next null slot. This also expunges + * any other stale entries encountered before the trailing null. See + * Knuth, Section 6.4 + * + * @param staleSlot index of slot known to have null key + * @return the index of the next null slot after staleSlot + * (all between staleSlot and this slot will have been checked + * for expunging). + */ + private int expungeStaleEntry(int staleSlot) { + Entry[] tab = table; + int len = tab.length; + + // expunge entry at staleSlot + tab[staleSlot].value = null; + tab[staleSlot] = null; + size--; + + // Rehash until we encounter null + Entry e; + int i; + for (i = nextIndex(staleSlot, len); + (e = tab[i]) != null; + i = nextIndex(i, len)) { + ThreadLocal k = e.get(); + if (k == null) { + e.value = null; + tab[i] = null; + size--; + } else { + int h = k.threadLocalHashCode & (len - 1); + if (h != i) { + tab[i] = null; + + // Unlike Knuth 6.4 Algorithm R, we must scan until + // null because multiple entries could have been stale. + while (tab[h] != null) + h = nextIndex(h, len); + tab[h] = e; + } + } + } + return i; + } +---- \ No newline at end of file diff --git "a/second/week_05/70/ThreadPoolExecutor\345\237\272\347\241\200\345\205\245\351\227\250\345\255\246\344\271\240\347\254\224\350\256\260.adoc" "b/second/week_05/70/ThreadPoolExecutor\345\237\272\347\241\200\345\205\245\351\227\250\345\255\246\344\271\240\347\254\224\350\256\260.adoc" new file mode 100644 index 0000000000000000000000000000000000000000..33cd931b79dc53b55c312646d0e9a0ba73137c71 --- /dev/null +++ "b/second/week_05/70/ThreadPoolExecutor\345\237\272\347\241\200\345\205\245\351\227\250\345\255\246\344\271\240\347\254\224\350\256\260.adoc" @@ -0,0 +1,268 @@ += ThreadPoolExecutor基础入门学习笔记 +Doc Writer +v1.0, 2020-04-05 +:toc-title: 目录 +:toc: left + +== 基本要求 +=== 源码学习要求 +- 至少提交2个类的源码分析笔记 + +=== jdk环境要求 +jdk1.8 + +=== 学习后需要至少掌握的知识点 +- ThreadPoolExecutor主要流程 +- ThreadPoolExecutor中submit与execute的主要区别? https://blog.csdn.net/puhaiyang/article/details/103715798[java线程池ThreadPoolExecutor通过submit提交runnable的task时,为何不会抛出oom] + +=== ThreadPoolExecutor的构造方法1 +[source,adoc] +---- + /** + * Creates a new {@code ThreadPoolExecutor} with the given initial + * parameters and default thread factory and rejected execution handler. + * It may be more convenient to use one of the {@link Executors} factory + * methods instead of this general purpose constructor. + * + * @param corePoolSize the number of threads to keep in the pool, even + * if they are idle, unless {@code allowCoreThreadTimeOut} is set + * @param maximumPoolSize the maximum number of threads to allow in the + * pool + * @param keepAliveTime when the number of threads is greater than + * the core, this is the maximum time that excess idle threads + * will wait for new tasks before terminating. + * @param unit the time unit for the {@code keepAliveTime} argument + * @param workQueue the queue to use for holding tasks before they are + * executed. This queue will hold only the {@code Runnable} + * tasks submitted by the {@code execute} method. + * @throws IllegalArgumentException if one of the following holds:
+ * {@code corePoolSize < 0}
+ * {@code keepAliveTime < 0}
+ * {@code maximumPoolSize <= 0}
+ * {@code maximumPoolSize < corePoolSize} + * @throws NullPointerException if {@code workQueue} is null + */ + public ThreadPoolExecutor(int corePoolSize, + int maximumPoolSize, + long keepAliveTime, + TimeUnit unit, + BlockingQueue workQueue) { + this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, + Executors.defaultThreadFactory(), defaultHandler); + } + + /** + * The default rejected execution handler + */ + private static final RejectedExecutionHandler defaultHandler = + new AbortPolicy(); +---- + +=== ThreadPoolExecutor的构造方法2 +[source,adoc] +---- + /** + * Creates a new {@code ThreadPoolExecutor} with the given initial + * parameters. + * + * @param corePoolSize the number of threads to keep in the pool, even + * if they are idle, unless {@code allowCoreThreadTimeOut} is set + * @param maximumPoolSize the maximum number of threads to allow in the + * pool + * @param keepAliveTime when the number of threads is greater than + * the core, this is the maximum time that excess idle threads + * will wait for new tasks before terminating. + * @param unit the time unit for the {@code keepAliveTime} argument + * @param workQueue the queue to use for holding tasks before they are + * executed. This queue will hold only the {@code Runnable} + * tasks submitted by the {@code execute} method. + * @param threadFactory the factory to use when the executor + * creates a new thread + * @param handler the handler to use when execution is blocked + * because the thread bounds and queue capacities are reached + * @throws IllegalArgumentException if one of the following holds:
+ * {@code corePoolSize < 0}
+ * {@code keepAliveTime < 0}
+ * {@code maximumPoolSize <= 0}
+ * {@code maximumPoolSize < corePoolSize} + * @throws NullPointerException if {@code workQueue} + * or {@code threadFactory} or {@code handler} is null + */ + public ThreadPoolExecutor(int corePoolSize, + int maximumPoolSize, + long keepAliveTime, + TimeUnit unit, + BlockingQueue 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.corePoolSize = corePoolSize; + this.maximumPoolSize = maximumPoolSize; + this.workQueue = workQueue; + this.keepAliveTime = unit.toNanos(keepAliveTime); + this.threadFactory = threadFactory; + this.handler = handler; + } +---- + +=== ThreadPoolExecutor中的execute方法 +[souce,adoc] +---- + /** + * Executes the given task sometime in the future. The task + * may execute in a new thread or in an existing pooled thread. + * + * If the task cannot be submitted for execution, either because this + * executor has been shutdown or because its capacity has been reached, + * the task is handled by the current {@code RejectedExecutionHandler}. + * + * @param command the task to execute + * @throws RejectedExecutionException at discretion of + * {@code RejectedExecutionHandler}, if the task + * cannot be accepted for execution + * @throws NullPointerException if {@code command} is null + */ + public void execute(Runnable command) { + if (command == null) + throw new NullPointerException(); + /* + * Proceed in 3 steps: + * + * 1. If fewer than corePoolSize threads are running, try to + * start a new thread with the given command as its first + * task. The call to addWorker atomically checks runState and + * workerCount, and so prevents false alarms that would add + * threads when it shouldn't, by returning false. + * + * 2. If a task can be successfully queued, then we still need + * to double-check whether we should have added a thread + * (because existing ones died since last checking) or that + * the pool shut down since entry into this method. So we + * recheck state and if necessary roll back the enqueuing if + * stopped, or start a new thread if there are none. + * + * 3. If we cannot queue task, then we try to add a new + * thread. If it fails, we know we are shut down or saturated + * and so reject the task. + */ + int c = ctl.get(); + if (workerCountOf(c) < corePoolSize) { + if (addWorker(command, true)) + return; + c = ctl.get(); + } + if (isRunning(c) && workQueue.offer(command)) { + int recheck = ctl.get(); + if (! isRunning(recheck) && remove(command)) + reject(command); + else if (workerCountOf(recheck) == 0) + addWorker(null, false); + } + else if (!addWorker(command, false)) + reject(command); + } + + + /* + * Methods for creating, running and cleaning up after workers + */ + + /** + * Checks if a new worker can be added with respect to current + * pool state and the given bound (either core or maximum). If so, + * the worker count is adjusted accordingly, and, if possible, a + * new worker is created and started, running firstTask as its + * first task. This method returns false if the pool is stopped or + * eligible to shut down. It also returns false if the thread + * factory fails to create a thread when asked. If the thread + * creation fails, either due to the thread factory returning + * null, or due to an exception (typically OutOfMemoryError in + * Thread.start()), we roll back cleanly. + * + * @param firstTask the task the new thread should run first (or + * null if none). Workers are created with an initial first task + * (in method execute()) to bypass queuing when there are fewer + * than corePoolSize threads (in which case we always start one), + * or when the queue is full (in which case we must bypass queue). + * Initially idle threads are usually created via + * prestartCoreThread or to replace other dying workers. + * + * @param core if true use corePoolSize as bound, else + * maximumPoolSize. (A boolean indicator is used here rather than a + * value to ensure reads of fresh values after checking other pool + * state). + * @return true if successful + */ + private boolean addWorker(Runnable firstTask, boolean core) { + retry: + for (;;) { + int c = ctl.get(); + int rs = runStateOf(c); + + // Check if queue empty only if necessary. + if (rs >= SHUTDOWN && + ! (rs == SHUTDOWN && + firstTask == null && + ! workQueue.isEmpty())) + return false; + + for (;;) { + int wc = workerCountOf(c); + if (wc >= CAPACITY || + wc >= (core ? corePoolSize : maximumPoolSize)) + return false; + if (compareAndIncrementWorkerCount(c)) + break retry; + c = ctl.get(); // Re-read ctl + if (runStateOf(c) != rs) + 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); + 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)) { + if (t.isAlive()) // precheck that t is startable + 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; + } +---- \ No newline at end of file diff --git "a/second/week_05/70/Thread\345\237\272\347\241\200\345\205\245\351\227\250\345\255\246\344\271\240\347\254\224\350\256\260.adoc" "b/second/week_05/70/Thread\345\237\272\347\241\200\345\205\245\351\227\250\345\255\246\344\271\240\347\254\224\350\256\260.adoc" new file mode 100644 index 0000000000000000000000000000000000000000..1912b662205ba7b9638ccf6fad1734f50997c41e --- /dev/null +++ "b/second/week_05/70/Thread\345\237\272\347\241\200\345\205\245\351\227\250\345\255\246\344\271\240\347\254\224\350\256\260.adoc" @@ -0,0 +1,235 @@ += Thread基础入门学习笔记 +Doc Writer +v1.0, 2020-04-05 +:toc-title: 目录 +:toc: left + +== 基本要求 +=== 源码学习要求 +- 至少提交2个类的源码分析笔记 + +=== jdk环境要求 +jdk1.8 + +=== 学习后需要至少掌握的知识点 +- Thread基础用法 + +=== Thread类的构造方法 +[souce,java] +---- + /** + * Allocates a new {@code Thread} object. This constructor has the same + * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread} + * {@code (null, null, gname)}, where {@code gname} is a newly generated + * name. Automatically generated names are of the form + * {@code "Thread-"+}n, where n is an integer. + */ + public Thread() { + init(null, null, "Thread-" + nextThreadNum(), 0); + } + + /** + * Initializes a Thread with the current AccessControlContext. + * @see #init(ThreadGroup,Runnable,String,long,AccessControlContext) + */ + private void init(ThreadGroup g, Runnable target, String name, + long stackSize) { + init(g, target, name, stackSize, null); + } + + /** + * Initializes a Thread. + * + * @param g the Thread group + * @param target the object whose run() method gets called + * @param name the name of the new Thread + * @param stackSize the desired stack size for the new thread, or + * zero to indicate that this parameter is to be ignored. + * @param acc the AccessControlContext to inherit, or + * AccessController.getContext() if null + */ + private void init(ThreadGroup g, Runnable target, String name, + long stackSize, AccessControlContext acc) { + if (name == null) { + throw new NullPointerException("name cannot be null"); + } + + this.name = name.toCharArray(); + + Thread parent = currentThread(); + SecurityManager security = System.getSecurityManager(); + if (g == null) { + /* Determine if it's an applet or not */ + + /* If there is a security manager, ask the security manager + what to do. */ + if (security != null) { + g = security.getThreadGroup(); + } + + /* If the security doesn't have a strong opinion of the matter + use the parent thread group. */ + if (g == null) { + g = parent.getThreadGroup(); + } + } + + /* checkAccess regardless of whether or not threadgroup is + explicitly passed in. */ + g.checkAccess(); + + /* + * Do we have the required permissions? + */ + if (security != null) { + if (isCCLOverridden(getClass())) { + security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); + } + } + + g.addUnstarted(); + + this.group = g; + this.daemon = parent.isDaemon(); + this.priority = parent.getPriority(); + if (security == null || isCCLOverridden(parent.getClass())) + this.contextClassLoader = parent.getContextClassLoader(); + else + this.contextClassLoader = parent.contextClassLoader; + this.inheritedAccessControlContext = + acc != null ? acc : AccessController.getContext(); + this.target = target; + setPriority(priority); + if (parent.inheritableThreadLocals != null) + this.inheritableThreadLocals = + ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); + /* Stash the specified stack size in case the VM cares */ + this.stackSize = stackSize; + + /* Set thread ID */ + tid = nextThreadID(); + } +---- + +=== Thread的start方法 +[souce,adoc] +---- + /** + * Causes this thread to begin execution; the Java Virtual Machine + * calls the run method of this thread. + *

+ * The result is that two threads are running concurrently: the + * current thread (which returns from the call to the + * start method) and the other thread (which executes its + * run method). + *

+ * 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 */ + } + } + } +---- + +=== thread的join方法 +[souce,adoc] +---- + /** + * Waits for this thread to die. + * + *

An invocation of this method behaves in exactly the same + * way as the invocation + * + *

+ * {@linkplain #join(long) join}{@code (0)} + *
+ * + * @throws InterruptedException + * if any thread has interrupted the current thread. The + * interrupted status of the current thread is + * cleared when this exception is thrown. + */ + public final void join() throws InterruptedException { + join(0); + } + + /** + * Waits at most {@code millis} milliseconds for this thread to + * die. A timeout of {@code 0} means to wait forever. + * + *

This implementation uses a loop of {@code this.wait} calls + * conditioned on {@code this.isAlive}. As a thread terminates the + * {@code this.notifyAll} method is invoked. It is recommended that + * applications not use {@code wait}, {@code notify}, or + * {@code notifyAll} on {@code Thread} instances. + * + * @param millis + * the time to wait in milliseconds + * + * @throws IllegalArgumentException + * if the value of {@code millis} is negative + * + * @throws InterruptedException + * if any thread has interrupted the current thread. The + * interrupted status of the current thread is + * cleared when this exception is thrown. + */ + public final synchronized void join(long millis) + throws InterruptedException { + long base = System.currentTimeMillis(); + long now = 0; + + if (millis < 0) { + throw new IllegalArgumentException("timeout value is negative"); + } + + if (millis == 0) { + while (isAlive()) { + wait(0); + } + } else { + while (isAlive()) { + long delay = millis - now; + if (delay <= 0) { + break; + } + wait(delay); + now = System.currentTimeMillis() - base; + } + } + } +---- \ No newline at end of file diff --git a/second/week_05/70/leetCode.adoc b/second/week_05/70/leetCode.adoc new file mode 100644 index 0000000000000000000000000000000000000000..2ea6dd31c1bbbdf1744e80500998ce4099b0a9dc --- /dev/null +++ b/second/week_05/70/leetCode.adoc @@ -0,0 +1,59 @@ +https://leetcode-cn.com/problems/print-foobar-alternately/[1115. 交替打印FooBar] +[souce,java] +---- +class FooBar { + private int n; + private Semaphore semaphore1 = new Semaphore(1); + private Semaphore semaphore2 = new Semaphore(0); + public FooBar(int n) { + this.n = n; + } + + public void foo(Runnable printFoo) throws InterruptedException { + + for (int i = 0; i < n; i++) { + semaphore1.acquire(); + // printFoo.run() outputs "foo". Do not change or remove this line. + printFoo.run(); + semaphore2.release(); + } + } + + public void bar(Runnable printBar) throws InterruptedException { + + for (int i = 0; i < n; i++) { + semaphore2.acquire(); + // printBar.run() outputs "bar". Do not change or remove this line. + printBar.run(); + semaphore1.release(); + } + } +} +---- + +https://leetcode-cn.com/problems/building-h2o/[1117. H2O 生成] +[souce,java] +---- +class H2O { + private Semaphore semaphoreH = new Semaphore(2); + private Semaphore semaphoreO = new Semaphore(0); + + public H2O() { + + } + + public void hydrogen(Runnable releaseHydrogen) throws InterruptedException { + semaphoreH.acquire(); + // releaseHydrogen.run() outputs "H". Do not change or remove this line. + releaseHydrogen.run(); + semaphoreO.release(); + } + + public void oxygen(Runnable releaseOxygen) throws InterruptedException { + semaphoreO.acquire(2); + // releaseOxygen.run() outputs "O". Do not change or remove this line. + releaseOxygen.run(); + semaphoreH.release(2); + } +} +---- \ No newline at end of file