(九)java多线程退出、线程状态

1. 线程的合并

  • 语法:线程对象名.join()
  • 若代码为:线程2.join()表示:本线程先等待,让线程2先进行完后,再进行本线程
  • 需要抛出异常
    请添加图片描述
//线程合并join()
public class Test1 {
    /**
     * CountDownLatch:可以实现相同的效果
     * @param args
     */
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 10; i++) {
                    System.out.println(Thread.currentThread().getName() + "\t" + i);
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        });
        t1.start();
        try {
            t1.join();//t1线程插队,先进行t1线程,再进行主线程。
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(Thread.currentThread().getName() +  "-  main orver");
    }
}

2. 线程的退出

2.1 stop

不推荐,线程退出方式粗暴,不管线程正在执行的任务,直接退出,可能丢失数据

//探究使用stop方法结束线程
import java.util.Scanner;
public class Test2 {
    public static void main(String[] args) {
        MyTask myTask = new MyTask();
        Thread thread = new Thread(myTask);
        thread.start();
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入(1/0)0表示退出");
        int i = scanner.nextInt();
        if (i == 0){
            thread.stop();  //非优雅退出,现在不推荐使用stop()
        }
        System.out.println("main over");
    }
}
class MyTask implements Runnable{
    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

2.2 中断信号

  • interrupt线程对象名.interrupt();,本线程给线程对象名发送中断信号(true)

    • 如果线程在阻塞状态,比如:sleep(),join(),wait(),这时接收到中断信号会抛出一个异常 InterruptException ,同时中断信号清除(false)
  • static interrupted():得到中断信号(true),然后把中断信号设置成false

    • 获取当前子线程Thread.currentThread()
  • isInterrupted():得到中断信号,不会更改信号

3. 线程的调度和时间片

3.1 线程的时间片

由于 CPU 的计算频率非常高,每秒计算数十亿次,于是,可以将 CPU 的时间从毫秒的维度进行分段,每一小段叫做一个 CPU 时间片。不同的操作系统、不同的处理器,线程的 CPU 时间片长度都不同。

3.2 线程的调度

操作系统的线程一个时间片的时间长度为 20 毫秒(比如 Windows XP),在一个 2GHz 的 CPU 上,那么一个时间片可以进行计算的次数是: 20 亿/(1000/20) =4 千万次,也就是说,一个时间片内的计算量是非常巨大的。 目前操作系统中主流的线程调度方式大都是:基于 CPU 时间片方式进行线程调度。线程只有得到 CPU 时间片,才能执行指令,处于执行状态;没有得到时间片的线程,处于就绪状态,等待系统分配下一个 CPU 时间片。由于时间片非常短,在各个线程之间快速地切换,表现出来特征是很多个线程在“同时执行”或者“并发执行”。

3.3 线程的调度模型

线程的调度模型,目前主要分为两种调度模型:分时调度模型抢占式调度模型

  • 分时调度模型
    系统平均分配 CPU 的时间片,所有线程轮流占用 CPU。分时调度模型在时间片调度的分配上,所有线程人人平等。 下图就是一个分时调度的简单例子:三个线程,轮流得到 CPU 时间片;一个线程执行时,另外两个线程处于就绪状态
    请添加图片描述
  • 抢占式调度模型
    系统按照线程优先级分配 CPU 时间片。优先级高的线程,优先分配 CPU 时间片;如果所有的就绪线程的优先级相同,那么会随机选择一个;优先级高的线程获取的 CPU 时间片相对多一些。 由于目前大部分操作系统都是使用抢占式调度模型进行线程调度。 Java 的线程管理和调度是委托给了操作系统完成的,与之相对应, Java 的线程调度也是使用抢占式调度模型

4. 线程状态

4.1 操作系统中

请添加图片描述
分别为:新建 就绪 运行 阻塞 死亡

4.2 Java中

  • NEW

    • new Thread()创建线程,此时线程还未启动
  • RUNNABLE,

    • 进入可执行:包含操作系统的就绪、运行两种状态
    • 调用了线程的start()实例方法后,线程就处于该状态
    • 调用线程的 start()方法,此线程进入就绪状态。
    • 当前线程的执行时间片用完。
    • 线程睡眠(sleep)操作结束。
    • 对其他线程合入(join)操作结束。
    • 等待用户输入结束。
    • 线程争抢到对象锁(Object Monitor)。
    • 当前线程调用了 yield 方法出让 CPU 执行权限。
  • BLOCKED

    • 阻塞 分为I/O输出阻塞线程等待获取锁
  • WAITING

    • 等待(无限期等待)进入该状态有三种方式
      • Object.wait() 方法,对应的唤醒方式为: Object.notify()
      • Thread.join() 方法,对应的唤醒方式为:被合入的线程执行完毕。
      • LockSupport.park() 方法,对应的唤醒方式为:
  • TIMED_WAITING,

    • 限时等待 ,有明确的等待时间
    • Thread.sleep(time) 方法,对应的唤醒方式为: sleep 睡眠时间结束
    • Object.wait(time) 方 法 , 对 应 的 唤 醒 方 式 为 : 调 用 Object.notify()
    • LockSupport.parkNanos(time)/parkUntil(time) 方法,对应的唤醒方式为:线程调用配套的 LockSupport.unpark(Thread)方法结束,或者线程停止(park)时限结束。
  • TERMINATED;

    • 线程结束或线程发生异常

Java中的BLOCKED、WAITING、TIMED_WAITING等价于操作系统中的阻塞状态
进入者三个状态的线程都会让出CPU的使用权,另外,等待或阻塞状态被唤醒后,进入Ready状态,需要重新获取时间片才能接着运行。

5. 多线程自增案例

4个线程自增一个堆(共享的)里的对象的值

public class Plus {
    private int amount;
    public int getAmount() {
        return amount;
    }
    public void selfAmount(){
        amount++;
    }
}

public class PlusTask implements Runnable{
    private Plus plus;
    public PlusTask() {}//无参构造函数
    public PlusTask(Plus plus) {//构造函数
        this.plus = plus;//此时plus为传入的plus
    }
    @Override
    public void run() {
        for (int i = 0; i < 100000000; i++) {
            this.plus.selfAmount();
        }
    }
    public Plus getPlus() {
        return plus;
    }
}

public class Test1 {
    public static void main(String[] args) throws InterruptedException {
        Plus plus = new Plus();
        PlusTask plusTask = new PlusTask(plus);//将plus的地址值传入,
        Thread t1 = new Thread(plusTask);//控制同一个PlusTask
        Thread t2 = new Thread(plusTask);
        Thread t3 = new Thread(plusTask);
        Thread t4 = new Thread(plusTask);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        /*
        如果不加join()方法,那么main线程,
        t1,t2,t3,t4线程将同时进行,
        main线程不会等待子线程结束后才打印
         */
        t1.join();
        t2.join();
        t3.join();
        t4.join();
        System.out.println("实际值" + plus.getAmount());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值