Java多线程之深入了解Thread类以及方法的应用

文章目录

  • 一、Thread类的属性和方法
  • 二、Thread类中方法的应用
      • (1)利用join()实现并发排序
      • (2)yield()的应用
      • (3)线程中断 interrupt


提示:以下是本篇文章正文内容,Java系列学习将会持续更新

一、Thread类的属性和方法

Java多线程之深入了解Thread类以及方法的应用_第1张图片

二、Thread类中方法的应用

(1)利用join()实现并发排序

Java多线程之深入了解Thread类以及方法的应用_第2张图片
四个线程分别对各自的每一段进行排序 (Arrays.sort)
当四个线程的工作全部完成之后,即{ t1.join(); t2.join(); t3.join(); t4.join() }
执行4路归并…(省略)
前提: 一个进程下的线程,共享的是同一块内存资源

import java.util.Arrays;
/**
 * 多线程并发排序
 */
public class ConcurrentSort {
    // 进行排序的线程
    static class SortWorker extends Thread {
        private final long[] array;
        private final int fromIndex;
        private final int toIndex;
        // 利用构造方法,将待排序的数组区间情况,传入
        // 对 array 的 [fromIndex, toIndex) 进行排序
        SortWorker(long[] array, int fromIndex, int toIndex) {
            this.array = array;
            this.fromIndex = fromIndex;
            this.toIndex = toIndex;
        }

        @Override
        public void run() {
            // 具体的排序过程,这里使用 Array.sort 做模拟
            Arrays.sort(array, fromIndex, toIndex);
        }
    }

    // 记录排序耗时
    public static void main(String[] args) throws InterruptedException {
        long[] array = ArrayHelper.generateArray(4000_0000);

        // 分别是
        // [0, 1000_0000)
        // [1000_0000, 2000_0000)
        // [2000_0000, 3000_0000)
        // [3000_0000, 4000_0000)
        long s = System.currentTimeMillis();
        Thread t1 = new SortWorker(array, 0, 1000_0000);
        t1.start();     // 先让 t1 进入就绪队列

        Thread t2 = new SortWorker(array, 1000_0000, 2000_0000);
        t2.start();     // 再让 t2 进入就绪队列

        Thread t3 = new SortWorker(array, 2000_0000, 3000_0000);
        t3.start();     // 然后让 t3 进入就绪队列

        Thread t4 = new SortWorker(array, 3000_0000, 4000_0000);
        t4.start();     // 最后让 t4 进入就绪队列

        // 把 4 个线程放入就绪队列的顺序是有序的
        // 但,哪个线程会被调度器选中,我们就不知道了,可以认为是随机的

        // 4 个线程开始分别的进行排序了
        // 等待 4 个线程全部排序完毕,这 4 个 join 没有前后顺序
        t1.join();
        t2.join();
        t3.join();
        t4.join();

        // 4 个线程一定全部结束了
        // TODO:进行 4 路归并,将 4 个有序数组,归并成一个有序数组
        long e = System.currentTimeMillis();
        // 计算耗时
        long elapsed = e - s;
        System.out.println(elapsed);
    }
}

回到目录…

(2)yield()的应用

 yield()主要用于执行一些耗时较久的计算任务时,为让防止计算机处于“卡顿”的现象,时不时的让出一些CPU资源,给OS内的其他进程。

让出CPU,会引导OS进行新的一轮线程调度。(上下文切换=保存当前线程的PC)
以后,等我们被分配CPU时,恢复我们之前保存的PC
现象:我们线程的代码是接着上一次的继续执行。

线程是OS调度的单位。
线程是OS分配CPU资源的单位。
我们程序员的角度来讲:线程是抢夺CPU的工具。

public class Main {
    static class PrintWhoAmI extends Thread {
        private final String who;

        public PrintWhoAmI(String who) {
            this.who = who;
        }

        @Override
        public void run() {
            while (true) {
                System.out.println("我是 " + who);
                if (who.equals("张三")) {
                    // 线程并没有结束和暂停,而是尽可能的让出CPU
                    Thread.yield();
                }
            }
        }
    }

    public static void main(String[] args) {
        PrintWhoAmI 张三 = new PrintWhoAmI("张三");
        PrintWhoAmI 李四 = new PrintWhoAmI("李四");
        张三.start();
        李四.start();
    }
}

回到目录…

(3)线程中断 interrupt

事件
  A叫来B干活。
  一些突发情况发生了,需要让B停止工作(即使分配它的任务还没有完成)
  所以A需要让B停止。

中断的方法
  ①暴力停止,直接将B给kill掉。不知道B的工作进行到哪一步(不可控)。
    void stop(); // 已经被废弃
  ②优雅的协商,A给B发送终止的信号,B收到信号后主动停止。
    void interrupt();

接收停止信号时,B的状态
  情况1: B正在正常执行代码,可以通过一个方法来判定
     静态方法 Thread.interrupted(); // 检测当前线程是否被中止
        true:有人让我们停止。
        false: 没人让我们停止
     B的代码类似: .
        while (true) {
          //写代码
          //看一眼手机,有没有人让我们停止
          if (Thread.interrupted()) {
            //有人让我们停止,停止
            break; //也可以其他方式
          }
        }
     至于B要不要停,完全是代码控制的

  情况2: B可能正处于休眠状态(比如sleep、join) ,意味着B无法立即执行
     此刻,JVM的处理方式是,以异常形式通知B : InterruptedException
     当B处于休眠状态时,捕获了InterruptedException异常,代表有人让我们停止。
     具体要不要停,什么时候停,怎么停,完全自己做主

import java.util.Scanner;
import java.util.concurrent.TimeUnit;
/**
 * 中断线程的方法, interrupt()的应用
 */
public class Main_Interrupt {
    static class B extends Thread {
        @Override
        public void run() {
            while (true) {

                for (int i = 0; i < 1000; i++) {
                    System.out.println("我正在写代码");
                }
                // 休息之前,检查有没有人让我们停止
                if (Thread.interrupted()) {
                    // 有人让我们停止
                    break;      // 跳出 while 循环
                }

                // 每写 1000 行,休息 1s
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    // 休息期间,有人让我们停止
                    break;      // 跳出 while 循环      // 大概率是这里
                }

                // 休息之后,检查有没有人让我们停止
                if (Thread.interrupted()) {
                    // 有人让我们停止
                    break;      // 跳出 while 循环
                }
            }

            System.out.println("B 停下来了");
        }
    }

    public static void main(String[] args) {

        B b = new B();
        b.start();

        // 让 B 停止工作
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入任意字符,停止 B 的工作");
        scanner.nextLine(); // 主线程阻塞在这里,直到用户输入内容

        b.interrupt();      // 让 b 停止
    }
}

回到目录…


总结:
提示:这里对文章进行总结:
以上就是今天的学习内容,本文是Java多线程的学习,认识了Thread类中常见的属性和方法,还有部分方法的应用。之后的学习内容将持续更新!!!

你可能感兴趣的:(Java多线程与并发,java,算法,开发语言)