Java线程 - 详解(1)

news2024/12/23 14:48:32

一,创建线程

方法一:继承Thread类

class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("线程1");
    }
}

public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();//start()方法启动线程

        //和上面的方法一样,只不过使用匿名内部类实现
        Thread thread1 = new Thread(){
            @Override
            public void run() {
                System.out.println("线程2");
            }
        };
        thread1.start();

    }
}

方法二:实现Runnable接口

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("线程1");
    }
}
public class Test {
    public static void main(String[] args) {

        Thread thread = new Thread(new MyRunnable());
        thread.start();
        
        //匿名内部类实现
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程2");
            }
        });
        thread2.start();

        //lambda表达式实现
        Thread thread3 = new Thread(() -> System.out.println("线程3"));
        thread3.start();

    }
}

二,Thread类及常见方法

2.1 构造方法

方法说明
Thread()创建线程对象
Thread(Runnable  target)使用Runnable对象创建线程对象
Thread(String  name)创建线程对象并命名
Thread(Runnable  target,String  name)使用Runnable对象创建线程对象,并命名
Thread(ThreadGroup  group,Runnable  target)线程可以被用来分组管理,分好的组即为线程组(了解即可)

2.2 获取 Thread 的常见属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()
  • ID是线程的唯一标识,ID是JAVA分配的,不会出现重复的,与上篇博客中PCB结构中的pid不是同一个东西。
  • 名称是方便各种调试工具使用。
  • 后台线程不会影响线程的结束,前台线程会影响线程的结束,一般线程默认为前台线程,还有一个注意点——JVM会在一个进程的所有后台进程结束后,才会结束运行。
  • 是否存活,简单理解就是 run 方法是否运行结束。
public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            System.out.println("线程开始");
            try {
                Thread.sleep(1000);//休眠线程 xxx ms
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },"1号线程");
        thread.setDaemon(true);//设置为后台线程
        thread.start();
        System.out.println(thread.getName() + " " + thread.isAlive());
        Thread.sleep(3000);
        System.out.println("线程结束");
        System.out.println(thread.isAlive());
    }
}

 

2.3 start()  与  run() 的区别

作用功能:

  1. start()方法内部是会调用系统的API,在系统内核创建一个线程
  2. run()方法只是描述线程具体实现的任务(会在start创建好之后会自动被调用)

运行结果:

  1. start调用方法后, start方法内部会调用Java 本地方法(封装了对系统底层的调用)真正的启动线程,并执行run方法中的代码,run 方法执行完成后线程进入销毁阶段。
  2. run方法是一个类中的普通方法,主动调用和调用普通方法一样,会顺序执行一次

 2.4 中断一个线程

在Java中,终止/销毁一个线程的做法比较单一,就是尽快让 run 方法执行结束。

方法一: 在代码中手动创建一个标志位,来作为 run 的执行结束条件,比如在很多线程中,执行时间长往往是写了一个循环。

public class Demo {
    private static boolean isQuit = false;//通过类属性来控制
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            while (!isQuit){
                System.out.println("666");
            }
        });
        thread.start();
        Thread.sleep(1);
        isQuit = true;
    }
}

 注:这里的 isQuit 不能是局部变量,因为如果是局部变量就不满足 lambda表达式中的变量捕获语法。

但是上面的方法有两个缺点:1. 需要手动创建变量 2. 当线程内部在 sleep 的时候,主线程修改变量时,新线程内部不能及时响应,所以Java提供了另一种解决方法:

方法二:使用 interupt() 和 isInterrupted()

方法说明
public void interrupt()中断对象关联的线程,如果线程阻塞,则以异常方式通知,否则设置标志位
public static boolean interrupted()判断当前线程中的标志位是否设置,调用后清除标志位
public boolean isInterrupted()判断对象关联的线程的标志位是否设置,调用后不清除标志
public class Demo {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            // Thread.currentThread()作用是得到当前的线程
            // isInterrupted() 判断标识符是否为false
            //或者 while(!Thread.interrupted()) 效果一样
           while(!Thread.currentThread().isInterrupted()){
               System.out.println("线程工作中!");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();//打印异常
               }
           }
        });
        thread.start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();//将标识符设置为true
    }
}

但是报错后,该线程没有中断,而是继续执行,这是为什么呢?

这是因为当我们将 标识符设为true 时,正好线程在sleep,会触发sleep内部的一个异常,从而会将线程从sleep中唤醒,但是在sleep抛出异常的同时,它会自动删除刚才设置的 标志位,这将会使 interrupt 这一操作好像没有被执行。

为什么Java会这样设定呢? 因为 Java 是希望当 线程收到 "中断" 信号时,它能自由决定接下来要怎么处理,比如:我们可以在打印报错后 接着写一些代码 :

public class Demo {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            // Thread.currentThread()作用是得到当前的线程
            // isInterrupted() 判断标识符是否为false
            //或者 while(!Thread.interrupted()) 效果一样
           while(!Thread.currentThread().isInterrupted()){
               System.out.println("线程工作中!");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();//打印异常
                   System.out.println("还需要完成的操作...");//2.
                   break;//1.直接退出
               }
           }
        });
        thread.start();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        thread.interrupt();//将标识符设置为true
    }
}

 注:

  1. 如果没有sleep,就不会出现上述情况,线程会直接退出!!!
  2. 我们不建议使用 Thread.interrupted() 这种做法,因为该方法是静态方法,意味着所有的线程共用一个标识符,而我们使用的线程肯定不止一个,如果使用该方法,就乱套了!!!

2.5 等待一个线程 - join()

作用:让一个线程等待另一个线程执行结束后,在继续执行。本质上是控制线程结束的顺序。

方法作用
public void join()等待线程结束
public void join(long millis)等待线程结束,但是最多等待 millis 毫秒
public void join(long millis, int nanos)同上,但是精度更高
public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                System.out.println("线程工作中!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        System.out.println("main线程开始等待");
        thread.join();
        System.out.println("等待结束");
    }
}

 注:

  1. 要分清哪个线程是等待的,哪个线程是被等待的
  2. join() 会出现 "死等" 的情况,我们一般不会使用,建议使用有参数的,可以自定义等待时间

2.6 其他 

方法作用
public static Thread currentThread()返回当前线程对象的引用
public static void sleep(long millis) throws interruptedException当前线程休眠millis毫秒
public static void sleep(long millis, int nanos) throws interruptedException同上,精度更高

三,线程的状态

  •  NEW:  Thread 对象已经有了,start 方法还没有调用
  • TERMINATED:  Thread 对象还在,内核中的线程已经没了
  • RUNNABLE:  就绪状态 (线程已经在 CPU 上运行 / 线程正在排队等待去 CPU 上运行)
  • TIME_WAITING:  阻塞,由于 sleep 这种固定时间的方式产生阻塞
  • WAITING:  阻塞,由于 wait 这种不固定时间的方式产生阻塞
  • BLOCKED:  阻塞,由于锁竞争导致的阻塞
public class Demo2 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        // getState() 获得当前线程的状态
        System.out.println(thread.getState());
        thread.start();
        for (int i = 0; i < 3; i++) {
            System.out.println(thread.getState());
            Thread.sleep(200);
        }
        thread.join();
        System.out.println(thread.getState());
    }
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/929032.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于YOLOv8模型和DarkFace数据集的黑夜人脸检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOv8模型和DarkFace数据集的黑夜人脸检测系统可用于日常生活中检测与定位黑夜下的人脸&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目…

TCP--半连接队列和全连接队列

原文地址&#xff1a;https://plantegg.github.io/2020/04/07/%E5%B0%B1%E6%98%AF%E8%A6%81%E4%BD%A0%E6%87%82TCP–%E5%8D%8A%E8%BF%9E%E6%8E%A5%E9%98%9F%E5%88%97%E5%92%8C%E5%85%A8%E8%BF%9E%E6%8E%A5%E9%98%9F%E5%88%97–%E9%98%BF%E9%87%8C%E6%8A%80%E6%9C%AF%E5%85%AC%E…

分布式事务篇-2.3 Seata事务模式

文章目录 前言一、事务模式 是什么&#xff1f;二、Seata中的事务模式支持&#xff1a;2.1 AT 模式&#xff08;自动补偿型事务&#xff09;&#xff1a;2.1.1 AT 模型&#xff1a;2.1.2 AT 写隔离&#xff1a;2.1.3 AT 读隔离&#xff1a;2.1.3 AT 优缺点&#xff1a; 2.2 TCC…

图的应用(考研)

一、最小生成树 性质&#xff1a; &#xff08;1&#xff09;不唯一&#xff08;2&#xff09;权值之和是唯一的&#xff08;3&#xff09;删去一条边会变成非连通图&#xff0c;增加一条边会产生回路&#xff08;4&#xff09;最小生成树的边数为顶点数减一 1、Prim算法 m…

第P2周:彩色图片识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 我的环境&#xff1a; 语言环境&#xff1a;Python3.10.7编译器&#xff1a;VScode深度学习环境&#xff1a;TensorFlow 2.13.0 一、前期工作&#xff1a; …

生产制造型企业BOM搭建分析

导 读 ( 文/ 2358 ) 在上几篇文章中&#xff0c;我们讲到了基础的物料管理方法&#xff0c;在生产制造中&#xff0c;物料作为原材料&#xff0c;通过加工&#xff0c;结构组装成产品。那么加工、组装的依据将来源于设计人员出具的零件清单&#xff0c;也就是我们常说的BOM。 …

本地编译angular提示内存溢出

本地遇到编译angular时&#xff0c;报如下错误&#xff1a; FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory 两种解决办法&#xff0c;具体如下&#xff1a; 设置环境变量&#xff0c;见图&#xff1a; 直接在…

cortex-A7核LED灯实验--STM32MP157

实验目的&#xff1a;实现LED1 / LED2 / LED3三盏灯工作 一&#xff0c;分析电路图 1&#xff0c;思路 分析电路图可知&#xff1a; 网络编号 引脚编号 LED1 PE10 LED2 > PF10 LED3 > PE8 2&#xff0c;工作原理&#xff1a; 写1&#xff1a;LED灯亮&#xf…

小猫爪:嵌入式小知识17-XCP on CAN简介

小猫爪&#xff1a;嵌入式小知识17-XCP on CAN简介 0 目录1 前言2 XCP on CAN3 实战演练3.1 CONNECT3.2 GET_COMM_MODE_INFO和GET_STATUS3.3 GET_SEED和UNLOCK3.4 获取Slave信息3.5 SET_MTA和BUILD_CHECKSUM3.6 设置DAQ3.7 DAQ传输3.8 SHORT_UPLOAD3.9 标定3.10 FLASH Program …

算法笔记(三):数组

静态数组 动态数组 把动态数组封装为一个类 1、属性 public class DynamicArray implements Iterable<Integer> {private int size 0; // 逻辑大小(有效的元素个数)private int capacity 8; // 容量private int[] array {}; // 动态数组 }2、方法 1&#xff…

癌症预测新利器:弹性逻辑回归让健康更可控!

一、引言 癌症是全球范围内健康领域的重大挑战&#xff0c;早期预测和诊断对于提高治疗效果和生存率至关重要。在过去的几十年里&#xff0c;随着医学和数据科学的快速发展&#xff0c;基于机器学习和统计方法的癌症风险预测成为研究的热点。其中&#xff0c;弹性逻辑回归作为一…

数据结构之哈希

哈希 1. 哈希概念2. 哈希冲突3. 哈希冲突解决3.1 哈希表的闭散列3.2 哈希表的开散列 2. 哈希的应用2.1 位图2.2 布隆过滤器 哈希&#xff08;Hash&#xff09;是一种将任意长度的二进制明文映射为较短的二进制串的算法。它是一种重要的存储方式&#xff0c;也是一种常见的检索方…

Unity——拖尾特效

拖尾是一种很酷的特效。拖尾的原理来自人类的视觉残留&#xff1a;观察快速移动的明亮物体&#xff0c;会看到物体移动的轨迹。摄像机通过调整快门时间&#xff0c;也可以拍出具有拖尾效果的照片&#xff0c;如在城市的夜景中&#xff0c;汽车的尾灯拖曳出红色的线条。 在较老…

平安健康,找到了医疗服务的价值密码

健康是人类的永恒需求&#xff0c;围绕医疗和健康服务衍生的产业&#xff0c;却苦于无法和用户建立足够紧密、长期的联系。由此&#xff0c;也不得不面临价值从何而来的问题。 作为医疗服务领域的代表性企业&#xff0c;平安健康医疗科技有限公司&#xff08;股票简称“平安好…

Oracle解锁表、包、用户、杀会话、停job

Oracle解锁表、包、用户、杀会话、停job 一、创建包tzq_server_pkg二、授权给需要使用的用户log三、解锁表&#xff1a;执行存过unlock_table(schema_name, table_name)四、解锁包&#xff1a;执行存过unlock_package(schema_name, pkg_name)五、解锁用户&#xff1a;执行存过u…

14-数据结构-二叉树的创建以及前中后遍历,以及结点和叶子节点的计算(C语言)

概述&#xff1a; 二叉树&#xff0c;这里采用孩子链表存储法&#xff0c;即一个数据域和两个左右孩子指针域。随后递归进行遍历即可。在创建二叉树的时候&#xff0c;先创建各个二叉树结点&#xff08;这里的结点采用动态分配&#xff0c;因此结点为指针变量&#xff09;&…

c语言中编译过程与预处理

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、c语言的编译与链接1、编译与链接概述2、编译与链接详解 二、c语言预处理1.c语言中内置的预定义符号2、#define定义标识符3、#define定义宏4、#define 替换规…

项目进度管理软件:选择最适合您的工具

项目进度管理作为项目管理的重要组成部分&#xff0c;可以帮助项目团队更好地控制项目进度&#xff0c;确保项目按时完成并达到预期目标。本文将介绍项目进度管理软件的功能以及市场上常见的几种项目进度管理软件。 “项目进度管理软件有哪些?功能出色的有Zoho Projects、Trel…

ResNet 模型原理

ResNet与Vgg的主要区别&#xff1a; 1.ResNet相较于Vgg具有更加深的网络结构 2.ResNet相较于Vgg引入了残差连接的结构 3.ResNet引入了BatchNorm层&#xff0c;使得ResNet能够训练更加深的网络结构 4.ResNet使用stride2的卷积层代替了Vgg中池化层进行下采样 5.ResNet相较于…

【在Windows下搭建Tomcat HTTP服务】

文章目录 前言1.本地Tomcat网页搭建1.1 Tomcat安装1.2 配置环境变量1.3 环境配置1.4 Tomcat运行测试1.5 Cpolar安装和注册 2.本地网页发布2.1.Cpolar云端设置2.2 Cpolar本地设置 3.公网访问测试4.结语 前言 Tomcat作为一个轻量级的服务器&#xff0c;不仅名字很有趣&#xff0…