【Java并发】创建和使用线程常用方法总结

news/2024/12/22 14:18:41/

目录

什么是线程

线程和进程、协程(虚拟线程)的区别

线程和进程的区别

线程和协程的区别

线程的六种状态

1.NEW

2.RUNNABLE

3.BLOCKED

4.WAITING

5.TIME_WAITING

6.TERMINATED

创建线程

方法一:通过继承Thread类

方法二:通过实现Runnable接口

两种方法的区别

线程常用方法 

start 与 run 线程执行

sleep 线程休眠

wait 线程等待 

join 线程合并

interrupt 线程中断

yield 线程让步 

setPriority 线程优先级

setDaemon 设置守护线程


什么是线程

线程是操作系统中的基本执行单元(能够直接执行的最小代码块),它是进程中的⼀个实体,是CPU调度和分派的基本单位。

线程和进程、协程(虚拟线程)的区别

线程和进程的区别

  • 在操作系统中,进程是基本的资源分配单位,操作系统通过进程来管理计算机的资源,如CPU、内存、磁盘等。⼀个进程可以包含多个线程,每个线程都可以独立执行不同的任务,但它们共享进程的资源。

线程和协程的区别

  • 调度方式上,线程由操作系统调度,切换线程时会涉及上下文切换和内核态的开销;协程由程序调度,在用户态切换,没有上下文切换的开销,性能更高。

  • 阻塞与非阻塞方面,线程通常采用阻塞模型;协程是非阻塞的。

  • 资源占用方面,线程每个线程需要分配栈空间,且栈大小固定,导致线程资源消耗较大;协程的栈空间可以动态增长,内存开销远小于线程。

总结一下:先有进程,然后进程可以创建线程,线程是依附在进程里面的, 线程里面可以包含多个协程;进程之间不共享全局变量,线程之间共享全局变量,但是要注意资源竞争的问题。

线程的六种状态

1.NEW

初始状态,线程被构建,但是还没有调用start()方法。

2.RUNNABLE

运行状态,Java线程将操作系统中的就绪和运行两种状态统称为"运行中"。

3.BLOCKED

阻塞状态,表示线程阻塞于锁。

4.WAITING

等待状态,表示线程进入等待状态,进入该状态表示当前线程需要其他线程通知(notify或者notifyAll)。

5.TIME_WAITING

超时等待状态,可以指定等待时间自己返回。

6.TERMINATED

终止状态,表示当前线程已经执行完毕。

创建线程

方法一:通过继承Thread类

  • 创建一个类继承Thread类。

  • 重写该类的run()方法,将线程要执行的代码放在run()方法中。

  • 创建该类的实例,并调用其start()方法来启动线程。

java">class MyThread extends Thread {@Overridepublic void run() {System.out.println("线程开始运行");}
}public class Main {public static void main(String[] args) {MyThread myThread = new MyThread();myThread.start(); }
}

方法二:通过实现Runnable接口

  • 创建一个类实现Runnable接口。

  • 实现Runnable接口的run()方法,将线程要执行的代码放在run()方法中。

  • 创建该类的实例,并将其作为参数传递给Thread类的构造函数,创建线程对象。

  • 调用线程对象的start()方法来启动线程。

java">class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("线程开始运行");}
}public class Main {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread = new Thread(myRunnable);thread.start(); }
}

或者更简单的写为:

java">    public static void main(String args[]){Thread thread = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("线程开始运行");}});thread.start();}

两种方法的区别

  • 继承Thread类:简单直接,但Java不支持多继承,如果类已经继承了其他类,则无法再继承Thread类。

  • 实现Runnable接口:更灵活,推荐使用。可以避免Java单继承的限制,适合多个线程共享同一个资源的情况。

线程常用方法 

start 与 run 线程执行

start()run()方法都与线程的执行有关,但它们之间有着本质的区别:

  • 执行run方法它不会产生新线程,而执行start方法会产生新线程。

  • run方法可以被执行无数次,而star方法只能被执行⼀次,原因就在于线程不能被重复启动。

 总结一下:run方法的作用是存放任务代码,而start的方法呢是启动线程。

sleep 线程休眠

sleep()方法是Thread类的一个静态方法,用于让当前正在执行的线程暂停执行指定的时间,以便其他线程可以运行。

休眠线程例如:

java">public class SleepExample {public static void main(String[] args) {Thread thread = new Thread(() -> {try {System.out.println("线程开始执行");Thread.sleep(2000); // 睡2秒System.out.println("线程执行完毕");} catch (InterruptedException e) {System.out.println("线程被中断");}});thread.start();}
}
sleep 特点

不释放锁、对中断敏感、释放 CPU。

wait 线程等待 

wait()方法是Object类的一个方法,用于让当前线程等待,直到其他线程调用同一对象的notify()notifyAll()方法。

wait()方法通常用于实现线程间的协调和同步,常用于生产者-消费者模型,例如:

java">public class WaitNotifyExample {private static final Object lock = new Object();public static void main(String[] args) {Thread producer = new Thread(() -> {synchronized (lock) {System.out.println("生产者开始生产");// 生产产品...System.out.println("生产者生产完毕");lock.notify(); // 通知消费者}});Thread consumer = new Thread(() -> {synchronized (lock) {try {System.out.println("消费者开始等待");lock.wait(); // 等待生产者生产完毕System.out.println("消费者开始消费");// 消费产品...} catch (InterruptedException e) {System.out.println("消费者被中断");}}});consumer.start();producer.start();}
}
wait 特点 

释放锁、对中断敏感、释放 CPU。

注意:wait()方法必须在同步块(synchronized block)或同步方法(synchronized method)中调用,否则会抛出IllegalMonitorStateException异常。因为wait()方法需要释放对象的锁,而只有在同步块中才能获取对象的锁。

join 线程合并

join()方法是Thread类的一个方法,用于等待一个线程终止。当你调用一个线程对象的join()方法时,当前线程会暂停执行,直到被调用join()方法的线程执行完毕。

常用于确保执行顺序例如:

java">public class JoinExample {public static void main(String[] args) {Thread thread1 = new Thread(() -> {System.out.println("线程1开始执行");// 模拟耗时操作try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程1执行完毕");});Thread thread2 = new Thread(() -> {System.out.println("线程2开始执行");try {thread1.join(); // 等待线程1执行完毕} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程2执行完毕");});thread1.start();thread2.start();}
}

interrupt 线程中断

interrupt()方法是Thread类的一个方法,用于中断一个线程。中断是一种协作机制,它不会立即停止线程,而是设置线程的中断状态,线程需要检查这个状态并作出相应的响应。

举个简单的例子:

java">public class InterruptExample {public static void main(String[] args) {Thread thread = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {// 执行任务...System.out.println("线程正在运行");try {Thread.sleep(1000); // 模拟耗时操作} catch (InterruptedException e) {Thread.currentThread().interrupt(); // 重新设置中断状态break; // 退出循环}}System.out.println("线程被中断,退出执行");});thread.start();try {Thread.sleep(3000); // 让主线程等待一段时间} catch (InterruptedException e) {e.printStackTrace();}thread.interrupt(); // 中断线程}
}

注意:线程通过方法isInterrupted()来进行判断是否被中断,也可以调用静态方法Thread.interrupted()对当前线程的中断标识位进行复位。

yield 线程让步 

yield()方法是Thread类的一个静态方法,用于提示线程调度器当前线程愿意让出CPU的执行权,以便其他线程可以执行。

举个简单的例子:

java">public class YieldExample {public static void main(String[] args) {Thread thread1 = new Thread(() -> {for (int i = 0; i < 5; i++) {System.out.println("线程1执行:" + i);if (i == 2) {Thread.yield(); // 在i等于2时让出CPU执行权}}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 5; i++) {System.out.println("线程2执行:" + i);}});thread1.start();thread2.start();}
}

注意:yield()方法并不能保证线程⼀定会让出CPU资源,它只是⼀个提示,告诉调度器当前线程愿意让出CPU资源。具体是否让出CPU资源,还是由调度器决定。 

setPriority 线程优先级

在Java线程中,通过一个整型成员变量priority来控制优先级,优先级的范围从1~10,在线程构建的时候可以通过setPriority(int)方法来修改优先级,默认优先级是5,优先级高的线程分配CPU时间片的数量要多于优先级低的线程。

修改线程优先级例如:

java">Thread thread = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("线程正在运行");}
});thread.setPriority(Thread.MAX_PRIORITY); // 设置线程优先级为最高
thread.start();

注意:优先级高的线程分配CPU时间片的数量要多于优先级低的线程,并不是优先级高的线程先比优先级低的执行。

setDaemon 设置守护线程

setDaemon方法是Thread类的一个方法,用于将线程设置为守护线程(daemon thread)。

什么是守护线程

守护线程是一种特殊的线程,它不用于程序的正常运行。当程序中所有的非守护线程(用户线程)都结束时,程序会自动退出,即使还有守护线程在运行。守护线程通常用于为用户线程提供服务或者执行后台任务,如垃圾回收器线程。


http://www.ppmy.cn/news/1557208.html

相关文章

sql server msdb数据库备份恢复

备份 BACKUP DATABASE [msdb] TO DISK ND:\liyuanshuai\test\sqlserver_bakfile\msdb20241219.bak WITH NOFORMAT, NOINIT, NAME Nlys-完整 数据库 备份, SKIP, NOREWIND, NOUNLOAD, COMPRESSION, STATS 10 GO然后删除2个测试的job&#xff0c;停止 SQL Server 代理…

第19天:信息收集-Web应用源码获取闭源备份开发泄漏WebPack打包资源搜索ICO定位

#知识点 1、信息收集-Web应用-源码获取-已知指纹&未知指纹 2、信息收集-Web应用-源码获取-泄漏问题&发现指纹 一、参考文章&#xff1a; https://www.secpulse.com/archives/124398.html https://mp.weixin.qq.com/s/QgLDdaefXlZtvlSiFQShZw 二、源码泄漏原因&#xff…

第十四届蓝桥杯Scratch国赛真题—转动的车轮

转动的车轮 编程实现&#xff1a; 转动的车轮&#xff08;车轮使用画笔绘制&#xff0c;画面中不能出现其他角色&#xff0c;否则0分&#xff09;。 注&#xff1a;角色、背景非源素材。 具体要求&#xff1a; 1). 点击绿旗&#xff0c;背景如图所示&#xff1b; 2). 等待1…

练习题 最小栈

最小栈 最小栈 class MinStack {private Stack<Integer> stack;private Stack<Integer> minstack;public MinStack() {stacknew Stack<>();minstacknew Stack<>();}public void push(int val) {stack.push(val);if(minstack.empty()){minstack.push(…

【C++11】可变模板参数

目录 可变模板的定义方式 参数包的展开方式 递归的方式展开参数包 STL中的emplace相关接口函数 STL容器中emplace相关插入接口函数 ​编辑 模拟实现&#xff1a;emplace接口 C11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板&#xff0c;相比 C9…

Odoo:免费开源ERP的AI技术赋能出海企业电子商务应用介绍

概述 伴随电子商务的持续演进&#xff0c;客户对于便利性、速度以及个性化服务的期许急剧攀升。企业务必要探寻创新之途径&#xff0c;以强化自身运营&#xff0c;并优化购物体验。达成此目标的最为行之有效的方式之一&#xff0c;便是将 AI 呼叫助手融入您的电子商务平台。我们…

Debian环境安装Docker Engine

Debian环境安装Docker Engine 卸载旧版本使用APT工具安装Docker设置存储库安装Docker设置权限 docker compose命令卸载Docker 卸载旧版本 要卸载的非官方软件包是&#xff1a; docker.iodocker-composedocker-docpodman-docker 此外&#xff0c;Docker Engine 依赖 containe…

.Net Core框架创建一个Windows服务类型的应用程序

在NuGet中的包管理中添加两个包 System.ServiceProcess.ServiceController Microsoft.Extensions.Hosting.WindowsServices 在Program.cs中添加.UseWindowsService()&#xff0c;另外还需要设置管理员身份运行 Program.cs代码如下 public class Program {public static void…