1.park、unpark概念及使用
1.1概念
在Java并发编程中,park和unpark是LockSupport类提供的两个静态方法,用于线程的阻塞和解除阻塞。
park方法用于阻塞当前线程,使其进入等待状态,而unpark方法用于解除被park方法阻塞的线程。这两个方法通常用于实现线程间的同步和通信, 可以替代传统的wait和notify机制,提供更灵活的方式来控制线程的阻塞和唤醒。
park方法的使用
当线程调用park方法时,它会阻塞当前线程,直到以下情况之一发生:
1.另一个线程调用了相应线程的unpark方法;
2.中断发生;
3.在被禁用的情况下调用park,并且在调用unpark之前已经有了许可;
*park方法可以重载,提供不同的版本,如parkNanos和parkUntil,以支持 超时 和 特定时间点 的阻塞。
unpark方法的使用:unpark方法用于解除被park方法阻塞的线程。
1..如果给定线程尚未被阻塞,调用 unpark 方法将设置该线程的许可,这样后续对park的调用将立即返回;
2.如果给定线程已经被park,则调用 unpark 将使其解除阻塞。
1.2Demo
public class parkDemo01 {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 开始执行");
LockSupport.park();// 阻塞当前线程
System.out.println(Thread.currentThread().getName() + " 执行完成");
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 开始执行");
System.out.println(Thread.currentThread().getName() + " 执行完成");
}
});
thread1.start();
thread2.start();
Thread.sleep(3000);// 等待 thread2 的完成
System.out.println("主线程 唤醒 thread1");
LockSupport.unpark(thread1);// unpark 唤醒 thread1
thread1.join();// 等待thread1的完成
System.out.println("主线程 执行");
}
}
1.3执行结果
Thread-1成功执行;Thread-0在启动后调用park方法阻塞,待主线程唤醒后,Thread-0继续执行。
2.join概念及使用
2.1概念
join()方法用于等待调用该方法的线程完成执行。当一个线程调用另一个线程的join()方法时,它会暂停自己的执行,直到被调用的线程完成执行才会继续执行。
join()方法特点:
1.等待线程完成:调用join()方法的线程会等待被调用的线程完成执行。这在需要等待其他线程完成某个任务后再继续执行的情况下非常有用。
2.同步操作:通过join()方法,可以实现线程的同步操作。主线程可以调用子线程的join()方法,确保子线程在完成后再继续执行主线程的后续操作。
3.协调线程顺序:通过多次调用join()方法,可以协调多个线程的执行顺序,确保它们按照特定的顺序执行。
4.异常处理:如果被调用的线程抛出了未捕获的异常,调用它的线程可以通过捕获该异常来处理或做出相应的操作。
2.2Demo
public class JoinDemo01 {
public static void main(String[] args) throws InterruptedException {
// 线程的总体执行顺序: thread1 -> thread2 -> thread3
Thread thread1 = new Thread(() -> {
System.out.println("线程1执行了");
});
Thread thread2 = new Thread(() -> {
try {
thread1.join();// thread1 -> thread2
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("线程2执行了");
});
Thread thread3 = new Thread(() -> {
try {
thread2.join();// thread2 -> thread3
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("线程3执行了");
});
// 下面的start方法可以按任意顺序写
thread3.start();
thread2.start();
thread1.start();
thread3.join();
System.out.println("主线程执行了");
}
}
2.3执行结果
测试用例共三个线程,设定执行顺序为 thread1 -> thread2 -> thread3
3.wait、notify概念及使用
3.1概念
使用 notify 和 wait 需要获取对象锁才可以,需要配合 synchronized 使用。
* wait 会释放对象锁,sleep不会释放
* notify 唤醒竞争该对象锁的进程(唤醒1个)
* notifyAll 唤醒竞争该对象锁的所有进程
3.2Demo
public class WaitNotifyDemo {
public static void main(String[] args) throws InterruptedException {
// 使用Obeject 即可作为对象锁
Object o = new Object();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 开始执行");
synchronized (o){
try {
o.wait();// 释放锁
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println(Thread.currentThread().getName() + " 执行完成");
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);// 确保thread1先执行
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + " 开始执行");
System.out.println(Thread.currentThread().getName() + " 执行完成");
synchronized (o){
o.notify();
}
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("主线程 执行");
}
}
3.3执行结果
Thread-0先启动,然后启动Thread-1;Thread-0调用wait方法被阻塞,Thread-1调用notify唤醒Thread-0。