15个多线程相关的面试题
By 大数据技术与架构
现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行?
这个线程问题通常会在第一轮或电话面试阶段被问到,目的是检测你对”join”方法是否熟悉。这个多线程问题比较简单,可以用join方法实现。核心:thread.Join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。
比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。
想要更深入了解,建议看一下join的源码,也很简单的,使用wait方法实现的。
t.join(); //调用join方法,等待线程t执行完毕
t.join(1000); //等待 t 线程,等待时间是1000毫秒。
public static void main(String[] args) {
method01();
method02();
}
/**
* 第一种实现方式,顺序写死在线程代码的内部了,有时候不方便
*/
private static void method01() {
Thread t1 = new Thread(new Runnable() {
@Override public void run() {
System.out.println("t1 is finished");
}
});
Thread t2 = new Thread(new Runnable() {
@Override public void run() {
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t2 is finished");
}
});
Thread t3 = new Thread(new Runnable() {
@Override public void run() {
try {
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t3 is finished");
}
});
t3.start();
t2.start();
t1.start();
}
/**
* 第二种实现方式,线程执行顺序可以在方法中调换
*/
private static void method02(){
Runnable runnable = new Runnable() {
@Override public void run() {
System.out.println(Thread.currentThread().getName() + "执行完成");
}
};
Thread t1 = new Thread(runnable, "t1");
Thread t2 = new Thread(runnable, "t2");
Thread t3 = new Thread(runnable, "t3");
try {
t1.start();
t1.join();
t2.start();
t2.join();
t3.start();
t3.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
在Java中Lock接口比synchronized块的优势是什么?你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它?
这个题的原答案我认为不是很全面。Lock接口和ReadWriteLock接口如下:
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
import java.util.HashMap;import java.util.Map;import java.util.Random;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantReadWriteLock;/** * 用ReadWriteLock读写锁来实现一个高效的Map缓存 * Created by LEO on 2017/10/30. */public class ReaderAndWriter<K, V> { private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock readLock = lock.readLock(); private final Lock writeLock = lock.writeLock(); private final Map<K, V> map; public ReaderAndWriter(Map<K, V> map) { this.map = map; } /************* 这是用lock()方法写的 ********************/// public V put(K key, V value){// writeLock.lock();// try {// return map.put(key, value);// }finally {// writeLock.unlock();// }// }// public V get(K key){// readLock.lock();// try {// return map.get(key);// }finally {// readLock.unlock();// }// } /************* 这是用tryLock()方法写的 ********************/ public V put(K key, V value){ while (true){ if(writeLock.tryLock()){ try { System.out.println("put "+ key +" = " + value); return map.put(key, value); }finally { writeLock.unlock(); } } } } public V get(K key){ while (true){ if (readLock.tryLock()) { try { V v = map.get(key); System.out.println("get "+ key +" = " + v); return v; } finally { readLock.unlock(); } } } } /******************** 下面是测试区 *********************************/ public static void main(String[] args) { final ReaderAndWriter<String, Integer> rw = new ReaderAndWriter<>(new HashMap<>()); ExecutorService exec = Executors.newCachedThreadPool(); for (int i = 0; i < 100; i++) { exec.execute(new TestRunnable(rw)); } exec.shutdown(); } static class TestRunnable implements Runnable{ private final ReaderAndWriter<String, Integer> rw; private final String KEY = "x"; TestRunnable(ReaderAndWriter<String, Integer> rw) { this.rw = rw; } @Override public void run() { Random random = new Random(); int r = random.nextInt(100); //生成随机数,小于30的写入缓存,大于等于30则读取数字 if (r < 30){ rw.put(KEY, r); } else { rw.get(KEY); } } }}
在java中wait和sleep方法的不同?
通常会在电话面试中经常被问到的Java线程面试问题。
最大的不同是在等待时wait会释放锁,而sleep一直持有锁。Wait通常被用于线程间交互,sleep通常被用于暂停执行。
此处我想理一下Java多线程的基础知识:
- Java的多线程锁是挂在对象上的,并不是在方法上的。即每个对象都有一个锁,当遇到类似synchronized的同步需要时,就会监视(monitor)每个想使用本对象的线程按照一定的规则来访问,规则也就是在同一时间内只能有一个线程能访问此对象。
- Java中获取锁的单位是线程。当线程A获取了对象B的锁,也就是对象B的持有标记上写的是线程A的唯一标识,在需要同步的情况下的话,只有线程A能访问对象B。
- Thread常用方法有:start/stop/yield/sleep/interrupt/join等,他们是线程级别的方法,所以并不会太关心锁的具体逻辑。
- Object的线程有关方法是:wait/wait(事件参数)/notify/notifyAll,他们是对象的方法,所以使用的时候就有点憋屈了,必须当前线程获取了本对象的锁才能使用,否则会报异常。但他们能更细粒度的控制锁,可以释放锁。
用Java实现阻塞队列
这是一个相对艰难的多线程面试问题,它能达到很多的目的。第一,它可以检测侯选者是否能实际的用Java线程写程序;第二,可以检测侯选者对并发场景的理解,并且你可以根据这个问很多问题。如果他用wait()和notify()方法来实现阻塞队列,你可以要求他用最新的Java 5中的并发类来再写一次。
下面是实现了阻塞的take和put方法的阻塞队列(分别用synchronized 和 wait/notify 实现):
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 实现了阻塞的take和put方法的阻塞队列
* 分别用synchronized 和 wait/notify 实现
* @author xuexiaolei
* @version 2017年11月01日
*/
public class MyBlocingQueue<E> {
private final List list;
private final int limit;//有大小限制的
public MyBlocingQueue(int limit) {
this.limit = limit;
this.list = new LinkedList<E>();
}
//这是用synchronized写的,在list空或者满的时候效率会低,因为会一直轮询
// public void put(E e){
// while(true){
// synchronized (list){
// if (list.size() < limit) {
// System.out.println("list : " + list.toString());
// System.out.println("put : " + e);
// list.add(e);
// return;
// }
// }
// }
// }
// public E take(){
// while (true) {
// synchronized (list) {
// if (list.size() > 0){
// System.out.println("list : " + list.toString());
// E remove = (E) list.remove(0);
// System.out.println("take : " + remove);
// return remove;
// }
// }
// }
// }
//用wait,notify写的,在list空或者满的时候效率会高一点,因为wait释放锁,然后等待唤醒
public synchronized void put(E e){
while (list.size() == limit){
try {
wait();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
System.out.println("list : " + list.toString());
System.out.println("put : " + e);
list.add(e);
notifyAll();
}
public synchronized E take() {
while (list.size() == 0){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("list : " + list.toString());
E remove = (E) list.remove(0);
System.out.println("take : " + remove);
notifyAll();
return remove;
}
/******************** 下面是测试区 *********************************/
public static void main(String[] args) {
final MyBlocingQueue<Integer> myBlocingQueue = new MyBlocingQueue(10);
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
exec.execute(new TestRunnable(myBlocingQueue));
}
exec.shutdown();
}
static class TestRunnable implements Runnable{
private final MyBlocingQueue<Integer> myBlocingQueue;
TestRunnable(MyBlocingQueue<Integer> myBlocingQueue) {
this.myBlocingQueue = myBlocingQueue;
}
@Override
public void run() {
Random random = new Random();
int r = random.nextInt(100);
//生成随机数,按照一定比率读取或者放入,可以更改!!!
if (r < 30){
myBlocingQueue.put(r);
} else {
myBlocingQueue.take();
}
}
}
}
BlockingQueue
Java5中提供了BlockingQueue的方法,并且有几个实现,在此介绍一下。BlockingQueue 具有 4 组不同的方法用于插入、移除以及对队列中的元素进行检查。如果请求的操作不能得到立即执行的话,每个方法的表现也不同。这些方法如下:import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 模仿ArrayBlockingQueue实现阻塞队列
* @author xuexiaolei
* @version 2017年11月01日
*/
public class MyBlocingQueue2<E> {
private final List list;
private final int limit;//有大小限制的
private final Lock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
public MyBlocingQueue2(int limit) {
this.limit = limit;
this.list = new LinkedList<E>();
}
public void put(E e) throws InterruptedException {
lock.lock();
try {
while (list.size() == limit){
notFull.await();
}
list.add(e);
notEmpty.signalAll();
}finally {
lock.unlock();
}
}
public E take() throws InterruptedException {
lock.lock();
try {
while (list.size() == 0){
notEmpty.await();
}
E remove = (E) list.remove(0);
notFull.signalAll();
return remove;
}finally {
lock.unlock();
}
}
}
用Java写代码来解决生产者——消费者问题
与上面的问题很类似,但这个问题更经典,有些时候面试都会问下面的问题。在Java中怎么解决生产者——消费者问题,当然有很多解决方法,我已经分享了一种用阻塞队列实现的方法。有些时候他们甚至会问怎么实现哲学家进餐问题。import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* 用阻塞队列快速实现生产者-消费者
* @author xuexiaolei
* @version 2017年11月01日
*/
public class ProduceAndConsumer {
public static void main(String[] args) {
final BlockingQueue<Integer> list = new ArrayBlockingQueue<Integer>(10);
Procude procude = new Procude(list);
Consumer consumer = new Consumer(list);
procude.start();
consumer.start();
}
static class Procude extends Thread{
private final BlockingQueue<Integer> list;
Procude(BlockingQueue<Integer> list) {
this.list = list;
}
@Override public void run() {
while(true){
try {
Integer take = list.take();
System.out.println("消费数据:" + take);
// Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class Consumer extends Thread{
private final BlockingQueue<Integer> list;
Consumer(BlockingQueue<Integer> list) {
this.list = list;
}
@Override public void run() {
while (true){
try {
int i = new Random().nextInt(100);
list.put(i);
System.out.println("生产数据:" + i);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
用Java写一个会导致死锁的程序,你将怎么解决?
这是我最喜欢的Java线程面试问题,因为即使死锁问题在写多线程并发程序时非常普遍,但是很多侯选者并不能写deadlock free code(无死锁代码?),他们很挣扎。只要告诉他们,你有N个资源和N个线程,并且你需要所有的资源来完成一个操作。为了简单这里的n可以替换为2,越大的数据会使问题看起来更复杂。通过避免Java中的死锁来得到关于死锁的更多信息。/**
* 简单死锁程序
* lockA、lockB分别是两个资源,线程A、B必须同是拿到才能工作
* 但A线程先拿lockA、再拿lockB
* 线程先拿lockB、再拿lockA
* @author xuexiaolei
* @version 2017年11月01日
*/
public class SimpleDeadLock {
public static void main(String[] args) {
Object lockA = new Object();
Object lockB = new Object();
A a = new A(lockA, lockB);
B b = new B(lockA, lockB);
a.start();
b.start();
}
static class A extends Thread{
private final Object lockA;
private final Object lockB;
A(Object lockA, Object lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override public void run() {
synchronized (lockA){
try {
Thread.sleep(1000);
synchronized (lockB){
System.out.println("Hello A");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class B extends Thread{
private final Object lockA;
private final Object lockB;
B(Object lockA, Object lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override public void run() {
synchronized (lockB){
try {
Thread.sleep(1000);
synchronized (lockA){
System.out.println("Hello B");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
产生死锁的四个必要条件:
- 互斥条件:一个资源每次只能被一个进程使用。
- 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
- 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
如何避免死锁?
- 从死锁的四个必要条件来看,破坏其中的任意一个条件就可以避免死锁。但互斥条件是由资源本身决定的,不剥夺条件一般无法破坏,要实现的话得自己写更多的逻辑。
- 避免无限期的等待:用Lock.tryLock(),wait/notify等方法写出请求一定时间后,放弃已经拥有的锁的程序。
- 注意锁的顺序:以固定的顺序获取锁,可以避免死锁。
- 开放调用:即只对有请求的进行封锁。你应当只想你要运行的资源获取封锁,比如在上述程序中我在封锁的完全的对象资源。但是如果我们只对它所属领域中的一个感兴趣,那我们应当封锁住那个特殊的领域而并非完全的对象。
- 最后,如果能避免使用多个锁,甚至写出无锁的线程安全程序是再好不过了。
什么是原子操作,Java中的原子操作是什么?
非常简单的java线程面试问题,接下来的问题是你是否需要同步一个原子操作。
原子操作是不可分割的操作,一个原子操作中间是不会被其他线程打断的,所以不需要同步一个原子操作。
多个原子操作合并起来后就不是一个原子操作了,就需要同步了。
i++不是一个原子操作,它包含 读取-修改-写入 操作,在多线程状态下是不安全的。
另外,java内存模型允许将64位的读操作或写操作分解为2个32位的操作,所以对long和double类型的单次读写操作并不是原子的,注意使用volitile使他们成为原子操作。
Java中的volatile关键是什么作用?怎样使用它?在Java中它跟synchronized方法有什么不同?
自从Java 5和Java内存模型改变以后,基于volatile关键字的线程问题越来越流行。应该准备好回答关于volatile变量怎样在并发环境中确保可见性。
volatile关键字的作用是:保证变量的可见性。
在java内存结构中,每个线程都是有自己独立的内存空间(此处指的线程栈)。当需要对一个共享变量操作时,线程会将这个数据从主存空间复制到自己的独立空间内进行操作,然后在某个时刻将修改后的值刷新到主存空间。这个中间时间就会发生许多奇奇怪怪的线程安全问题了,volatile就出来了,它保证读取数据时只从主存空间读取,修改数据直接修改到主存空间中去,这样就保证了这个变量对多个操作线程的可见性了。换句话说,被volatile修饰的变量,能保证该变量的 单次读或者单次写 操作是原子的。
但是线程安全是两方面需要的 原子性(指的是多条操作)和可见性。volatile只能保证可见性,synchronized是两个均保证的。
volatile轻量级,只能修饰变量;synchronized重量级,还可修饰方法。
volatile不会造成线程的阻塞,而synchronized可能会造成线程的阻塞。
什么是竞争条件(race condition)?你怎样发现和解决的?
这是一道出现在多线程面试的高级阶段的问题。大多数的面试官会问最近你遇到的竞争条件,以及你是怎么解决的。有些时间他们会写简单的代码,然后让你检测出代码的竞争条件。可以参考我之前发布的关于Java竞争条件的文章。在我看来这是最好的java线程面试问题之一,它可以确切的检测候选者解决竞争条件的经验。关于这方面最好的书是《java并发编程实战》。
竞争条件,在《java并发编程实战》叫做竞态条件:指设备或系统出现不恰当的执行时序,而得到不正确的结果。
下面是个最简单的例子,是一个单例模式实现的错误示范:
@NotThreadSafe
public class LazyInitRace {
private ExpensiveObject instance = null;
public ExpensiveObject getInstance() {
if (instance == null)
instance = new ExpensiveObject();
return instance;
}
}
你将如何使用thread dump?你将如何分析Thread dump?
在UNIX中你可以使用kill -3,然后thread dump将会打印日志,在windows中你可以使用”CTRL+Break”。非常简单和专业的线程面试问题,但是如果他问你怎样分析它,就会很棘手。java线程的状态转换介绍
后续分析要用到,所以此处穿插一下这个点:位于对象等待池中的阻塞状态(Blocked in object’s wait pool):
当线程处于运行状态时,如果执行了某个对象的wait()方法,Java虚拟机就会把线程放到这个对象的等待池中,这涉及到“线程通信”的内容。
位于对象锁池中的阻塞状态(Blocked in object’s lock pool):
当线程处于运行状态时,试图获得某个对象的同步锁时,如果该对象的同步锁已经被其他线程占用,Java虚拟机就会把这个线程放到这个对象的锁池中,这涉及到“线程同步”的内容。
其他阻塞状态(Otherwise Blocked):
当前线程执行了sleep()方法,或者调用了其他线程的join()方法,或者发出了I/O请求时,就会进入这个状态。
当线程退出run()方法时,就进入死亡状态,该线程结束生命周期。我们运行之前的那个死锁代码SimpleDeadLock.java,然后尝试输出信息(/*这是注释,作者自己加的*/):
/* 时间,jvm信息 */
2017-11-01 17:36:28
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode):
/* 线程名称:DestroyJavaVM
编号:#13
优先级:5
系统优先级:0
jvm内部线程id:0x0000000001c88800
对应系统线程id(NativeThread ID):0x1c18
线程状态:waiting on condition [0x0000000000000000] (等待某个条件)
线程详细状态:java.lang.Thread.State: RUNNABLE 及之后所有*/
"DestroyJavaVM" #13 prio=5 os_prio=0 tid=0x0000000001c88800 nid=0x1c18 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Thread-1" #12 prio=5 os_prio=0 tid=0x0000000018d49000 nid=0x17b8 waiting for monitor entry [0x0000000019d7f000]
/* 线程状态:阻塞(在对象同步上)
代码位置:at com.leo.interview.SimpleDeadLock$B.run(SimpleDeadLock.java:56)
等待锁:0x00000000d629b4d8
已经获得锁:0x00000000d629b4e8*/
java.lang.Thread.State: BLOCKED (on object monitor)
at com.leo.interview.SimpleDeadLock$B.run(SimpleDeadLock.java:56)
- waiting to lock <0x00000000d629b4d8> (a java.lang.Object)
- locked <0x00000000d629b4e8> (a java.lang.Object)
"Thread-0" #11 prio=5 os_prio=0 tid=0x0000000018d44000 nid=0x1ebc waiting for monitor entry [0x000000001907f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.leo.interview.SimpleDeadLock$A.run(SimpleDeadLock.java:34)
- waiting to lock <0x00000000d629b4e8> (a java.lang.Object)
- locked <0x00000000d629b4d8> (a java.lang.Object)
"Service Thread" #10 daemon prio=9 os_prio=0 tid=0x0000000018ca5000 nid=0x1264 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x0000000018c46000 nid=0xb8c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x0000000018be4800 nid=0x1db4 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x0000000018be3800 nid=0x810 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x0000000018bcc800 nid=0x1c24 runnable [0x00000000193ce000]
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 <0x00000000d632b928> (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 <0x00000000d632b928> (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=0x0000000017781800 nid=0x524 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000001778f800 nid=0x1b08 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001776a800 nid=0xdac in Object.wait() [0x0000000018b6f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000d6108ec8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x00000000d6108ec8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000017723800 nid=0x1670 in Object.wait() [0x00000000189ef000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000d6106b68> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x00000000d6106b68> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"VM Thread" os_prio=2 tid=0x000000001771b800 nid=0x604 runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000001c9d800 nid=0x9f0 runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000001c9f000 nid=0x154c runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000001ca0800 nid=0xcd0 runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000001ca2000 nid=0x1e58 runnable
"VM Periodic Task Thread" os_prio=2 tid=0x0000000018c5a000 nid=0x1b58 waiting on condition
JNI global references: 33
/* 此处可以看待死锁的相关信息!*/
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x0000000017729fc8 (object 0x00000000d629b4d8, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x0000000017727738 (object 0x00000000d629b4e8, a java.lang.Object),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at com.leo.interview.SimpleDeadLock$B.run(SimpleDeadLock.java:56)
- waiting to lock <0x00000000d629b4d8> (a java.lang.Object)
- locked <0x00000000d629b4e8> (a java.lang.Object)
"Thread-0":
at com.leo.interview.SimpleDeadLock$A.run(SimpleDeadLock.java:34)
- waiting to lock <0x00000000d629b4e8> (a java.lang.Object)
- locked <0x00000000d629b4d8> (a java.lang.Object)
Found 1 deadlock.
/* 内存使用状况,详情得看JVM方面的书 */
Heap
PSYoungGen total 37888K, used 4590K [0x00000000d6100000, 0x00000000d8b00000, 0x0000000100000000)
eden space 32768K, 14% used [0x00000000d6100000,0x00000000d657b968,0x00000000d8100000)
from space 5120K, 0% used [0x00000000d8600000,0x00000000d8600000,0x00000000d8b00000)
to space 5120K, 0% used [0x00000000d8100000,0x00000000d8100000,0x00000000d8600000)
ParOldGen total 86016K, used 0K [0x0000000082200000, 0x0000000087600000, 0x00000000d6100000)
object space 86016K, 0% used [0x0000000082200000,0x0000000082200000,0x0000000087600000)
Metaspace used 3474K, capacity 4500K, committed 4864K, reserved 1056768K
class space used 382K, capacity 388K, committed 512K, reserved 1048576K
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 线程都准备完成后一起执行的例子
* @author xuexiaolei
* @version 2017年11月02日
*/
public class CountdownLatchTest {
private final static int THREAD_NUM = 10;
public static void main(String[] args) {
CountDownLatch lock = new CountDownLatch(THREAD_NUM);
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < THREAD_NUM; i++) {
exec.submit(new CountdownLatchTask(lock, "Thread-"+i));
}
exec.shutdown();
}
static class CountdownLatchTask implements Runnable{
private final CountDownLatch lock;
private final String threadName;
CountdownLatchTask(CountDownLatch lock, String threadName) {
this.lock = lock;
this.threadName = threadName;
}
@Override public void run() {
//循环多次是为了证明,CountdownLatch只会阻挡一次
for (int i = 0; i < 3; i++) {
System.out.println(threadName + " 准备完成");
lock.countDown();
try {
lock.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(threadName + " 执行完成");
}
}
}
}
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 各个线程执行完成后,主线程做总结性工作的例子
* @author xuexiaolei
* @version 2017年11月02日
*/
public class CountdownLatchTest2 {
private final static int THREAD_NUM = 10;
public static void main(String[] args) {
CountDownLatch lock = new CountDownLatch(THREAD_NUM);
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < THREAD_NUM; i++) {
exec.submit(new CountdownLatchTask(lock, "Thread-"+i));
}
try {
lock.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("大家都执行完成了,做总结性工作");
exec.shutdown();
}
static class CountdownLatchTask implements Runnable{
private final CountDownLatch lock;
private final String threadName;
CountdownLatchTask(CountDownLatch lock, String threadName) {
this.lock = lock;
this.threadName = threadName;
}
@Override public void run() {
System.out.println(threadName + " 执行完成");
lock.countDown();
}
}
}
import java.util.concurrent.*;
/**
*
* @author xuexiaolei
* @version 2017年11月02日
*/
public class CyclicBarrierTest {
private final static int THREAD_NUM = 10;
public static void main(String[] args) {
CyclicBarrier lock = new CyclicBarrier(THREAD_NUM, new Runnable() {
@Override public void run() {
System.out.println("大家都准备完成了");
}
});
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0; i < THREAD_NUM; i++) {
exec.submit(new CountdownLatchTask(lock, "Thread-"+i));
}
exec.shutdown();
}
static class CountdownLatchTask implements Runnable{
private final CyclicBarrier lock;
private final String threadName;
CountdownLatchTask(CyclicBarrier lock, String threadName) {
this.lock = lock;
this.threadName = threadName;
}
@Override public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(threadName + " 准备完成");
try {
lock.await();
} catch (BrokenBarrierException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(threadName + " 执行完成");
}
}
}
}
/**
* 不可变对象
* @author xuexiaolei
* @version 2017年11月03日
*/
public class ImmutableObjectPerson {
private final String name;
private final String sex;
public ImmutableObjectPerson(String name, String sex) {
this.name = name;
this.sex = sex;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
}
文章不错?点个【在看】吧! 👇