线程(线程基本概念、java实现多线程、使用多线程、线程的生命周期、线程同步、线程死锁)

(一)线程基本概念


一、 程序, 进程, 线程的概念


程序: 使用某种语言编写一组指令(代码)的集合,静态的

进程: 运行的程序,表示程序一次完整的执行, 当程序运行完成, 进程也就结束了

个人电脑: CPU 单个, 双核, CPU的时间分片, 抢占式

每个独立执行的程序称为进程

每个进程都有自己独立的内存空间, 进制之间的通信很困难

在操作系统中进程是进行系统资源分配、调度和管理的最小单位,进程在执行过程中拥有独立的内存单元。比如:Windows采用进程作为最小隔离单位,每个进程都有自己的数据段、代码段,并且与别的进程没有任何关系。因此进程间进行信息交互比较麻烦

线程: 一个进程中,可以同时有多条执行链路, 这些执行链路称为线程, 线程是CPU的调度与分配最小单位, 同一个进程多个线程共享这个进程的内存资源: JVM内存模型

二、进程与线程区别:

进程包含线程, 一个进程包含多个线程, 一个进程最小必须包含一个线程(主线程,main线程), 运行main()方法的时候, 创建了一个main线程

一个进程死亡了, 这个进程中所有的线程死亡

线程销毁,进程未必会关闭

并行: 多CPU执行各种不同任务,

并发: 一个CPU执行不同的任务(多个线程)

多线程速度快吗? 看CPU的核数,个数

三、多线程

多线程是指一个进程在执行过程中可以产生多个线程,这些线程可以同时存在、同时运行,形成多条执行线

(二)java实现多线程


在jdk.1.5之前: 创建线程的方式: 两种:

  • 继承Thread类
  • 实现Runnable接口

在JDK1.5之后: 多加了两种:

  • 实现 Callable接口
  • 线程池

第一种方式: 继承Thread

  1. 编写一个类继承Thread,该类是线程类

  2. 重写run(), 编写该线程需要完成的任务

  3. 创建线程类对象

  4. 调用start()方法,启动线程

/*** 实现线程的第一种方式:* 继承Thread*/
public class MyThread1 extends Thread{
​//private static Object o = new Object();public MyThread1(String name) {super(name);}
​//重写run()方法@Overridepublic void run() {//线程完成的功能for (int i = 1; i <=10 ; i++) {//得到当前正在运行的线程//Thread.currentThread()//getName() 得到线程的名字System.out.println(Thread.currentThread().getName()+":输出"+i);}}
}

 注意事项:

  1. 线程启动,一定是调用start() , 不是调用run(), 如果直接调用run() ,只是方法的调用,没有创建线程
  2. 一个线程一旦启动,就不能重复启动

第二种方式: 实现Runnable接口

启动线程: 都必须借助Thread的start()

Runnable实现类: 就是一个线程的任务类

继承Thread与Runnable接口的区别:

  1. 继承Thread类, 这个线程类就不能再继承其他类, 实现Runnable接口, 可以再继承其他类
  2. 实现Runnable接口, 可以让多个线程共享这个Runnable的实现类对象
  3. 继承Thread类,启动简单, 实现Runnable接口, 必须依赖Thread类的start()方法

推荐 实现Runnable接口

/*** 线程的第二种实现方式: 实现Runnable接口*/
public class MyRunnable implements  Runnable {@Overridepublic void run(){//线程需要完成的任务for (int i = 1; i <=10 ; i++) {System.out.println(Thread.currentThread().getName()+":输出"+i);}}
}

实际开发中使用 

new Thread(new Runnable() {@Overridepublic void run() { }}).start();

 第三种方式: 实现Callable接口

  1. 第一步编写一个类实现Callable接口,重写call()方法
  2. 启动线程
  3. 创建Callable接口实现类对象
  4. 创建一个FutureTask对象, 传递Callable接口实现类对象, FutureTask异步得到Callable执行结果, 提供get() FutureTask 实现Future接口( get()) 实现Runnable接口
  5. 创建一个Thread对象, 把FutureTask对象传递给Thread, 调用start()启动线程
/*** 线程的第三种实现方式:*   实现Callable接口*/
public class MyCallable implements Callable<String> {@Overridepublic String call() throws Exception {//线程睡眠 单位: 毫秒Thread.sleep(10000);                   System.out.println(Thread.currentThread().getName()+"执行完成");return "callable";}
}

 第四种方式: 使用线程池创建

1.FixedThreadPool固定大小的线程池
2.CachedThreadPool缓存线程池。该线程池创建的线程数量不固定,当有新任务需要执行时,会创建新的线程来执行任务,如果有线程处于空闲状态,会优先使用空闲线程。适用于执行时间短的任务,如处理HTTP请求等。
3.SingleThreadExecutor单线程线程池。该线程池只创建一个线程来执行任务
4.ScheduledThreadPool定时任务线程池。该线程池可以定时执行任务,可以设置任务执行的时间、执行周期等。适用于需要定时执行任务的场景,如定时备份数据等。

1、使用newCachedThreadPool

newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

这种类型的线程池特点是:

  • 工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。

  • 如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认为1分钟),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。

  • 在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪。

示例代码如下:

package test;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class ThreadPoolExecutorTest {public static void main(String[] args) {ExecutorService cachedThreadPool = Executors.newCachedThreadPool();for (int i = 0; i < 10; i++) {final int index = i;try {Thread.sleep(index * 1000);} catch (InterruptedException e) {e.printStackTrace();}cachedThreadPool.execute(new Runnable() {public void run() {System.out.println(index);}});}}}

 2、使用newFixedThreadPool

创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。

FixedThreadPool是一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是,在线程池空闲时,即线程池中没有可运行任务时,它不会释放工作线程,还会占用一定的系统资源。

示例代码如下:

package test;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class ThreadPoolExecutorTest {public static void main(String[] args) {ExecutorService cachedThreadPool = Executors.newCachedThreadPool();for (int i = 0; i < 10; i++) {final int index = i;try {Thread.sleep(index * 1000);} catch (InterruptedException e) {e.printStackTrace();}cachedThreadPool.execute(new Runnable() {public void run() {System.out.println(index);}});}}}

3、使用newSingleThreadExecutor

创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO,优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

示例代码如下:

package test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExecutorTest {public static void main(String[] args) {ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();for (int i = 0; i < 10; i++) {final int index = i;singleThreadExecutor.execute(new Runnable() {public void run() {try {System.out.println(index);Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}});}}
}

4、使用newScheduledThreadPool

创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行。

延迟3秒执行,延迟执行示例代码如下:

package test;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorTest {public static void main(String[] args) {ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);scheduledThreadPool.schedule(new Runnable() {public void run() {System.out.println("delay 3 seconds");}}, 3, TimeUnit.SECONDS);}
}

表示延迟1秒后每3秒执行一次,定期执行示例代码如下:

package test;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorTest {public static void main(String[] args) {ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);scheduledThreadPool.scheduleAtFixedRate(new Runnable() {public void run() {System.out.println("delay 1 seconds, and excute every 3 seconds");}}, 1, 3, TimeUnit.SECONDS);}
}

(三)使用多线程

一、启动线程

1.实现线程的第一种方式:*继承Thread

抢占式执行,谁抢到就是谁的,main函数最先执行(多数情况下)

//启动线程//创建线程对象MyThread1 t1 =new MyThread1("t1");MyThread1 t2 =new MyThread1("t2");MyThread1 t3 =new MyThread1("t3");//调用start()启动线程, 线程与其他线程抢占cpu资源, 谁抢到,执行谁的run()中的代码t1.start();t1.start();t2.start();t3.start();
​System.out.println("main线程执行完成...");

2.线程的第二种实现方式: 实现Runnable接口

public static void main(String[] args) {        
//创建任务对象        
MyRunnable task = new MyRunnable();        
//创建线程: Thread        
Thread t1 = new Thread(task, "t1");       
Thread t2 = new Thread(task, "t2");        
//启动线程        
t1.start();        
t2.start();    
}

3.线程的第三种实现方式:实现Callable接口

get(); //阻塞的 main最后执行

MyCallable callable = new MyCallable();//再次封装 FutureTaskFutureTask<String> task = new FutureTask<>(callable);Thread t1 = new Thread(task, "t1");//启动线程t1.start();//获取结果// String rs = task.get(); //阻塞的,  是main线程在执行//设置一个超时时间, 一旦到达超时时间, 停止执行,抛一个异常//String rs = task.get(1, TimeUnit.SECONDS);//取消执行task.cancel(true);//System.out.println(rs);System.out.println("main线程执行完成!!");

二,线程的生命周期

新生状态:调用start()方法之前都处于出生状态

就绪状态:调用start()方法后处于就绪状态(又称为可执行状态)

运行状态:得到系统资源后处于运行状态

阻塞状态:如果一个线程在运行状态下发出输入/输出请求,该线程将进入阻塞状态,在其等待输入输出结束时线程进入了就绪状态。

线程阻塞:

死亡状态:当线程run()方法执行完毕时线程进入死亡状态。

  1. 同步阻塞
  2. sleep() Thread的方法 时间到自动醒,释放cpu资源,不释放锁, join() 阻塞
  3. yield() 礼让, 释放cpu资源, 与其他线程抢CPU资源
  4. wait() 等待: Object类中的方法, 一定等待唤醒(notify() notifyAll()), 释放cpu资源,释放锁资源, 线程通信
  5. suspend()和resume(),由jdk1.0提供,jdk1.2之后就被放弃了,它也是让线程暂停,但是它不释放资源,导致死锁出现

t1.yield(); //礼让
Thread.sleep(1000);
thread4.join();
//等方法会让线程进入阻塞状态 

三,线程同步

同步方法

在方法上添加一个synchronized修饰符, 往对象上加锁

非静态同步方法:锁加在 this(对象)

静态同步方法:锁加在类.class(对象)

锁的释放: 当把同步方法执行完之后,马上释放锁

同步方法: 锁住的代码范围整个方法, 锁的控制粒度太宽

public 返回值类型 方法名(){//...synchronized(锁对象){//锁住的代码}//...
}

四,线程死锁

多个线程,相互之间需要对方的锁, 但是又不释放自己的锁,造成程序卡住, 这些线程都在等待,等待对方的锁,

死锁形成的原因: 互斥锁,排他锁

1> 互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用

2> 不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。

3> 请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占用。

4> 循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路

形成死锁的这4个条件缺一不可, 避免死锁: 打破4个条件 的一个就可以

项目一定避免出现死锁:

解决死锁:

使用完某个锁,马上释放

多个线程获取锁的顺序是一致, A线程获取锁: a-->b-->c

B线获取锁: a-->b-->v

模拟死锁:

A线程 先获取objA锁, 再获取objB锁, 在获取objB锁时,不释放objA锁

B线程 先获取objB锁, 再获取objA锁, 在获取objA锁时,不释放objB锁

(四)统计线程运行时间

1,java 统计线程运行时间

public class MyThread extends Thread {public void run() {// 线程相关的任务代码for (int i = 0; i < 1000000; i++) {// 模拟线程运行的任务}}public static void main(String[] args) {// 创建线程MyThread thread = new MyThread();// 记录开始时间long startTime = System.currentTimeMillis();// 执行线程任务thread.start();// 等待线程执行完成thread.join();// 记录结束时间long endTime = System.currentTimeMillis();// 输出线程运行时间System.out.println("总耗时::"+(endTime-startTime)/1000+"秒");}
}

2,例如:一个任务需要2s执行,现在有十个任务,共需要多久时间?

 

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

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

相关文章

OpenCV快速入门:目标检测——轮廓检测、轮廓的距、点集拟合和二维码检测

文章目录 前言一、轮廓检测1.1 图像轮廓的概念1.2 轮廓检测算法简介1.3 轮廓检测基本步骤1.4 轮廓检测函数说明1.4.1 轮廓发现1.4.2 轮廓面积1.4.3 轮廓周长1.4.4 轮廓外接多边形1.4.5 点到轮廓距离1.4.6 凸包检测 1.5 轮廓检测代码实现 二、轮廓的距2.1 几何距2.2 中心距2.3 H…

ssh远程连接不了虚拟机ubuntu

直奔主题 1. 确保linux安装了ssh2.查看网络适配器是否启用3.连接成功 1. 确保linux安装了ssh sudo apt-get install openssh-server2.查看网络适配器是否启用 3.连接成功

VBA技术资料MF85:将工作簿批量另存为PDF文件

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…

如何实现车机体验”遥遥领先”?头部玩家已经给出答案

车机与手机的深度融合&#xff0c;通过跨终端互联互通实现全场景、沉浸式的用户体验&#xff0c;正在成为各大高端智能汽车品牌的新战场。 此前&#xff0c;已经有华为、苹果几大手机巨头已经纷纷开启“造车”业务&#xff0c;同时吉利等车企也反向进入手机领域&#xff0c;各…

TransmittableThreadLocal - 线程池中也可以传递参数了

一、InheritableThreadLocal的不足 InheritableThreadLocal可以用于主子线程之间传递参数&#xff0c;但是它必须要求在主线程中手动创建的子线程才可以获取到主线程设置的参数&#xff0c;不能够通过线程池的方式调用。 但是现在我们实际的项目开发中&#xff0c;一般都是采…

快来看看你的手表是否有忘记关闭的应用在后台偷偷耗电?

在这个智能化的时代&#xff0c;手表已经不仅仅是用来看时间的工具&#xff0c;它更是我们生活的助手&#xff0c;从消息提醒到健康监测&#xff0c;它似乎无所不能。但与此同时&#xff0c;你是否注意到手表电量的续航有时长&#xff0c;有时慢&#xff1f;有可能&#xff0c;…

C语言每日一题(33)随机链表的复制

力扣138 随机链表的复制 题目描述 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xff0c;其中每个新节点的值都…

解锁OpenAI潜力:OpenAI 全面解析与最佳实践

该项目是由OpenAI公司提供的一个大型代码库&#xff0c;其中包含了各类与OpenAI API相关的代码示例和最佳实践。 此项目名为OpenAI Cookbook&#xff0c;目的是为了帮助使用者更有效地利用OpenAI API&#xff0c;将其应用于自己的工作和生活中。具体来说&#xff0c;可以解决一…

打码平台之图鉴的使用步骤

打码平台之图鉴 背景&#xff1a; ​ 今天给大家推荐一个我一直使用的验证码识别平台&#xff0c;图鉴&#xff0c;我没有收费&#xff0c;我只是觉得这个网站使用方便&#xff0c;支持验证码种类多&#xff0c;好了&#xff0c;话不多说&#xff0c;上教程&#xff01; 注册…

SmartX 超融合 5.1 版本有哪些新特性和技术提升?

近日&#xff0c;SmartX 正式发布了超融合产品组合 SmartX HCI 5.1 版本&#xff0c;以全面升级的超融合软件、分布式块存储、容器管理与服务、软件定义的网络与安全等组件&#xff0c;为虚拟化和容器负载在计算、存储、网络和管理层面提供统一的架构和生产级别的能力支持。本期…

2024年测试工程师必看文章系列之python+pytest接口自动化(1)-接口测试

一般我们所说的接口即API&#xff0c;那什么又是API呢&#xff0c;百度给的定义如下&#xff1a; API&#xff08;Application Programming Interface&#xff0c;应用程序接口&#xff09;是一些预先定义的接口&#xff08;如函数、HTTP接口&#xff09;&#xff0c;或指软件系…

如何在AIX操作系统上修改Java环境变量

AIX操作系统是IBM的Unix操作系统&#xff0c;通常用于企业级应用和服务器环境。在AIX上配置Java环境变量是执行Java应用程序和开发Java代码的重要步骤。本文将详细介绍如何在AIX上修改Java环境变量&#xff0c;并提供具体示例来帮助你完成这个任务。 步骤1&#xff1a;确定Java…

matlab如何实现任意长序列所有排列方式

最近被问到一个问题&#xff0c;如何计算一个由3个0和3个1组成的序列的所有组合情况&#xff0c;处理这个问题我没有找到特别恰当的函数&#xff08;如果有能直接做的函数欢迎评论告知&#xff09;&#xff0c;所以采用比较接近需求的perms函数来解决这个问题 首先看perms函数…

许战海战略文库|三步成就技术品牌:奥迪如何打造Quattro技术品牌?

引言&#xff1a;在当前全球化和信息化快速发展的背景下,技术品牌的打造不仅是企业竞争力提升的重要途径,也是企业实现长远发展的基石。技术品牌的建设并非一蹴而就的过程,而是需要企业准确把握市场趋势发掘自身核心竞争力,并通过长期的积累和推广逐渐在市场中树立起良好的技术…

视频监控管理平台EasyCVR告警查询拖动条无法显示,该如何解决?

视频汇聚/视频云存储/集中存储/视频监控管理平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;实现视频资源的鉴权管理、按需调阅、全网分发、云存储、智能分析等&#xff0c;视频智能分析平台EasyCVR融合性强、开放度…

JSON.toJSONString/JSONObject.toJSONString将实体类对象转换成JSON字符串时,多出了params字符串[记录贴]

我这里是给与了实体类一些固定的默认值&#xff0c;转换莫名其妙多出了params参数&#xff0c;回头深挖一下 **光看代码了 被偷了后方&#xff0c;忘记继承了还 ** 将实体类转换成JSON格式&#xff0c;三种写法都是一样的&#xff0c;内核都是阿里巴巴的 System.out.println(…

[Docker]七.配置 Docker 网络

一.Docker0 网络 1.多个容器之间如何通信,是否可以直接连接 默认启动的所有容器都会加入到docker0这个网络中,所有各个容器件是可以直接通信的 先看看网卡信息: 启动几个容器来演示一下: #启动mycentos这个容器 [rootlocalhost zph]# docker run -it -d --name mycentos d757…

OSG文字-各种文字效果(边框、阴影及颜色倾斜)示例(2)

各种文字效果(边框、阴影及颜色倾斜)示例 各种文字效果(边框、阴影及颜色倾斜)示例的代码如程序清单9-2所示&#xff1a; 1. /* 各种文字效果(边框、阴影及颜色倾斜)示例 */ 2. osg::ref_ptr<osg::Camera> createAllKindText(const string &strDataFolder) 3. {…

家政保洁预约小程序app开发特点有哪些?

家政预约服务小程序APP开发的特点介绍&#xff1b; 1. 低成本&#xff1a;用户通过手机APP下单&#xff0c;省去了中介费用&#xff0c;降低了雇主的雇佣成本。 2. 高收入&#xff1a;家政服务人员通过手机APP接单&#xff0c;省去了中介费用&#xff0c;从而提高了服务人员的…

京东优惠券查询API接口接入方案,item_search_coupon - 京东优惠券查询接口演示

要接入京东优惠券查询API接口&#xff08;item_search_coupon&#xff09;&#xff0c;您可以按照以下步骤进行操作&#xff1a; 注册并获取API密钥&#xff1a;首先&#xff0c;您需要在京东开放平台上注册并获取API密钥。这将为您提供唯一的标识符和密钥&#xff0c;用于访问…