JUC学习笔记——进程与线程( 五 )

最后我们讲解一个项目中常用的案例:
// 在没有利用 cpu 来计算时,不要让 while(true) 空转浪费 cpu,这时可以使用 yield 或 sleep 来让出 cpu 的使用权 给其他程序while(true) {try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}}// - 可以用 wait 或 条件变量达到类似的效果// - 不同的是,后两种都需要加锁,并且需要相应的唤醒操作 , 一般适用于要进行同步的场景// - sleep 适用于无需锁同步的场景// wait实现synchronized(锁对象) {while(条件不满足) {try {锁对象.wait();} catch(InterruptedException e) {e.printStackTrace();}}// do sth...}// 条件变量实现lock.lock();try {while(条件不满足) {try {条件变量.await();} catch (InterruptedException e) {e.printStackTrace();}}// do sth...} finally {lock.unlock();}Priority下面我们来介绍线程的优先级设置:

  • 首先优先级的大小从1~10,默认为5
  • 优先级只是起到一定影响作用,不会对线程的执行顺序起到绝对作用,只是优先级越高其获得CPU可能性越大
我们给出简单代码示例:
// 我们同样需要在单核CPU下进行@Slf4j(topic = "c.TestYield")public class TestYield {public static void main(String[] args) {Runnable task1 = () -> {int count = 0;for (;;) {System.out.println("---->1 " + count++);}};Runnable task2 = () -> {int count = 0;for (;;) {System.out.println("---->2 " + count++);}};Thread t1 = new Thread(task1, "t1");Thread t2 = new Thread(task2, "t2");t1.setPriority(Thread.MIN_PRIORITY);t2.setPriority(Thread.MAX_PRIORITY);t1.start();t2.start();}}// 运行结果:我们会发现t2的执行次数明显高于t1---->1 283500---->2 374389join首先我们需要了解join的作用:
  • 用于等待直至所指线程结束为止
我们采用一个简单的例子进行解释:
// 下述例子我们希望打印线程t1的r的值 , 但是我们都知道main和t1线程都是同时运行的,并且t1等待了1s,所以这时的r是没有赋值的,为0static int r = 0;public static void main(String[] args) throws InterruptedException {test1();}private static void test1() throws InterruptedException {log.debug("开始");Thread t1 = new Thread(() -> {log.debug("开始");sleep(1);log.debug("结束");r = 10;});t1.start();log.debug("结果为:{}", r);log.debug("结束");}// 但是我们可以选择使用join方法来使主线程等待,知道t1线程结束后再去运行main线程static int r = 0;public static void main(String[] args) throws InterruptedException {test1();}private static void test1() throws InterruptedException {log.debug("开始");Thread t1 = new Thread(() -> {log.debug("开始");sleep(1);log.debug("结束");r = 10;});t1.start();log.debug("结果为:{}", r);t1.join();log.debug("结果为:{}", r);log.debug("结束");}// 注意我们如果采用sleep或许也可以实现相同的效果,但是很难准确确定其实际线程的结束时刻,所以正常情况下无法使用sleep此外我们还需要讲解join的其他几个性质:
  • 我们可以借助join来实现线程之间的同步操作
  • 当多个线程都采用join时 , 我们需要等所有线程都结束后继续运行
  • join是可以设置时效性的 , 当超过时则不再等待而是直接运行 , 若不超过就按照线程结束时间计算
我们通过几个案例解释上述性质:
// 借助join完成线程之间的同步操作(其实前面第一个例子就是同步操作,我们需要先完成t1线程才能执行main线程的内容)static int r = 0;public static void main(String[] args) throws InterruptedException {test1();}private static void test1() throws InterruptedException {log.debug("开始");Thread t1 = new Thread(() -> {log.debug("开始");sleep(1);log.debug("结束");r = 10;});t1.start();t1.join();log.debug("结果为:{}", r);log.debug("结束");}// 多个join需要等待所有线程完成// 如下述,一个需要1s,一个需要2s,但由于同时进行,所以我们只需要2s就可以全部执行 , 然后再执行mainstatic int r1 = 0;static int r2 = 0;public static void main(String[] args) throws InterruptedException {test2();}private static void test2() throws InterruptedException {Thread t1 = new Thread(() -> {sleep(1);r1 = 10;});Thread t2 = new Thread(() -> {sleep(2);r2 = 20;});long start = System.currentTimeMillis();t1.start();t2.start();t1.join();t2.join();long end = System.currentTimeMillis();log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);}// join是可以设置时效性的,当超过时则不再等待而是直接运行,若不超过就按照线程结束时间计算static int r1 = 0;static int r2 = 0;public static void main(String[] args) throws InterruptedException {test3();}public static void test3() throws InterruptedException {Thread t1 = new Thread(() -> {// 这里设置时间,若低于1.5s按sleep时间执行;若高于1.5s则在1.5s时执行sleep(1);r1 = 10;});long start = System.currentTimeMillis();t1.start();// 线程执行结束会导致 join 结束t1.join(1500);long end = System.currentTimeMillis();log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);}

推荐阅读