线程(线程基本概念、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()方法
    @Override
    public 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 {
    @Override
    public void run(){
        //线程需要完成的任务
        for (int i = 1; i <=10 ; i++) {
     System.out.println(Thread.currentThread().getName()+":输出"+i);
        }
    }
}

实际开发中使用 

new Thread(new Runnable() {
        @Override
        public 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> {
    @Override
    public 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();
        //再次封装 FutureTask
        FutureTask<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执行,现在有十个任务,共需要多久时间?

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

整套数字化招采平台安全防御体系

招采平台作为数字化供应链的重要组成部分&#xff0c;需要确保招标采购过程的安全性,主体信息和交易数据信息尤为重要,通过必要的安全架构、技术和安全管理制度&#xff0c;做到事前防范、事中监管和事后审计的安全防御。 一、平台技术安全架构 1、先进的技术架构&#xff0c…

算法设计与分析复习--回溯(一)

文章目录 上一篇回溯法性质子集和问题装载问题下一篇 上一篇 算法设计与分析复习–贪心&#xff08;二&#xff09; 回溯法性质 类似穷举的搜索尝试过程&#xff0c;在搜索尝试过程中寻找问题的解&#xff0c;组织得井井有条&#xff08;避免遗漏&#xff09;&#xff0c; 高…

KNN(k近邻法)算法理论和实战

KNN概念 k近邻法&#xff08;k-nearest neighbor&#xff0c;k-NN&#xff09;是一种基本分类与回归方法。 k近邻法的输入为实例的特征向量对应于特征空间的点&#xff1b;输出为实例的类别&#xff0c;可以取多类。 k近邻法假设给定一个训练数据集&#xff0c;其中的实例类…

【机器学习】039_合理初始化

一、稳定训练 目标&#xff1a;使梯度值在更合理的范围内 常见方法如下&#xff1a; 将乘法变为加法 ResNet&#xff1a;当层数较多时&#xff0c;会加入一些加法进去 LSTM&#xff1a;如果时序序列较长时&#xff0c;把一些对时序的乘法做加法 归一化 梯度归一化&…

全链路压测的步骤及重要性

全链路压测是一种系统性的性能测试方法&#xff0c;旨在模拟真实用户场景下的完整操作流程&#xff0c;全面评估软件系统在不同压力下的性能表现。这种测试方法对于保证应用程序的高可用性、稳定性和可扩展性至关重要。 1. 全链路压测概述 全链路压测是在模拟实际用户使用场景的…

SMU可以供电的同时测量电流和电压

SMU可以供电的同时测量电流和电压 SMU本身能够提供电流或电压&#xff0c;同时测量负载或被测设备&#xff08;DUT&#xff1a;Device Under Test&#xff09;上的电流和电压。这是与传统电源相比使用SMU的优势之一。 SMU测量的电流和电压值将反映在NI-DCPower软面板中&#…

(swjtu西南交大)数据库实验(数据库需求分析):音乐软件数据管理系统

实验内容&#xff1a; 数据库需求分析&#xff1a;各用户组需求描述&#xff0c;绘出数据流图&#xff08;详细案例参见教材p333~p337&#xff0c;陶宏才&#xff0c;数据库原理及设计&#xff0c;第三版&#xff09;&#xff1b; 一、选题背景 近年来&#xff0c;“听歌”逐…

【docker】虚拟化和docker容器概念

基础了解 IAAS&#xff1a; 基础设施服务&#xff0c;&#xff08;只提供基础设施&#xff0c;没有系统&#xff09; **SAAS&#xff1a; ** 软件即服务&#xff0c;&#xff08;提供基础设施和系统&#xff09; PAAS&#xff1a; 平台即服务&#xff0c;&#xff08;提供基…

【Docker】从零开始:1.Docker概述

【Docker】从零开始&#xff1a;1.Docker概述 1.什么是Docker2.为什么要使用Docker3.传统虚拟机技术与Linux容器技术的区别(1).传统虚拟机技术(2).Linux容器 4.Docker的特点一次构建、随处运行a.更快速的应用交付和部署b.更便捷的升级和扩缩容&#xff1a;c.更简单的系统运维d.…

三字经||无聊数了下三字经的字数

三字经总字数去除标点后1416个 该文章无技术含量&#xff0c;仅三字经原文&#xff0c;学技术的同学可以止步了 三字经&#xff08;原文&#xff09; 【作者】王应麟 【朝代】南宋 人之初&#xff0c;性本善。性相近&#xff0c;习相远。 苟不教&#xff0c;性乃迁。教之道&a…

视频接入网关的用法

视频接入网关是一种多功能的视频网关设备&#xff0c;可以解决各种视频接入&#xff0c;视频输出&#xff0c;视频转码&#xff0c;视频融合的问题。可以应用在应急指挥&#xff0c;智慧融合等项目中&#xff0c;与各种系统进行对接&#xff0c;解决视频能力跨系统集成的难题。…

matlab-BP神经网络的训练参数大全

本文部分图文来自《老饼讲解-BP神经网络》bp.bbbdata.com 本文列兴趣MATLAB神经网络工具箱中&#xff0c;训练参数trainParam的各个参数与意义 以方便在使用matlab工具箱时&#xff0c;用于查阅 一、matlab神经网络工具箱trainParam的参数列表 trainParam中的各个具体参数如下…

【数据结构(三)】单链表(1)

文章目录 1. 链表介绍2. 单链表应用实例2.1. 顺序添加方式2.1.1. 思路分析2.1.2. 代码实现 2.2. 按照编号顺序添加方式2.2.1. 思路分析2.2.2. 代码实现 3. 单链表节点的修改3.1. 思路分析3.2. 代码实现 4. 单链表节点的删除4.1. 思路分析4.2. 代码实现 5. 单链表常见面试题5.1.…

代码随想录算法训练营|五十九~六十天

下一个更大元素|| 503. 下一个更大元素 II - 力扣&#xff08;LeetCode&#xff09; 和每日温度一样的套路&#xff0c;就是这里可以循环数组&#xff0c;两个数组拼接&#xff0c;然后循环两遍就行。 public class Solution {public int[] NextGreaterElements(int[] nums)…

Python如何实现模板方法设计模式?什么是模板方法设计模式?Python 模板方法设计模式示例代码

什么是模板方法&#xff08;Template Method&#xff09;设计模式&#xff1f; 模板方法&#xff08;Template Method&#xff09;是一种行为型设计模式&#xff0c;它定义了一个算法的骨架&#xff0c;将一些步骤延迟到子类中实现。这种模式允许子类为一个算法的特定步骤提供…

DeepStream--测试lpdnet车牌检测模型

模型地址&#xff1a;https://catalog.ngc.nvidia.com/orgs/nvidia/teams/tao/models/lpdnet/version 模型格式已经从加密的etlt格式变为onnx格式。这个模型用于从汽车图片上检测出车牌位置&#xff0c;模型有两个&#xff0c;一个用于美国车&#xff0c;一个用于中国车。 Nv…

Mysql之聚合函数

Mysql之聚合函数 什么是聚合函数常见的聚合函数GROUP BYWITH ROLLUPHAVINGHAVING与WHERE的对比 总结SQL底层原理 什么是聚合函数 对一组数据进行汇总的函数&#xff0c;但是还是返回一个结果 聚合函数也叫聚集&#xff0c;分组函数 常见的聚合函数 1.AVG(): 求平均值 2.SUM() :…

HarmonyOS基础组件之Button三种类型的使用

简介 HarmonyOS在明年将正式不再兼容Android原生功能&#xff0c;这意味着对于客户端的小伙伴不得不开始学习HarmonyOS开发语言。本篇文章主要介绍鸿蒙中的Button使用。 HarmonyOS中的Button相较于Android原生来说&#xff0c;功能比较丰富&#xff0c;扩展性高&#xff0c;减…

OpenShift 4 - 部署 RHODS 环境,运行 AI/ML 应用(视频)

《OpenShift / RHEL / DevSecOps 汇总目录》 说明&#xff1a;本文已经在 OpenShift 4.14 RHODS 1.33 的环境中验证 文章目录 RHODS 简介安装 RHODS 环境运行环境说明用 RHODS Operator 安装环境创建 Jupyter Notebook 运行环境 开发调式 AI/ML 应用部署运行 AI/ML 应用视频参…

国产压力测试工具的主要作用

国产压力测试工具可以帮助软件开发和维护团队对系统进行全面的性能测试&#xff0c;以评估系统在高负载下的性能表现。以下是国产压力测试工具的主要作用&#xff1a; 性能评估&#xff1a;国产压力测试工具可以模拟多用户同时对系统进行访问和操作&#xff0c;通过对系统的响应…