当前位置: 首页 > news >正文

Java多线程详解(1)

一、认识线程

1.1、什么是进程

进程是操作系统对一个正在运行的程序的一种抽象,换言之,可以把进程看做程序的一次运行过程;同时,在操作系统内部,进程又是操作系统进行资源分配的基本单位

1.2、什么是线程

一个线程就是一个“执行流”,每个线程之间都可以按照顺序执行自己的代码,多个线程之间“同时”执行着多份代码。

1.3、为什么要有线程

(1)“并发编程”成为“刚需”
1、要想提升算力,就需要多核CPU,而并发编程更能充分利用多核CPU资源。
2、为了让等待IO的时间能去做其他事情,也需要并发编程
(2)线程比进程更轻量
1、创建线程比创建进程更快
2、销毁线程比销毁进程更快
3、调度线程比调度进程更快

1.4、进程和线程的区别

1、进程中包含线程,每一个进程都至少有一个线程
2、进程是申请系统资源的最小单位
3、线程是系统调度的最小单位
4、线程之间共享进程申请的系统资源
5、一个线程如果崩溃,就会影响整个进程

二、线程的创建方式

(1)继承Thread类

继承Thread来创建一个线程类

class MyThread extends Thread{@Overridepublic void run() {System.out.println("创建线程方法1...");}
}

创建MyThread类的实例

MyThread t1=new MyThread();

调用start()方法启动线程

t1.start();

(2)实现Runnable接口

实现Runnable接口

class MyRunnable implements Runnable{@Overridepublic void run() {System.out.println("创建线程方法2...");}
}

创建Thread类实例,调用Thread的构造方法时将Runnable对象作为参数

Thread t2=new Thread(new MyRunnable());

调用start()方法

t2.start();

(3)使用匿名内部类创建Thread子类对象

Thread t3=new Thread(){@Overridepublic void run() {System.out.println("创建线程方法3...");}
};

(4)匿名内部类创建Runnable子类对象

Thread t4=new Thread(new Runnable() {@Overridepublic void run() {System.out.println("创建线程方法4...");}
});

(5)lambda表达式

Thread t5 = new Thread(()->{System.out.println("创建线程方法5...");
});

三、多线程的优势

多线程的优势:增加运行速度
示例:分别使用func1()和func2()计算对a和b累加和累减1000000000次的时间,其中func1()串行累加,func2()使用线程并行累减,比较串行和并行的时间

public class Test01 {private static long count=1000000000;private static long a=0;private static long b=0;public static void main(String[] args) throws InterruptedException {func1();func2();}//串行private static void func1(){//记录func1()开始运行的时间long begin=System.currentTimeMillis();//对a累加for(long i=0;i<count;i++){a++;}//对b累加for(long i=0;i<count;i++){b++;}//记录func1()完成累加后的时间long end=System.currentTimeMillis();System.out.println("串行运行时间:"+(end-begin));}//并行private static void func2() throws InterruptedException {//记录func2()开始运行的时间long begin=System.currentTimeMillis();//用线程对a累减Thread t1 = new Thread(()->{for(long i=0;i<count;i++){a--;}});//用线程对b累减Thread t2 = new Thread(()->{for(long i=0;i<count;i++){b--;}});//启动线程t1和t2t1.start();t2.start();//等待线程t1和t2运行完成t1.join();t2.join();//记录func2()完成累减后的时间long end=System.currentTimeMillis();System.out.println("并行运行时间:"+(end-begin));}
}

其运行结果如下:
在这里插入图片描述
由此可见,当任务量偏大时,使用多线程确实效率更高

注意:(1)不是任何时候多线程的效率都比单线程高,当任务量很小时,单线程效率可能比多线程高 (2)创建线程本身也有一定的系统开销,这个开销没有创建进程的大 (3)线程在CPU上调度也需要时间

四、Thread类及常见方法

1、常见构造方法

(1)Thread()(说明:创建线程对象)
(2)Thread(Runnable target)(说明:使用Runnable对象创建线程对象)
(3)Thread(String name)(说明:创建线程对象,并命名)
(4)Thread(Runnable target,String name)(说明:使用Runnable对象创建线程对象,并命名)

2、几个常见属性

在这里插入图片描述
注意:关于后台进程,JVM会在一个进程的所有非后台线程结束后,才会结束运行

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

调用start()方法,才真的在操作系统的底层创建出一个线程

4、中断一个线程

方法一:自定义一个标志位,通过修改这个标志位,通知线程中断

public class Dome_Thread03 {//定义一个标志位static boolean isQuit=false;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{while(!isQuit){System.out.println("hello thread...");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("线程结束...");});//启动线程t1.start();//休眠5秒Thread.sleep(5000);//更改标志位isQuit=true;}
}

运行结果如下:
在这里插入图片描述

方法二:使用interrupt()方法来通知(注意:调用interrupt()方法时,如果线程在运行状态,直接中断线程,不会报异常,符合程序预期,如果线程在等待状态,就会报一个中断异常,要在异常处理代码块中进行中断逻辑实现)

public class Dome_Thread04 {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(()->{while(!Thread.currentThread().isInterrupted()){System.out.println("hello thread...");try {Thread.sleep(1000);} catch (InterruptedException e) {System.out.println("休眠被中断...");throw new RuntimeException(e);}}System.out.println("线程已退出...");});System.out.println("线程是否存活:"+t1.isAlive());//启动线程t1.start();Thread.sleep(1000);System.out.println("线程是否存活:"+t1.isAlive());//中断线程t1.interrupt();//等待线程销毁Thread.sleep(100);//查看线程是否存活System.out.println("线程是否存活:"+t1.isAlive());}
}

运行结果如下:
在这里插入图片描述

5、等待一个线程 - join()

有时我们需要等一个线程完成工作后才进行下一步工作,这时就需要用到join()
在这里插入图片描述

6、获取当前线程引用 -Thread.currentThread()

示例:

public class Test02 {public static void main(String[] args) {//获取当前线程Thread t=Thread.currentThread();//打印当前线程名字System.out.println(t.getName());}
}

运行结果如下:
在这里插入图片描述

7、休眠当前线程 - Thread.sleep()

注意:当sleep括号里面仅有一个参数时,时间单位为毫秒,有两个参数时,第一个参数为毫秒,第二个参数为纳秒

五、线程的状态

首先看一下线程的全部状态:

public class Dome_Thread06 {public static void main(String[] args) {for(Thread.State state:Thread.State.values()){System.out.println(state);}}
}

其运行结果如下:
在这里插入图片描述
其各自代表的含义如下:
(1)NEW:表示创建好了一个Java线程对象,安排好了任务,但还没有启动(没有调用start()方法之前是不会创建PCB的,和PCB没有任何关系)
(2)RUNNABLE:运行+就绪的状态,在执行任务时最常见的状态之一,在系统中有对应PCB
(3)BLOCKED:等待锁的状态,阻塞的一种
(4)WAITING:没有等待时间,一直死等,直到被唤醒
(5)TIMED_WAITING:指定了等待时间的阻塞状态,过时不候
(6)TERMINATED:结束,完成状态,PCB已经销毁,但是Java线程对象还在

未完待续

http://www.dtcms.com/a/308437.html

相关文章:

  • C#反射的概念与实战
  • [2025CVPR-小样本方向]ImagineFSL:基于VLM的少样本学习的想象基集上的自监督预训练很重要
  • 三方支付详解
  • SQL 中 WHERE 与 HAVING 的用法详解:分组聚合场景下的混用指南
  • 大数据平台数仓数湖hive之拉链表高效实现
  • 深度学习入门:用pytorch跑通GitHub的UNET-ZOO项目
  • 云服务器数据库
  • Camx-查看sensor mode 和效果参数
  • (LeetCode 每日一题) 2683. 相邻值的按位异或 (位运算)
  • 网络操作系统与应用服务器-1
  • SIwave 中 SIwizard 的 500 多个标准列表
  • 代码详细注释:演示多线程如何安全操作共享变量,使用互斥锁避免数据竞争。
  • Linux 文件系统基本管理
  • minidocx: 在C++11环境下运行的解决方案(二)
  • 网络攻击新态势企业级安全防御指南
  • Git分支管理:每个分支为什么这么命名?
  • Acrobat DC 应用安全配置:沙箱防护、数字签名
  • 了解微前端和SSO单点登录
  • Linux/Ubuntu 系统中打开火狐firefox、chromium浏览器失败
  • (三)从零搭建unity3d机器人仿真:使用WheelCollider实现turtlebot轮子差速运动
  • Linux系统编程-gcc(黑马笔记)
  • 译 | 用于具有外生特征的时间序列预测模型TimeXer
  • JavaScript 大数运算!
  • Abp+ShardingCore+EFCore.BulkExtensions使用案例
  • MCU中的DAC(数字模拟转换器)是什么?
  • 动态挑战-响应机制和密钥轮换
  • 算法练习:JZ32 从上往下打印二叉树
  • iOS高级开发工程师面试——其他
  • 磁盘坏道检测工具在美国服务器硬件维护中的使用规范
  • Linux 计划任务管理