java.util.concurrent java.util.concurrent.atomic java.util.concurrent.locks
concurrent
a. 并存的;同时发生的
https://docs.oracle.com/javase/8/docs/api/
普通的线程 Thread。 Runnable。 没有返回值,效率相比 Callable 相对较低。2 个。main 和 gc。
理解。一个进程 Typora。输入 + 自动保存(线程负责的)(两个线程)。
对于 Java 而言,Java 不能直接开启线程。而是 native 方法让 C++ 操作底层系统开启线程。Java 不能操作硬件。
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();并发编程,并发 & 并行。
并发(多线程操作同一资源)。CPU 一核,模拟出来多条线程,天下武功,唯快不破,快速交替。
并行(多个人一起行走)。CPU 多核,多个可线程以同时执行。
package com.geek.demo00; /** * @author geek */ public class Test01 { public static void main(String[] args) { // 获取 CPU 核数。 int availableProcessors = Runtime.getRuntime().availableProcessors(); System.out.println(availableProcessors);// 8 } }wait(); ~ Object。 sleep(); ~ Thread。
import java.util.concurrent.TimeUnit; try { TimeUnit.DAYS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } 锁的释放。wait(); 会释放锁。 sleep(); 睡觉,抱着锁睡觉,不释放。企业中一般不用。
使用的范围不同。sleep(); 可以在任何地方睡。 wait(); 必须在同步代码块中。
是否需要捕获异常。 ×××wait(); 不需要。 sleep(); 需要。
实现类。
ReentrantLock ~ 可重入锁。ReentrantLockWriteLock.ReadLock ~ 读锁。ReentrantLockWriteLock.WriteLock ~ 写锁。 ReentrantLock。(常用)。公平锁(先来后到) & 非公平锁(可以插队,默认)。
/** * Creates an instance of {@code ReentrantLock}. * This is equivalent to using {@code ReentrantLock(false)}. */ public ReentrantLock() { sync = new NonfairSync(); } /** * Creates an instance of {@code ReentrantLock} with the * given fairness policy. * * @param fair {@code true} if this lock should use a fair ordering policy */ public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }synchronized Java 内置关键字。 Lock 是一个 Java 类。
synchronized 无法判断获取锁的状态。 Lock 可以判断是否获取到了锁。
synchronized 会自动释放锁。 Lock 必须手动释放锁。如果不释放锁会死锁。
synchronized 线程 1 (获得锁,阻塞),线程 2 (等待,傻傻的等)。 Lock 锁就不会一直等待下去。lock.tryLock();。
synchronized 可重入锁,不可以中断,非公平。 Lock 可重入锁,可以判断锁,非公平(可以自己设置)。
synchronized 适合锁少量的代码同步问题。 Lock 适合锁大量的同步代码。
原因。
if 只有一次判断,并且 notifyAll();。
if 判断改为 while() {}。
package com.geek.pc; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @author geek */ public class B { public static void main(String[] args) { Data2 data2 = new Data2(); new Thread(() -> { for (int i = 0; i < 10; i++) { data2.increment(); } }, "A").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { data2.decrement(); } }, "B").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { data2.increment(); } }, "C").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { data2.decrement(); } }, "D").start(); } } /** * 资源类。数字。 */ // 判断等待 + `业务` + 通知。 class Data2 { Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); // condition.await();// 等待。 // condition.signalAll();// 唤醒全部。 // 保持 number 为 0. private int number = 0; // +1。 public void increment() { lock.lock(); // 业务代码。 try { // if (number != 0) { while (number != 0) { // 等待。 condition.await(); } number++;// number 为 0,才 +1。 System.out.println(Thread.currentThread().getName() + " => " + number); // 通知其他线程,我 +1 完毕了。 condition.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } // -1。 public void decrement() { lock.lock(); // 业务代码。 try { // if (number == 0) { while (number == 0) { // 等待。 condition.await(); } number--;// number 不为 0,才 -1。 System.out.println(Thread.currentThread().getName() + " => " + number); // 通知其他线程,我 -1 完毕了。 condition.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }eg. 生产线:下单 -> 支付 -> 交易 -> 物流。
package com.geek.pc; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * A 执行完调用 B。 * B 执行完调用 C。 * C 执行完调用 A。 */ public class C { public static void main(String[] args) { Data3 data3 = new Data3(); new Thread(() -> { for (int i = 0; i < 10; i++) { data3.printA(); } }, "A").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { data3.printB(); } }, "B").start(); new Thread(() -> { for (int i = 0; i < 10; i++) { data3.printC(); } }, "C").start(); } } /** * 资源类 Lock。 */ class Data3 { private Lock lock = new ReentrantLock(); private Condition condition1 = lock.newCondition(); private Condition condition2 = lock.newCondition(); private Condition condition3 = lock.newCondition(); private int number = 1;// 1 ~ A 2 ~ B 3 ~ C。 public void printA() { lock.lock(); try { // 业务,判断,执行,通知。 while (number != 1) { // 等待。 condition1.await(); } System.out.println(Thread.currentThread().getName() + " ~ AAAAA"); // 唤醒。唤醒指定的人 ~ B。 number = 2; condition2.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printB() { lock.lock(); try { // 业务,判断,执行,通知。 while (number != 2) { // 等待。 condition2.await(); } System.out.println(Thread.currentThread().getName() + " ~ BBBBB"); // 唤醒。唤醒指定的人 ~ C。 number = 3; condition3.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printC() { lock.lock(); try { // 业务,判断,执行,通知。 while (number != 3) { // 等待。 condition3.await(); } System.out.println(Thread.currentThread().getName() + " ~ CCCCC"); // 唤醒。唤醒指定的人 ~ A。 number = 1; condition1.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }Set 本质就是 map,key 是不重复的。
/** * Adds the specified element to this set if it is not already present. * More formally, adds the specified element <tt>e</tt> to this set if * this set contains no element <tt>e2</tt> such that * <tt>(e==null ? e2==null : e.equals(e2))</tt>. * If this set already contains the element, the call leaves the set * unchanged and returns <tt>false</tt>. * * @param e element to be added to this set * @return <tt>true</tt> if this set did not already contain the specified * element */ public boolean add(E e) { return map.put(e, PRESENT)==null; } // Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object();可以有返回值。
可以抛出异常。
方法不同。run(); / call();。
package com.geek.callable; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class CallableTest { public static void main(String[] args) throws ExecutionException, InterruptedException { // new Thread(new Runnable() { // @Override // public void run() { // // } // }).start(); // new Thread(new FutureTask<V>()).start(); // new Thread(new FutureTask<V>(Callable<V>)).start(); MyThread myThread = new MyThread(); // 适配类。 FutureTask futureTask = new FutureTask(myThread); new Thread(futureTask, "A").start(); new Thread(futureTask, "B").start();// 结果会被缓存。效率高。 // 获取 Callable 返回结果。 Object o = futureTask.get();// get(); 方法可能会阻塞。把 ta 放到最后或使用异步处理。 System.out.println("o = " + o); } } class MyThread implements Callable<Integer> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ @Override // public Object call() throws Exception { public Integer call() throws Exception { // return null; return 1024; } }
latch
n. 门闩;插销;碰锁;弹簧锁 v. 用插销插上;用碰锁锁上
package com.geek.add; import java.util.concurrent.CountDownLatch; /** * countDown(); 数量 -1。 * 就为 0,countDownLatch.await(); 被唤醒,继续以下代码。 */ public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { // 总数是 6。 CountDownLatch countDownLatch = new CountDownLatch(6); for (int i = 0; i < 6; i++) { new Thread(() -> { System.out.println(Thread.currentThread().getName() + " went out."); countDownLatch.countDown();// -1。 }, String.valueOf(i)).start(); } // 等待计数器归零,再向下执行。 countDownLatch.await(); System.out.println("Door closed."); } } /* 0 went out. 3 went out. 1 went out. 2 went out. 5 went out. 4 went out. Door closed. Process finished with exit code 0 */理解:加法计数器。
cyclic
a. 循环的;周期的
barrier
n. 屏障;障碍物;障碍;阻力;关卡;分界线;隔阂
package com.geek.add; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; public class CyclicBarrierDemo { public static void main(String[] args) { // 集齐 7 颗龙珠召唤神龙。 CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> System.out.println("召唤神龙成功。")); for (int i = 0; i < 7; i++) { final int temp = i; new Thread(() -> { System.out.println(Thread.currentThread().getName() + "收集 " + temp + " 龙珠。"); try { cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }).start(); } } }n. 信号标;旗语 v. 打旗语;(用其他类似的信号系统)发信号
package com.geek.add; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; public class SemaphoreDemo { public static void main(String[] args) { // 参数:线程数量。停车位。 Semaphore semaphore = new Semaphore(3); for (int i = 0; i < 6; i++) { new Thread(() -> { try { semaphore.acquire(); System.out.println(Thread.currentThread().getName() + " 抢到车位。"); TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName() + " 离开车位。"); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } }, String.valueOf(i)).start(); } } }public interface ReadWriteLock A ReadWriteLock maintains a pair of associated locks, one for read-only operations and one for writing. The read lock may be held simultaneously by multiple reader threads, so long as there are no writers. The write lock is exclusive.
读可以被多线程同时读,写只能一个线程去写。
package com.geek.rw; import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * 独占锁(写锁)~ 一次只能被一个线程占有。 * 共享锁(读锁)~ 一次可以被多个线程占有。 * ReadWriteLock * 读 ~ 读 可以共存。 * 读 ~ 写 不能共存。 * 写 ~ 写 不能共存。 */ public class ReadWriteLockDemo { public static void main(String[] args) { MyCacheLock myCacheLock = new MyCacheLock(); // 写入。 for (int i = 0; i < 5; i++) { int temp = i; new Thread(() -> myCacheLock.put(temp + "", temp + ""), String.valueOf(i)).start(); } // 读取。 for (int i = 0; i < 5; i++) { int temp = i; new Thread(() -> myCacheLock.get(temp + ""), String.valueOf(i)).start(); } } } /** * 自定义缓存。加锁。 */ class MyCacheLock { private volatile Map<String, Object> map = new HashMap<>(); private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); /** * 存。 * * @param key * @param value */ public void put(String key, Object value) { // 写入时只希望有一个线程写。 readWriteLock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName() + " 写入 " + key); map.put(key, value); System.out.println(Thread.currentThread().getName() + " 写入 OK"); } catch (Exception e) { e.printStackTrace(); } finally { readWriteLock.writeLock().unlock(); } } /** * 取。 * * @param key */ public void get(String key) { readWriteLock.readLock().lock(); try { System.out.println(Thread.currentThread().getName() + " 读取 " + key); Object o = map.get(key); System.out.println("o = " + o); System.out.println(Thread.currentThread().getName() + " 读取 OK"); } catch (Exception e) { e.printStackTrace(); } finally { readWriteLock.readLock().unlock(); } } } /** * 自定义缓存。 */ class MyCache { private volatile Map<String, Object> map = new HashMap<>(); /** * 存。 * * @param key * @param value */ public void put(String key, Object value) { System.out.println(Thread.currentThread().getName() + " 写入 " + key); map.put(key, value); System.out.println(Thread.currentThread().getName() + " 写入 OK"); } /** * 取。 * * @param key */ public void get(String key) { System.out.println(Thread.currentThread().getName() + " 读取 " + key); Object o = map.get(key); System.out.println("o = " + o); System.out.println(Thread.currentThread().getName() + " 读取 OK"); } }进去一个元素,必须等待取出来之后,才能再放一个元素。
put、take。
package com.geek.queue; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; /** * 同步队列。 * 进去一个元素,必须等待取出来之后,才能再放一个元素。 * <p> * put、take。 */ public class SynchronousQueueDemo { public static void main(String[] args) { SynchronousQueue synchronousQueue = new SynchronousQueue(); new Thread(() -> { try { synchronousQueue.put("1"); System.out.println(Thread.currentThread().getName() + " put 1."); synchronousQueue.put("2"); System.out.println(Thread.currentThread().getName() + " put 2."); synchronousQueue.put("3"); System.out.println(Thread.currentThread().getName() + " put 3."); } catch (InterruptedException e) { e.printStackTrace(); } }, "T1").start(); new Thread(() -> { try { TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName() + " ~ " + synchronousQueue.take()); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName() + " ~ " + synchronousQueue.take()); TimeUnit.SECONDS.sleep(3); System.out.println(Thread.currentThread().getName() + " ~ " + synchronousQueue.take()); } catch (InterruptedException e) { e.printStackTrace(); } }, "T2").start(); } }程序的运行,本质:占用系统资源。
线程池、连接池、内存池、对象池,创建、销毁十分浪费资源。
↓ ↓ ↓
池化技术。
事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我。
降低资源消耗。提高响应速度。方便管理。线程复用、控制最大并发数、管理线程。
〖强制〗线程池不允许使用 Executors 去创建, 而是通过ThreadPooIExecutor 的方式, 这样的处理方式让写的同学更加明确线程池的运行规则, 规避资源耗尽的风险。 说明: Executors 返回的线程池对象的弊端如下:
1 ) FixedThreadPool 和SingleThreadPooI: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。 2 ) CachedThreadPool 和 ScheduledThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
开启线程本质 ~ new ThreadPoolExecutor();。int corePoolSize,// 核心线程池大小。 int maximumPoolSize,// 最大线程池大小。
都为 1。
/** * Creates an Executor that uses a single worker thread operating * off an unbounded queue, and uses the provided ThreadFactory to * create a new thread when needed. Unlike the otherwise * equivalent {@code newFixedThreadPool(1, threadFactory)} the * returned executor is guaranteed not to be reconfigurable to use * additional threads. * * @param threadFactory the factory to use when creating new * threads * * @return the newly created single-threaded Executor * @throws NullPointerException if threadFactory is null */ public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory)); }int corePoolSize,// 核心线程池大小。 int maximumPoolSize,// 最大线程池大小。
为传入的参数。
/** * 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<Runnable>()); }int corePoolSize,// 核心线程池大小。 int maximumPoolSize,// 最大线程池大小。
0 和 21 亿(2147483647)。
/** * 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<Runnable>()); 本质 ~ public ThreadPoolExecutor();。 /** * 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:<br> * {@code corePoolSize < 0}<br> * {@code keepAliveTime < 0}<br> * {@code maximumPoolSize <= 0}<br> * {@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<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; }一般 1 2 窗口开启,3 4 5 窗口关闭。
第三个人进来,进入等待区。
等待区满了,3 4 5 窗口人员回来上班。
等待区满了,又进来人了,拒绝策略生效。
自己实现线程池。 package com.geek.pool; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * Executors 工具类。 * 3 大方法。 * 使用线程池创建线程。 */ public class Demo01 { public static void main(String[] args) { // 自定义线程池。 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 5, 3,// 超时,1 个小时也没有人来 3 4 5 窗口,就关闭。 TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()// 银行满了,但还有人进来,就不处理了,抛异常。 ); try { // 5 个人进入,只有 corePoolSize = 2 个线程处理。 // 6 个人进入,最多有 3 个线程处理。 // 7 个人进入,最多有 4 个线程处理。 // 8 个人进入,最多有 5 个线程处理。 // 9 个人进入,抛异常。 // java.util.concurrent.RejectedExecutionException: Task com.geek.pool.Demo01$$Lambda$1/1831932724@7699a589 rejected from java.util.concurrent.ThreadPoolExecutor@58372a00[Running, pool size = 5, active threads = 0, queued tasks = 0, completed tasks = 8] for (int i = 0; i < 9; i++) { threadPoolExecutor.execute(() -> System.out.println(Thread.currentThread().getName())); } } catch (Exception e) { e.printStackTrace(); } finally { // 线程池用完,程序结束,关闭线程池。 threadPoolExecutor.shutdown(); } } } package com.geek.pool; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * Executors 工具类。 * 3 大方法。 * 使用线程池创建线程。 */ public class Demo01 { public static void main(String[] args) { // 自定义线程池。 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 5, 3,// 超时,1 个小时也没有人来 3 4 5 窗口,就关闭。 TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), Executors.defaultThreadFactory(), // new ThreadPoolExecutor.AbortPolicy()// 银行满了,但还有人进来,就不处理了,抛异常。 // new ThreadPoolExecutor.CallerRunsPolicy()// 哪来的去哪里。(main(); 线程执行的。) // pool-1-thread-1 //pool-1-thread-5 //pool-1-thread-4 //main //pool-1-thread-3 //pool-1-thread-2 //pool-1-thread-4 //pool-1-thread-5 //pool-1-thread-1 // new ThreadPoolExecutor.DiscardPolicy()// 队列满了,不会抛出异常,丢掉任务(只执行了 8 次)。 new ThreadPoolExecutor.DiscardOldestPolicy()// 队列满了,尝试和最早的线程竞争,也不抛出异常。 ); try { // 5 个人进入,只有 corePoolSize = 2 个线程处理。 // 6 个人进入,最多有 3 个线程处理。 // 7 个人进入,最多有 4 个线程处理。 // 8 个人进入,最多有 5 个线程处理。 // 9 个人进入,抛异常。 // java.util.concurrent.RejectedExecutionException: Task com.geek.pool.Demo01$$Lambda$1/1831932724@7699a589 rejected from java.util.concurrent.ThreadPoolExecutor@58372a00[Running, pool size = 5, active threads = 0, queued tasks = 0, completed tasks = 8] for (int i = 0; i < 9; i++) { threadPoolExecutor.execute(() -> System.out.println(Thread.currentThread().getName())); } } catch (Exception e) { e.printStackTrace(); } finally { // 线程池用完,程序结束,关闭线程池。 threadPoolExecutor.shutdown(); } } }CPU 几核就定义最大线程数为 CPU 核数。可以保持 CPU 效率最高。
Runtime.getRuntime().availableProcessors();
程序有 15 个大型任务,io 十分占资源。
判断程序中十分耗 io 资源的线程数,大于这个线程数即可。
集合、MySQL 本质是用来存储东西的。
计算都应该交给流来操作。
package com.geek.stream; import java.util.Arrays; import java.util.Comparator; import java.util.List; /** * 5 个用户。筛选。 * id 是偶数。 * 年龄大于 23。 * 用户名转为大写字母。 * 用户按名称倒序。 * 只输出一个用户。 */ public class Test { public static void main(String[] args) { User u1 = new User(1, "a", 21); User u2 = new User(2, "b", 22); User u3 = new User(3, "c", 23); User u4 = new User(4, "d", 24); User u5 = new User(6, "e", 25); // 集合就是用来存储的。 List<User> list = Arrays.asList(u1, u2, u3, u4, u5); // 计算交给 Stream 流。 // Stream<User> stream = list.stream(); list.stream() .filter(u -> u.getId() % 2 == 0) .filter(u -> u.getAge() > 23) .map(u -> u.getName().toUpperCase()) // .sorted(new Comparator<String>() { // @Override // public int compare(String o1, String o2) { // return o2.compareTo(o1); // } // }) .sorted(Comparator.reverseOrder()) .limit(1) .forEach(System.out::println); } }jdk 1.7。并行执行任务。提高效率,大数据量。
大数据:Map Reduce(把大任务拆成小任务)。
ForkJoin 特点:工作窃取。
这里面维护的是双端队列。
package com.geek.forkjoin; import java.util.concurrent.RecursiveTask; /** * 求和计算。 * 普通程序员 * ForkJoin * Stream */ public class ForkJoinDemo extends RecursiveTask<Long> { private Long start; private Long end; // 临界值。 private Long temp = 10000L; public ForkJoinDemo(Long start, Long end) { this.start = start; this.end = end; } /** * 计算方法。 * The main computation performed by this task. * * @return the result of the computation */ @Override protected Long compute() { if (end - start < temp) { Long sum = 0L; for (Long i = 1L; i <= 1_000_000_000; i++) { sum += i; } return sum; } else {// ForkJoin。 long middle = (start + end) / 2;// 中间值。 ForkJoinDemo task1 = new ForkJoinDemo(start, middle); task1.fork();// 拆分任务,把任务压入线程队列。 ForkJoinDemo task2 = new ForkJoinDemo(middle + 1, end); task2.fork();// 拆分任务,把任务压入线程队列。 return task1.join() + task2.join(); } } // public static void main(String[] args) { // int sum = 0; // for (int i = 1; i <= 10_000_000; i++) { // sum += i; // } // System.out.println("sum = " + sum); // } } package com.geek.forkjoin; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinTask; import java.util.stream.LongStream; public class Test { public static void main(String[] args) throws ExecutionException, InterruptedException { // test1(); // test2(); test3(); } // 普通。 private static void test1() { long start = System.currentTimeMillis(); Long sum = 0L; for (Long i = 1L; i <= 1_000_000_000; i++) { sum += i; } System.out.println("sum = " + sum); long end = System.currentTimeMillis(); System.out.println("时间 ~ " + (end - start)); // sum = 500000000500000000 //时间 ~ 6155 } // ForkJoin。 private static void test2() throws ExecutionException, InterruptedException { long start = System.currentTimeMillis(); ForkJoinPool forkJoinPool = new ForkJoinPool(); ForkJoinTask<Long> task = new ForkJoinDemo(0L, 1_000_000_000L); // forkJoinPool.execute(task); ForkJoinTask<Long> submit = forkJoinPool.submit(task);// 提交任务。 Long sum = submit.get(); System.out.println("sum = " + sum); long end = System.currentTimeMillis(); System.out.println("时间 ~ " + (end - start)); // sum = 43715662942646859 //时间 ~ 28120 } // Stream 并行流。 private static void test3() { long start = System.currentTimeMillis(); // (] long sum = LongStream.rangeClosed(0L, 1_000_000_000L).parallel().reduce(0, Long::sum); System.out.println("sum = " + sum); long end = System.currentTimeMillis(); System.out.println("时间 ~ " + (end - start)); // sum = 500000000500000000 //时间 ~ 293 } }volatile
a. 易变的;无定性的;无常性的;可能急剧波动的;不稳定的;易恶化的;易挥发的;易发散的
可见性。 package com.geek; import java.util.concurrent.TimeUnit; public class JMMDemo02 { // 不加 volatile 程序就会死循环。 // 加 volatile 可以保证可见性。 volatile static int number = 0; public static void main(String[] args) { new Thread(() -> { while (number == 0) { } }).start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } number = 1; System.out.println(number); } } 不保证原子性。 原子性:不可分割。线程 A 在执行的时候,不能被打扰,也不能被分割。要么帕里执行成功,要么同时执行失败。
package com.geek; /** * 不保证原子性。 */ public class JMMDemo03 { // volatile 不能保证原子性。 private volatile static int number = 0; private static void add() { ++number;// 不是一个原子操作。 } public static void main(String[] args) { // 理论上 number 结果为 20000。 for (int i = 0; i < 20; i++) { new Thread(() -> { for (int j = 0; j < 1000; j++) { add(); } }).start(); } // main 和 gc 线程。只要大于 2,说明线程还没执行完。 while (Thread.activeCount() > 2) { Thread.yield(); } System.out.println(Thread.currentThread().getName() + " ~ " + number); // main ~ 18307 // main ~ 18526 // synchronized 可保证 20000。但 volatile 不能保证。 // Non-atomic operation on volatile field 'number' less... (Ctrl+F1) //Inspection info: Reports any non-atomic operations on volatile fields. Non-atomic operations on volatile fields are operations where the field is read and the value is used to update the field. It is possible for the value of the field to change between the read and the write, possibly invalidating the operation. The non-atomic operation can be avoided by surrounding it with a synchronized block or by making use of one of the classes from the java.util.concurrent.atomic package. } } 为什么 ++number; 不是原子性操作。 G:\lyfGeek\IdeaProjects\geek_juc\jvm\target\classes\com\geek>javap -c JMMDemo03.class Compiled from "JMMDemo03.java" public class com.geek.JMMDemo03 { public com.geek.JMMDemo03(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void add(); Code: 0: getstatic #2 // Field number:I 3: iconst_1 4: iadd 5: putstatic #2 // Field number:I 8: return public static void main(java.lang.String[]); Code: 0: iconst_0 1: istore_1 2: iload_1 3: bipush 20 5: if_icmpge 29 8: new #3 // class java/lang/Thread 11: dup 12: invokedynamic #4, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable; 17: invokespecial #5 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V 20: invokevirtual #6 // Method java/lang/Thread.start:()V 23: iinc 1, 1 26: goto 2 29: invokestatic #7 // Method java/lang/Thread.activeCount:()I 32: iconst_2 33: if_icmple 42 36: invokestatic #8 // Method java/lang/Thread.yield:()V 39: goto 29 42: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream; 45: new #10 // class java/lang/StringBuilder 48: dup 49: invokespecial #11 // Method java/lang/StringBuilder."<init>":()V 52: invokestatic #12 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread; 55: invokevirtual #13 // Method java/lang/Thread.getName:()Ljava/lang/String; 58: invokevirtual #14 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/la ng/StringBuilder; 61: ldc #15 // String ~ 63: invokevirtual #14 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/la ng/StringBuilder; 66: getstatic #2 // Field number:I 69: invokevirtual #16 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 72: invokevirtual #17 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 75: invokevirtual #18 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 78: return static {}; Code: 0: iconst_0 1: putstatic #2 // Field number:I 4: return } 如果不加 lock 和 synchronized,怎么保证原子性。使用原子类解决原子性问题。
java.util.concurrent.atomic。
package com.geek; import java.util.concurrent.atomic.AtomicInteger; /** * 不保证原子性。 */ public class JMMDemo03 { // volatile 不能保证原子性。 // private volatile static int number = 0; private volatile static AtomicInteger number = new AtomicInteger(); public static void add() { // ++number;// 不是一个原子操作。 number.getAndIncrement();// CAS。 } public static void main(String[] args) { // 理论上 number 结果为 20000。 for (int i = 0; i < 20; i++) { new Thread(() -> { for (int j = 0; j < 1000; j++) { add(); } }).start(); } // main 和 gc 线程。只要大于 2,说明线程还没执行完。 while (Thread.activeCount() > 2) { Thread.yield(); } System.out.println(Thread.currentThread().getName() + " ~ " + number); // main ~ 20000 }你写的程序,计算机并不是按照你写的那样执行的。
源代码 -> 编译器优化重排 -> 指令并行也可能发生重排 -> 内存系统也会重排 --> 执行。
处理器在进行指令重排时,会考虑数据之间的依赖性。
int x = 1;// 1 int y = 2;// 2 x = x + 5;// 3 y = y + x;// 4 期望 1 2 3 4,但执行时可能 2 1 3 4,1 3 2 4. 但不可能 4 1 2 3。volatile 可以避免指令重排。
内存屏障。
保证特定操作的执行顺序。 保证某些变量的内存可见性。
↓ ↓ ↓
idea 的 class 文件中显示有空参构造。
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package com.geek.single; public enum EnumSingle { INSTANCE; private EnumSingle() { } public EnumSingle getInstance() { return INSTANCE; } }于是用反射空参对象,报错 ~ 没有空参构造器。IDEA 个骗子。
package com.geek.single; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; /** * 枚举本身也是一个 Class 类。 */ public enum EnumSingle { INSTANCE; public EnumSingle getInstance() { return INSTANCE; } } class Test { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { EnumSingle instance1 = EnumSingle.INSTANCE; EnumSingle instance2 = EnumSingle.INSTANCE; System.out.println("instance1 = " + instance1); System.out.println("instance2 = " + instance2); // instance1 = INSTANCE //instance2 = INSTANCE Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null); declaredConstructor.setAccessible(true); // Exception in thread "main" java.lang.NoSuchMethodException: com.geek.single.EnumSingle.<init>() EnumSingle enumSingle1 = declaredConstructor.newInstance(); EnumSingle enumSingle2 = declaredConstructor.newInstance(); System.out.println("enumSingle1 = " + enumSingle1); System.out.println("enumSingle2 = " + enumSingle2); } }反编译看看,但是还是有空参构造。骗子!
> javap -p EnumSingle.class Compiled from "EnumSingle.java" public final class com.geek.single.EnumSingle extends java.lang.Enum<com.geek.single.EnumSingle> { public static final com.geek.single.EnumSingle INSTANCE; private static final com.geek.single.EnumSingle[] $VALUES; public static com.geek.single.EnumSingle[] values(); public static com.geek.single.EnumSingle valueOf(java.lang.String); private com.geek.single.EnumSingle(); public com.geek.single.EnumSingle getInstance(); static {}; } 使用 jad 反编译。工具下载:https://download.csdn.net/download/lyfGeek/12673711
jad.exe -sjava EnumSingle.class Parsing EnumSingle.class… Generating EnumSingle.java
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov. // Jad home page: http://www.kpdus.com/jad.html // Decompiler options: packimports(3) // Source File Name: EnumSingle.java package com.geek.single; public final class EnumSingle extends Enum { public static EnumSingle[] values() { return (EnumSingle[])$VALUES.clone(); } public static EnumSingle valueOf(String name) { return (EnumSingle)Enum.valueOf(com/geek/single/EnumSingle, name); } private EnumSingle(String s, int i) { super(s, i); } public EnumSingle getInstance() { return INSTANCE; } public static final EnumSingle INSTANCE; private static final EnumSingle $VALUES[]; static { INSTANCE = new EnumSingle("INSTANCE", 0); $VALUES = (new EnumSingle[] { INSTANCE }); } }其实是有参构造器。
private EnumSingle(String s, int i) { super(s, i); }反射走起。
package com.geek.single; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; /** * 枚举本身也是一个 Class 类。 */ public enum EnumSingle { INSTANCE; public EnumSingle getInstance() { return INSTANCE; } } class Test { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { EnumSingle instance1 = EnumSingle.INSTANCE; EnumSingle instance2 = EnumSingle.INSTANCE; System.out.println("instance1 = " + instance1); System.out.println("instance2 = " + instance2); // instance1 = INSTANCE //instance2 = INSTANCE Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class, int.class); declaredConstructor.setAccessible(true); // Exception in thread "main" java.lang.NoSuchMethodException: com.geek.single.EnumSingle.<init>() EnumSingle enumSingle1 = declaredConstructor.newInstance(); EnumSingle enumSingle2 = declaredConstructor.newInstance(); System.out.println("enumSingle1 = " + enumSingle1); System.out.println("enumSingle2 = " + enumSingle2); } // instance1 = INSTANCE //instance2 = INSTANCE //Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects // at java.lang.reflect.Constructor.newInstance(Constructor.java:417) // at com.geek.single.Test.main(EnumSingle.java:34) }内存操作。效率很高。
这是一个很标准的自旋锁体现。
CAS ~ 比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么执行操作。如果不是就一直循环。
缺点。
循环会耗时间。一次性只能保证一个共享变量的原子性。ABA 问题。java.util.concurrent.atomic.AtomicReference
package com.geek.cas; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicStampedReference; /** * CAS ~ Compare and Swap。 * 比较并交换。 */ public class ABADemo { // 对于 SQL ~ 乐观锁。 public static void main(String[] args) { // AtomicInteger atomicInteger = new AtomicInteger(1); // 【强制】所有整型包装类对象之间值的比较,全部使用 equals 方法比较。 //说明:对于 Integer var = ? 在-128 至 127 之间的赋值,Integer 对象是在 IntegerCache.cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals 方法进行判断。 // AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(2020, 1); AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1); new Thread(() -> { int stamp = atomicStampedReference.getStamp();// 获得版本号。~ 1 System.out.println("a 1 ~ stamp = " + stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } // 乐观锁,版本号 +1。 System.out.println(atomicStampedReference.compareAndSet(1, 2, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));// true System.out.println("a 2 ~ stamp = " + atomicStampedReference.getStamp());// 2 System.out.println(atomicStampedReference.compareAndSet(2, 1, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));// true System.out.println("a 3 ~ stamp = " + atomicStampedReference.getStamp());// 3 }, "A").start(); new Thread(() -> { int stamp = atomicStampedReference.getStamp();// 获得版本号。~ 1 System.out.println("b 1 ~ stamp = " + stamp);// 1 try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } // 乐观锁,版本号 +1。 System.out.println(atomicStampedReference.compareAndSet(1, 6, stamp, stamp + 1));// false System.out.println("b 1 ~ atomicStampedReference.getStamp() = " + atomicStampedReference.getStamp());// 3 }, "B").start(); } }公平锁(先来后到) 。
非公平锁(可以插队,默认)。
/** * Creates an instance of {@code ReentrantLock}. * This is equivalent to using {@code ReentrantLock(false)}. */ public ReentrantLock() { sync = new NonfairSync(); } /** * Creates an instance of {@code ReentrantLock} with the * given fairness policy. * * @param fair {@code true} if this lock should use a fair ordering policy */ public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }(递归锁)。
package com.geek.lock; public class Demo01 { public static void main(String[] args) { Phone phone = new Phone(); new Thread(phone::sms, "A").start(); new Thread(() -> phone.sms(), "B").start(); } } class Phone { public synchronized void sms() { System.out.println(Thread.currentThread().getName() + " ~ sms"); call(); } public synchronized void call() { System.out.println(Thread.currentThread().getName() + " ~ call"); } } package com.geek.lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Demo02 { public static void main(String[] args) { Phone2 phone = new Phone2(); new Thread(phone::sms, "A").start(); new Thread(() -> phone.sms(), "B").start(); } } class Phone2 { Lock lock = new ReentrantLock(); public synchronized void sms() { lock.lock(); // lock 锁必须配对,否则就会死在里面。 lock.lock(); try { System.out.println(Thread.currentThread().getName() + " ~ sms"); call(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); lock.unlock(); } } public synchronized void call() { lock.lock(); try { System.out.println(Thread.currentThread().getName() + " ~ call"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }\jdk1.8.0_241\bin\ 下的 jps.exe。
jps -l
G:\lyfGeek\ProgramFiles\Java\jdk1.8.0_241\bin>jps -l 16416 org.jetbrains.jps.cmdline.Launcher 17664 18624 com.geek.lock.DeadLockDemo 19508 org.jetbrains.kotlin.daemon.KotlinCompileDaemon 18044 sun.tools.jps.Jps 20396 org.jetbrains.idea.maven.server.RemoteMavenServer使用 jstack 进程号。
G:\lyfGeek\ProgramFiles\Java\jdk1.8.0_241\bin>jstack 18624 2020-11-08 12:29:54 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.241-b07 mixed mode): "DestroyJavaVM" #14 prio=5 os_prio=0 tid=0x0000000002dc3000 nid=0x4d98 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "T2" #13 prio=5 os_prio=0 tid=0x000000001e7d1000 nid=0xa08 waiting for monitor entry [0x000000001f70f000] java.lang.Thread.State: BLOCKED (on object monitor) at com.geek.lock.MyThread.run(DeadLockDemo.java:41) - waiting to lock <0x000000076bb9bf28> (a java.lang.String) - locked <0x000000076bb9bf60> (a java.lang.String) at java.lang.Thread.run(Thread.java:748) "T1" #12 prio=5 os_prio=0 tid=0x000000001e7ce800 nid=0x4664 waiting for monitor entry [0x000000001f60e000] java.lang.Thread.State: BLOCKED (on object monitor) at com.geek.lock.MyThread.run(DeadLockDemo.java:41) - waiting to lock <0x000000076bb9bf60> (a java.lang.String) - locked <0x000000076bb9bf28> (a java.lang.String) at java.lang.Thread.run(Thread.java:748) "Service Thread" #11 daemon prio=9 os_prio=0 tid=0x000000001e79e000 nid=0x47c runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x000000001e700800 nid=0x1b20 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000000001e6fd800 nid=0xb9c waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000000001e6f8000 nid=0x4fc8 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000000001e6f3800 nid=0x4d40 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000000001e6e6000 nid=0x4d64 runnable [0x000000001ef0e000] java.lang.Thread.State: RUNNABLE at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) at java.net.SocketInputStream.read(SocketInputStream.java:171) at java.net.SocketInputStream.read(SocketInputStream.java:141) at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326) at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178) - locked <0x000000076ba84df8> (a java.io.InputStreamReader) at java.io.InputStreamReader.read(InputStreamReader.java:184) at java.io.BufferedReader.fill(BufferedReader.java:161) at java.io.BufferedReader.readLine(BufferedReader.java:324) - locked <0x000000076ba84df8> (a java.io.InputStreamReader) at java.io.BufferedReader.readLine(BufferedReader.java:389) at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64) "Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000000001e649000 nid=0x4564 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000001e6a2800 nid=0x1e1c runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001e631800 nid=0x2990 in Object.wait() [0x000000001ec0f000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000076b908ee0> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144) - locked <0x000000076b908ee0> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216) "Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000002eb9000 nid=0x4a5c in Object.wait() [0x000000001eb0f000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x000000076b906c00> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:502) at java.lang.ref.Reference.tryHandlePending(Reference.java:191) - locked <0x000000076b906c00> (a java.lang.ref.Reference$Lock) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153) "VM Thread" os_prio=2 tid=0x000000001c847800 nid=0x3200 runnable "GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002dd8800 nid=0x2cd4 runnable "GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000002dda000 nid=0xef8 runnable "GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000002ddc000 nid=0x4cc4 runnable "GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000002ddd800 nid=0x31bc runnable "GC task thread#4 (ParallelGC)" os_prio=0 tid=0x0000000002ddf800 nid=0x3c20 runnable "GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000002de1000 nid=0x3524 runnable "GC task thread#6 (ParallelGC)" os_prio=0 tid=0x0000000002de5000 nid=0x367c runnable "GC task thread#7 (ParallelGC)" os_prio=0 tid=0x0000000002de6000 nid=0x4660 runnable "VM Periodic Task Thread" os_prio=2 tid=0x000000001e7cd000 nid=0x2c40 waiting on condition JNI global references: 12 Found one Java-level deadlock: ============================= "T2": waiting to lock monitor 0x000000001c850e38 (object 0x000000076bb9bf28, a java.lang.String), which is held by "T1" "T1": waiting to lock monitor 0x000000001c853358 (object 0x000000076bb9bf60, a java.lang.String), which is held by "T2" Java stack information for the threads listed above: =================================================== "T2": at com.geek.lock.MyThread.run(DeadLockDemo.java:41) - waiting to lock <0x000000076bb9bf28> (a java.lang.String) - locked <0x000000076bb9bf60> (a java.lang.String) at java.lang.Thread.run(Thread.java:748) "T1": at com.geek.lock.MyThread.run(DeadLockDemo.java:41) - waiting to lock <0x000000076bb9bf60> (a java.lang.String) - locked <0x000000076bb9bf28> (a java.lang.String) at java.lang.Thread.run(Thread.java:748) Found 1 deadlock.日志。
堆栈。