本篇文章将Java线程中常见的功能进行整理,涉及点:join、yield、isAlive方法、synchronized的使用、生产者-消费者模式(wait/notify实现和阻塞队列实现)、Lock+Condition模拟阻塞队列、线程同步工具、模拟死锁程序、jstack寻找死锁程序。
join
join类似于同步,当A线程中调用了B线程的join()方法时,表示只有当B线程执行完毕时,A线程才能继续执行(如下代码), 但是B线程必须已经调用start()方法,否则join就会失效
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
System.out.println("我是线程start");
try {
System.out.println("等待3秒");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是线程end");
});
thread.start();
thread.join();
System.out.println("我是线程2 正常情况下肯定是我先执行完,但是加入join后,main主线程会等待子线程执行完毕后才执行");
}
yield
yield,可以理解为让步,对于运行的线程(占有CPU资源),可以通过yield方法 让出其占有的CPU,让其它具有相同优先级的等待线程获取执行权;但是,并不能保证 让出CPU后其他等待线程一定能拿到执行权。
这里是两个线程交叉打印。
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 10; i += 2) {
System.out.println("线程1:" + i);
Thread.yield();
}
});
Thread thread2 = new Thread(() -> {
for (int i = 1; i < 10; i += 2) {
System.out.println("线程2:" + i);
Thread.yield();
}
});
thread1.start();
thread2.start();
}
isAlive
isAlive()方法来检查线程是否已停止
public static void main(String[] args) {
Thread thread = new Thread(() -> {
while (true){
}
});
thread.start();
System.out.println("线程是否存活"+thread.isAlive());
}
synchronized
synchronized是Java中的关键字,是一种同步锁, 我们使用多线程通常是为了达到异步的目的,但是异步也带来一些安全问题, 对于一些多线程访问的资源,可能我们更多的是要求资源在某一时刻只被一个线程占有,这个时候就需要我们 做一些操作保证资源不被多个线程同时拥有。
public static void syn() {
new Thread(() -> {
System.out.println("syn1");
exe();
System.out.println("--");
}).start();
new Thread(() -> {
System.out.println("syn2");
exe();
System.out.println("--");
}).start();
}
static int i = 0;
private static synchronized void exe() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + ++i);
}
public static void main(String[] args) {
syn();
}
生产者消费者模式
生产者消费者模式是一个多线程同步问题的经典案例, 其目的是解决当多个线程共同去操作同一份数据时,使用线程的同步保证信息的同步性和安全性。这里使用 wait-notify 和 阻塞队列(ArrayBlockingQueue)实现。
需要注意的是如果使用LinkedBlockingQueue实现,需要考虑LinkedBlockingQueue默认Integer.MAX_VALUE大小容量, 如果生产者的速度一旦大于消费者的速度,也许还没有等到队列满阻塞产生,系统内存就有可能已被消耗殆尽了。
//wait-notify实现
private static void produceCustom() {
Queue<Long> queue = new LinkedList<>();
Thread produce = new Thread(() -> {
while (true) {
//不上锁会IllegalMonitorStateException
synchronized (queue) {
while (queue.size() == 10) {
try {
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("开始生产");
queue.add(System.currentTimeMillis());
queue.notifyAll();
}
}
});
Thread customer = new Thread(() -> {
while (true) {
synchronized (queue) {
while (queue.isEmpty()) {
try {
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("移除元素:" + queue.remove());
queue.notifyAll();
}
}
});
produce.start();
customer.start();
}
//阻塞队列实现生产者消费者
private static void produceCustomBlockQueue() {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
new Thread(() -> {
while (true) {
boolean res = queue.offer(i);
if (res) {
System.out.println("存放元素");
++i;
}
}
}).start();
new Thread(() -> {
while (true) {
try {
System.out.println("take:" + queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
//测试两种方式
public static void main(String[] args) {
//wait-notify实现
produceCustom();
//阻塞队列实现生产者消费者
produceCustomBlockQueue();
}
死锁
死锁的四个必要条件:(1) 互斥条件:一个资源每次只能被一个进程使用。(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
public static void main(String[] args) {
deadLock();
}
public static void deadLock() {
Object o = new Object();
Object o1 = new Object();
new Thread(() -> {
synchronized (o) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1) {
System.out.println("+");
}
}
}).start();
new Thread(() -> {
synchronized (o1) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o) {
System.out.println("-");
}
}
}).start();
}
转载:https://blog.csdn.net/qq_41871159/article/details/106564365