您的位置:首页 > 健康 > 美食 > 制作视频的网站软件_郑州富士康目前状况_seo网站优化助理_短视频seo搜索优化

制作视频的网站软件_郑州富士康目前状况_seo网站优化助理_短视频seo搜索优化

2025/1/8 8:12:22 来源:https://blog.csdn.net/kwok924/article/details/142926538  浏览:    关键词:制作视频的网站软件_郑州富士康目前状况_seo网站优化助理_短视频seo搜索优化
制作视频的网站软件_郑州富士康目前状况_seo网站优化助理_短视频seo搜索优化

本文是学习尚硅谷周阳老师《JUC并发编程》的总结(文末有链接)。

在 Java 并发编程:实现线程等待和唤醒有 3 种方法:

  • Object 类的 wait/notify 方法
  • Condition 类的 await/signal 方法
  • LockSupport 类的 park/unpark 方法

Object 类的 wait/notify 方法

实现代码:

package juc;import java.util.concurrent.TimeUnit;public class LockSupportDemo {public static void main(String[] args){Object objectLock = new Object();new Thread(() -> {synchronized (objectLock) {System.out.println(Thread.currentThread().getName() + " come in");try {objectLock.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " 被唤醒");}}, "t1").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {synchronized (objectLock) {System.out.println(Thread.currentThread().getName() + " come in");objectLock.notify();System.out.println(Thread.currentThread().getName() + " 发出通知");}}, "t2").start();}
}

注意:

  • wait 和 notify 方法必须要在同步块或同步方法里面,且成对出现使用
  • 必须先 wait 后 notify

Condition 类的 await/signal 方法

实现代码:

package juc;import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockSupportDemo {public static void main(String[] args){Lock lock = new ReentrantLock();Condition condition = lock.newCondition();new Thread(() -> {lock.lock();try {System.out.println(Thread.currentThread().getName() + " come in");condition.await();System.out.println(Thread.currentThread().getName() + " 被唤醒");} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}, "t1").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {lock.lock();try {System.out.println(Thread.currentThread().getName() + " come in");condition.signal();System.out.println(Thread.currentThread().getName() + " 发出通知");} finally {lock.unlock();}}, "t2").start();}
}

注意:

  • await 和 signal方法必须先获取锁,即在 lock/unlock 对里面才能使用
  • 必须先 await 后 signal

LockSupport 类的 park/unpark 方法

上述两种方法在使用时都有如下限制:

  • 线程要先获得并持有锁,必须在锁块(synchronized 或 lock)中
  • 必须要先等待再唤醒,线程才能够被唤醒

因此才出现了 LockSupport 类,通过此类可以不受上面的限制更方便地实现线程等待和唤醒。

LockSupport 是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,阻塞之后也有对应的唤醒方法,底层调用的是 UnSafe 类的 native 代码。

LockSupport 提供 park() 和 unpark() 方法提供阻塞线程和唤醒线程的功能。

实现代码:

package juc;import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;public class LockSupportDemo {public static void main(String[] args){Thread t1 = new Thread(() -> {System.out.println(Thread.currentThread().getName() + " come in");LockSupport.park();System.out.println(Thread.currentThread().getName() + " 被唤醒");}, "t1");t1.start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {System.out.println(Thread.currentThread().getName() + " come in");LockSupport.unpark(t1);System.out.println(Thread.currentThread().getName() + " 发出通知");}, "t2").start();}
}

通过 LockSupport 类可以先 unpark 给 t1 线程颁发许可证,等到 t1 运行到 park 时直接放行通过,不会阻塞。

package juc;import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;public class LockSupportDemo {public static void main(String[] args){Thread t1 = new Thread(() -> {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " come in");LockSupport.park();System.out.println(Thread.currentThread().getName() + " 被唤醒");}, "t1");t1.start();new Thread(() -> {System.out.println(Thread.currentThread().getName() + " come in");LockSupport.unpark(t1);System.out.println(Thread.currentThread().getName() + " 发出通知");}, "t2").start();}
}

输出:

t2 come in
t2 发出通知
t1 come in
t1 被唤醒Process finished with exit code 0

LockSupport 和每个使用它的线程都有一个许可(permit)关联,每个线程都有一个相关的 permit ,permit 最多只有 1 个,重复调用 unpark() 也不会累积 permit。

可以通过如下代码验证:

package juc;import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;public class LockSupportDemo {public static void main(String[] args){Thread t1 = new Thread(() -> {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " come in");LockSupport.park();System.out.println(Thread.currentThread().getName() + " 通过第1个park");LockSupport.park();System.out.println(Thread.currentThread().getName() + " 通过第2个park");System.out.println(Thread.currentThread().getName() + " 被唤醒");}, "t1");t1.start();new Thread(() -> {System.out.println(Thread.currentThread().getName() + " come in");LockSupport.unpark(t1);LockSupport.unpark(t1);System.out.println(Thread.currentThread().getName() + " 发出通知");}, "t2").start();}
}

输出:

t2 come in
t2 发出通知
t1 come in
t1 通过第1个park// 程序阻塞到这里

虚假唤醒

实现线程等待和唤醒时需要注意是否有虚假唤醒问题。

例如下面示例代码,本意是线程 A 和 线程 C 在 number 为 0 时对其加 1,线程 B 和线程 D 在 number 为 1 时对其减 1,最后结果预期是 0。

package juc;/*** 虚假唤醒Demo*/class share {private int number = 0;public synchronized void increment() throws InterruptedException {if (number != 0) {this.wait();}number++;System.out.println(Thread.currentThread().getName() + " : " + number);this.notifyAll();}public synchronized void decrement() throws InterruptedException {if (number == 0) {this.wait();}number--;System.out.println(Thread.currentThread().getName() + " : " + number);this.notifyAll();}
}public class SpuriousWakeupDemo {public static void main(String[] args) {share share = new share();new Thread(() -> {for (int i = 0; i < 10; i++) {try {share.increment();} catch (InterruptedException e) {e.printStackTrace();}}}, "A").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {share.decrement();} catch (InterruptedException e) {e.printStackTrace();}}}, "B").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {share.increment();} catch (InterruptedException e) {e.printStackTrace();}}}, "C").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {share.decrement();} catch (InterruptedException e) {e.printStackTrace();}}}, "D").start();}
}

实际运行结果可能是:

A : 1
D : 0
C : 1
B : 0
C : 1
B : 0
C : 1
B : 0
C : 1
B : 0
C : 1
B : 0
C : 1
B : 0
C : 1
B : 0
C : 1
B : 0
C : 1
B : 0
C : 1
B : 0
D : -1
D : -2
D : -3
D : -4
D : -5
D : -6
D : -7
D : -8
D : -9
A : -8// 程序还可能阻塞住

出现这个问题的原因就是虚假唤醒,上述代码运行情况可能是:

  1. 线程 A 先抢到锁,将 number 加 1 ,number 为 1,通知其它线程,第 1 次方法执行结束,释放锁(因为 synchronized 是加在方法上面的,方法执行完后释放锁)

  2. 线程 C 抢到锁,判断 if (number != 0) 不满足条件,调用 this.wait() 等待,释放锁;

  3. 线程 A 抢到锁,第 2 次执行方法,判断 if (number != 0) 不满足条件,调用 this.wait() 等待,释放锁;

  4. 线程 B 抢到锁,将 number 减 1, number 为 0,通知其它线程,第 1 次方法执行结束释放锁;

  5. 线程 A 抢到锁,收到线程 B 的通知结束等待,执行 wait 后面的代码将 number 加 1, number 为 1,通知其它线程,第 2 次方法执行结束,释放锁;

  6. 线程 C 抢到锁,收到线程 A 的通知结束等待,执行 wait 后面的代码将 number 加 1,number 为 2,通知其它线程,第 2 次方法执行结束,释放锁;

    … …

    这里就出现了 number 大于 1 的情况,虽然和上面输出结果不一样,但都是不符合预期的结果。

解决方法是将判断条件 if (number == 0) 改为 while (number == 0):

package juc;/*** 虚假唤醒Demo*/class share {private int number = 0;public synchronized void increment() throws InterruptedException {while (number != 0) {this.wait();}number++;System.out.println(Thread.currentThread().getName() + " : " + number);this.notifyAll();}public synchronized void decrement() throws InterruptedException {while (number == 0) {this.wait();}number--;System.out.println(Thread.currentThread().getName() + " : " + number);this.notifyAll();}
}public class SpuriousWakeupDemo {public static void main(String[] args) {share share = new share();new Thread(() -> {for (int i = 0; i < 10; i++) {try {share.increment();} catch (InterruptedException e) {e.printStackTrace();}}}, "A").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {share.decrement();} catch (InterruptedException e) {e.printStackTrace();}}}, "B").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {share.increment();} catch (InterruptedException e) {e.printStackTrace();}}}, "C").start();new Thread(() -> {for (int i = 0; i < 10; i++) {try {share.decrement();} catch (InterruptedException e) {e.printStackTrace();}}}, "D").start();}
}

修改后的输出为:

A : 1
D : 0
C : 1
B : 0
C : 1
B : 0
C : 1
B : 0
C : 1
B : 0
C : 1
B : 0
C : 1
B : 0
C : 1
B : 0
C : 1
B : 0
C : 1
B : 0
C : 1
B : 0
A : 1
D : 0
A : 1
D : 0
A : 1
D : 0
A : 1
D : 0
A : 1
D : 0
A : 1
D : 0
A : 1
D : 0
A : 1
D : 0
A : 1
D : 0Process finished with exit code 0

参考

  • 尚硅谷JUC并发编程: 第 50 - 55 节 (https://www.bilibili.com/video/BV1ar4y1x727?spm_id_from=333.788.videopod.episodes&vd_source=9266b9af652d5902d068c94b9d60116f&p=50)
  • 【尚硅谷】大厂必备技术之JUC并发编程:第10节(https://www.bilibili.com/video/BV1Kw411Z7dF?p=10&spm_id_from=333.788.videopod.episodes&vd_source=9266b9af652d5902d068c94b9d60116f)

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com