【多线程基础】Thread类的基本用法

Hi~!这里是奋斗的明志,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~
🌱🌱个人主页:奋斗的明志
🌱🌱所属专栏:Java多线程

在这里插入图片描述

📚本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为展示我的学习过程及理解。文笔、排版拙劣,望见谅。

在这里插入图片描述

一、Thread 类及常见方法

Thread类是JVM用来管理线程的一个类,换句话数,每个线程都有一个唯一的Thread对象与之对应。
每个执行流,也需要有一个对象来描述,类似下图所示,而Thread类的对象就是用来描述一个线程执行流的,JVM会将这些Thread对象组织起来,用于线程调度,线程管理。


在这里插入图片描述


二、Thread的常见构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable对象创建线程对象
Thread(String name)创建线程对象,并命名
Thread(Runnable target,String name)使用Runnable对象创建线程,并命名
【了解】Thread(ThreadGroup group,Runnable target)线程可以被用来分组管理,它允许创建一个新线程,并指定该线程所属的线程组和线程要执行的任务。
public static void main(String[] args) {
        //创建线程
        Thread t1 = new Thread();
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {

            }
        });
        Thread t3 = new Thread("明志");
        Thread t4 = new Thread(new Runnable() {
            @Override
            public void run() {
                
            }
        }, "明志");
    }

【通过jconsole进行观察】

在这里插入图片描述


public static void main(String[] args) {
        Thread t = new Thread(() -> {
            while (true) {
                System.out.println("hello!!!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "自定义线程");
        t.start();
    }

在这里插入图片描述

  • 上述代码中在main中引入死循环,就可以看见main线程了

main线程是 jvm 中通过 c++ 代码创建出来的
没有通过 java 中的 Thread 类创建的,就不会重写 run 方法
咱们自己的线程是通过 java 代码创建的
无论是自己创建的线程,还是 jvm 内部创建的线程,核心在于要制定一个任务给线程,让线程知道自己要干什么

三、Thread的几个属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()
  • ID是线程的唯一标识,不同线程不会重复(这里的ID和系统中PCB上的id是不同的,是JVM自己设计的一套id体系)(java代码无法获取到PCB上的id,与PCB上的是一一对应,但是编号不是一个体系)

  • 名称是各种调试工具用到

  • 状态表示线程当前所处的一个情况(就绪、阻塞)

  • 优先级高的线程理论上来说更容易调度到(这里java虽然提供了优先级接口,实际上就算修改了优先级现象也不明显,程序员很难干预到)

  • 关于后台线程,要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行(前台线程、后台线程类似手机里面的程序)

  • 线程的中断问题

前台线程:这样的线程如果不运行结束的话,此时java进程是一定不会结束
在java代码中,main线程就是前台线程,程序员创建的线程,默认情况下是前台线程,可以通过 setDaemon()方法把线程设置为后台线程
有的线程负责垃圾回收(gc)
后台线程:这样的线程,即使继续在执行,也不能阻止java进程结束


在这里插入图片描述


public static void main(String[] args) {
        Thread t = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        //把t设置为后台线程
        t.setDaemon(true);
        t.start();
    }

在这里插入图片描述

关于线程各种属性的设置,到要在start之前

  • 是否存活,即简单的理解,为run方法是否运行结束了(指的是系统中的线程PCB是否还存在,Thread 对象的生命周期和 PCB中的生命周期不一定完全一样)
public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        System.out.println(t.isAlive());
        t.start();
        Thread.sleep(1000);
        System.out.println(t.isAlive());
    }

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

public static void main(String[] args) {
        Thread t = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "在执行");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println(Thread.currentThread() + "即将死去");
        }, "自己定义的线程");
        System.out.println(Thread.currentThread().getName() + ":ID" + t.getId());
        System.out.println(Thread.currentThread().getName() + ":名称" + t.getName());
        System.out.println(Thread.currentThread().getName() + ":状态" + t.getState());
        System.out.println(Thread.currentThread().getName() + ":优先级" + t.getPriority());
        System.out.println(Thread.currentThread().getName() + ":后台线程" + t.isDaemon());
        System.out.println(Thread.currentThread().getName() + ":活着" + t.isAlive());
        System.out.println(Thread.currentThread().getName() + ":被中断" + t.isInterrupted());

        t.start();
//        while (t.isAlive()) {
//            System.out.println(Thread.currentThread().getName() + ":状态" + t.getState());
//        }
    }

四、启动一个线程 - start()

之前我们已经看到了如何通过覆写 run 方法创建一个线程对象,但线程对象被创建出来并不意味着线程就开始运行了。

  • 覆写 run 方法是提供给线程要做的事情的指令清单
  • 线程对象可以认为是把 李四、王五叫过来了
  • 而调用 start() 方法法,就是喊⼀声:”⾏动起来!“,线程才真正独⽴去执⾏了。
    在这里插入图片描述
public static void main(String[] args) {
        Thread t = new Thread(() -> {
            System.out.println("hello");
        });
        Thread t2 = new Thread(() -> {
            System.out.println("hello 2");
        });

        t.start();
        t2.start();

    }

在这里插入图片描述


五、中断(终止)一个线程

李四⼀旦进到工作状态,他就会按照⾏动指南上的步骤去进行工作,不完成是不会结束的。但有时我们需要增加⼀些机制,例如⽼板突然来电话了,说转账的对方是个骗⼦,需要赶紧停⽌转账,那张三该如何通知李四停⽌呢?这就涉及到我们的停⽌线程的⽅式了。

目前常见的有以下两种方式:

  1. 通过共享的标记来进行沟通
  2. 调用 interrupt() 方法来通知

示例一: 使用自定义的变量来作为标志位

需要给标志位上加 volatile 关键字(这个关键字的功能后面介绍).

private static class MyRunnable implements Runnable {
        public volatile boolean isQuit = false;

        @Override
        public void run() {
            while (!isQuit) {
                System.out.println(Thread.currentThread().getName() + "别管我,我忙着转账呢!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println(Thread.currentThread().getName() + "啊,险些误了大事");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyRunnable target = new MyRunnable();
        Thread thread = new Thread(target, "李四");
        System.out.println(Thread.currentThread().getName() + "让李四开始转账。");
        thread.start();
        Thread.sleep(10 * 1000);
        System.out.println(Thread.currentThread().getName()
                + ": ⽼板来电话了,得赶紧通知李四对⽅是个骗⼦!");
        target.isQuit = true;
    }

在这里插入图片描述


示例二: 使用 Thread.interrupted() 或者
Thread.currentThread().isInterrupted() 代替自定义标志位

Thread 内部包含了⼀个 boolean 类型的变量作为线程是否被中断的标记

private static class MyRunnable implements Runnable {

        @Override
        public void run() {
            while (!Thread.interrupted()) {
                System.out.println(Thread.currentThread().getName() + "别管我,我忙着转账呢!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println(Thread.currentThread().getName()
                            + ": 有内⻤,终⽌交易!");
                    // 注意此处的 break
                    break;
                }
            }
            System.out.println(Thread.currentThread().getName() + "啊,险些误了大事");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyRunnable target = new MyRunnable();
        Thread t = new Thread(target, "李四");
        System.out.println(Thread.currentThread().getName() + ":让李四开始转账。");
        t.start();
        Thread.sleep(10 * 1000);
        System.out.println(Thread.currentThread().getName() + ":老板来电话了,得赶紧通知李四对方是个骗子!!!");
        t.interrupt();
    }

在这里插入图片描述

thread 收到通知的方式有两种:

  1. 如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,清除中断标志
    当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择忽
    略这个异常, 也可以跳出循环结束线程.
  2. 否则,只是内部的⼀个中断标志被设置,thread 可以通过Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志
    这种方式通知收到的更及时,即使线程正在 sleep 也可以马上收到。

在这里插入图片描述

在这里插入图片描述

  • 14
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值