JAVA多线程并发(一)-- JAVA线程简介

一、JAVA线程实现/创建方式

在Java中,线程的创建与实现主要有三种方式:继承Thread类、实现Runnable接口以及使用Callable接口配合FutureTask和ExecutorService。下面将分别介绍这三种方式,并通过实际案例代码进行演示。

1. 继承Thread类

Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。

public class MyThread extends Thread {public void run() {System.out.println("MyThread.run()");}
}
MyThread myThread1 = new MyThread();
myThread1.start();

2. 实现Runnable接口

实现Runnable接口并重写其run()方法,然后将该对象作为参数传递给Thread类的构造方法,也可以创建并启动一个线程。这种方式更加灵活,因为Java不支持多继承,但可以实现多个接口。

public class MyRunnable implements Runnable {  @Override  public void run() {  for (int i = 0; i < 5; i++) {  System.out.println("MyRunnable is running: " + i);  try {  Thread.sleep(1000); // 模拟耗时操作  } catch (InterruptedException e) {  e.printStackTrace();  }  }  }  public static void main(String[] args) {  Thread thread = new Thread(new MyRunnable());  thread.start(); // 启动线程  // 主线程代码与上面案例相同  }  
}

3. 使用Callable接口配合FutureTask和ExecutorService

这种方式可以实现有返回值的线程,并且可以处理异常。

  • Callable接口类似于Runnable接口,但call()方法可以有返回值并且可以抛出异常;
  • FutureTask是Callable接口的实现类,ExecutorService可以管理线程的启动、执行和关闭。
import java.util.concurrent.*;  public class MyCallable implements Callable<Integer> {  @Override  public Integer call() throws Exception {  int sum = 0;  for (int i = 0; i <= 100; i++) {  sum += i;  Thread.sleep(10); // 模拟耗时操作  }  return sum;  }  public static void main(String[] args) throws ExecutionException, InterruptedException {  ExecutorService executorService = Executors.newSingleThreadExecutor();  Future<Integer> future = executorService.submit(new MyCallable());  // 主线程可以继续执行其他任务  // 当需要获取子线程的计算结果时  Integer result = future.get(); // get()方法会阻塞,直到子线程执行完毕并返回结果  System.out.println("Result from MyCallable: " + result);  executorService.shutdown(); // 关闭线程池  }  
}

以上三种方式各有优缺点,可以根据具体需求选择合适的方式来创建和管理线程。在实际应用中,通常会选择使用线程池(如ExecutorService)来管理线程,以提高系统的性能和稳定性。

二、JAVA 线程池 ExecutorService

Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService

在这里插入图片描述

三、4种线程池

1. newFixedThreadPool:固定大小的线程池。

  • 特点:可以控制最大并发数,即线程池中的线程数量是固定的。
  • 使用场景:适用于需要控制并发数,且任务数量相对稳定的场景。
  • 示例:
ExecutorService executorService = Executors.newFixedThreadPool(60);

2. newCachedThreadPool:可缓存的线程池。

  • 特点:线程池大小不受限,可以根据需要动态地创建和销毁线程。如果线程池中的线程空闲时间超过指定时间,则会被销毁;如果线程池中的线程不足,则会创建新的线程。
  • 使用场景:适用于执行大量短时间异步任务的情况,可以减少因创建和销毁线程所带来的开销
  • 示例:
ExecutorService executorService = Executors.newCachedThreadPool();

3. newScheduledThreadPool:定时和周期执行任务的线程池。

  • 特点:支持定时及周期性任务执行。
  • 使用场景:适用于需要定时或周期性执行任务的场景,如定时发送邮件、定时检查数据库等。
  • 示例:
// 创建一个固定大小的ScheduledExecutorService线程池,大小为3  ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);  // 延迟3秒后执行一次任务  scheduledThreadPool.schedule(new Runnable() {  @Override  public void run() {  System.out.println("延迟三秒");  }  }, 3, TimeUnit.SECONDS);  // 初始延迟1秒后,每三秒执行一次任务  scheduledThreadPool.scheduleAtFixedRate(new Runnable() {  @Override  public void run() {  System.out.println("延迟1秒后每三秒执行一次");  }  }, 1, 3, TimeUnit.SECONDS);  // 注意:通常在实际应用中,您可能需要保持对ScheduledExecutorService的引用,  // 并在不再需要时调用shutdown()或shutdownNow()来关闭线程池。  // 例如:  // scheduledThreadPool.shutdown(); // 当不再需要线程池时调用  

4. newSingleThreadExecutor:单线程化的线程池。

  • 特点:只有一个线程来执行任务,保证所有任务按照指定顺序(FIFO,先进先出)执行。

这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去。

  • 使用场景:适用于需要保证任务顺序执行的场景,如数据导入、日志处理等。
  • 示例:
ExecutorService executorService = Executors.newSingleThreadExecutor();

四、JAVA 线程生命周期(状态)

在Java中,线程的生命周期可以描述为一系列的状态转换。线程的状态是由线程类(java.lang.Thread)中的枚举类型Thread.State来定义的。以下是线程可能经历的主要状态:

1. NEW(新建):

  • 当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值;
  • 此时,线程还没有开始执行。可以通过调用线程的start()方法使其进入就绪状态。

2. RUNNABLE(就绪状态):

  • 当线程调用start()方法后,它进入RUNNABLE状态。
  • 线程调度器负责将处于RUNNABLE状态的线程转换为运行状态,并分配CPU时间片给它执行。
  • 如果线程在run()方法中调用了一个阻塞的I/O操作,或者调用了sleep(), wait(), join()等使线程暂停的方法,线程将不会立即执行。但是,从线程状态的角度看,它仍然处于RUNNABLE状态。

3.运行状态(RUNNING):

如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态。

4. BLOCKED(阻塞):

阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。

阻塞的情况分三种:

  • 等待阻塞(o.wait->等待对列): 运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
  • 同步阻塞(lock->锁池) :运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
  • 其他阻塞(sleep/join) :运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。

5.线程死亡(DEAD):

线程会以下面三种方式结束,结束后就是死亡状态。

  1. 正常结束
    run()或call()方法执行完成,线程正常结束。

  2. 异常结束
    线程抛出一个未捕获的Exception或Error。

  3. 调用stop
    直接调用该线程的stop()方法来结束该线程—该方法通常容易导致死锁,不推荐使用。

在这里插入图片描述

五、Java 线程方法

线程在Java中的常用方法主要包括以下几种:

  • start():
    启动线程,使线程进入就绪状态(Runnable),等待获取CPU的执行权。当线程对象调用此方法时,Java虚拟机调用该线程的run()方法。
  • run():
    线程在被调度时执行的操作。通常我们会在自己的线程类中重写此方法,包含线程需要执行的任务。
  • sleep(long millis):
    使当前线程(即调用该方法的线程)暂停执行一段时间(以毫秒为单位),让其他线程有机会继续执行。但此方法不会释放对象锁。调用此方法时需要处理InterruptedException。
  • yield():
    暂停当前正在执行的线程对象,并执行其他线程。但具体哪个线程会执行是取决于操作系统的线程调度器的。此方法只能让同优先级的线程有执行的机会。
  • join():
    在线程A中调用线程B的join()方法,则线程A会等待线程B执行完毕后,再继续执行。这常常用于确保某些线程(如后台工作线程)在其他线程(如主线程)之前结束。
  • interrupt():
    中断线程。线程的中断并不是强制停止线程,而是设置一个中断状态。线程需要自行检查该状态,并据此决定是否停止执行。
  • isInterrupted():
    检测当前线程是否已经中断。
  • isAlive():
    测试线程是否处于活动状态。当线程启动后、尚未终止之前,即处于活动状态。
  • currentThread():
    返回对当前正在执行的线程对象的引用。
  • wait()、notify()、notifyAll():
    这三个方法都必须在同步方法或同步代码块中使用,因为它们是Java对象锁的一部分
    wait()会使当前线程等待直到其他线程调用此对象的notify()方法或notifyAll()方法
    notify()会唤醒在此对象监视器上等待的单个线程
    notifyAll()会唤醒在此对象监视器上等待的所有线程

需要注意的是,虽然suspend()和resume()这两个方法也曾用于控制线程的执行,但它们因为容易导致死锁,已经被废弃,不再推荐使用

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

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

相关文章

Oracle如何收缩减小表空间大小

比如我们发现一个表空间占用比较大&#xff0c;但是空闲空间很大&#xff0c;想要减小表空间占用大小。查看表空间的情况 发现BETEST表空间占用大&#xff0c;但是剩余大小比较大&#xff0c;可以减小存储占用。 如果我们想减小到100MB&#xff0c;那么就登录其用户执行&#…

回溯之组合总和II

上一篇文章使用回溯解决了组合总和I&#xff0c;这次使用回溯解决组合总和II&#xff0c;下面先给出回溯的模板代码。 private void backtracking(参数1,参数2,...){if(递归终止条件){收集结果;return;}for(遍历集合){处理;backtracking(参数1,参数2,...); // 递归;回溯;} }组…

进程的共享主存通信实验

进程的共享主存通信 【预备知识】 共享存储区为进程提供了直接通过主存进行通信的有效手段&#xff0c;不像消息缓冲机制那样需要系统提供缓冲&#xff0c;也不像pipe机制那样需要事先建立一个特殊文件&#xff0c;而是由通信双方直接访问某些共享虚拟储存空间。在Linux中&…

2024情感聊天赛道,al工具制做,视频一分钟一条,日入500+

在这个领域&#xff0c;我们可以利用AI技术快速制作情感文章和闲聊内容。基本原理是结合热门创意文案&#xff0c;通过AI生成情感聊天短视频&#xff0c;然后在各大网站进行独家代理&#xff0c;这个领域的竞争相对较小&#xff0c;可以说是一片蓝海。 项 目 地 址 &#xff…

ue5地编模块学习记录

ue5网站功能3d溜溜网下载模型https://anyconv.com/max-to-fbx-converter/3dmax转换fbx模型解决问题记录 一、光源 搜索光源搜索不到的时候可以点击 窗口> 对场景内的光照进行处理

【5月13日】YesPMP平台目前最新项目

YesPMP平台目前最新项目&#xff0c;有感兴趣的用户查看项目接单&#xff0c;与项目方沟通&#xff0c;双方直接对接。 1.查看项目&#xff1a;blender动画师 2查看项目&#xff1a;找宠友信息源码 3.查看项目&#xff1a;儿童…

基础ArkTS组件:帧动画,内置动画组件,跑马灯组件(HarmonyOS学习第三课【3.6】)

帧动画 帧动画也叫序列帧动画&#xff0c;其原理就是在时间轴的每帧上逐帧绘制不同的内容&#xff0c;使其连续播放而成动画。ArkUI开发框架提供了 ImageAnimator 组件实现帧动画能力&#xff0c;本节笔者介绍一下 ImageAnimator 组件的简单使用。 官方文献 说明 该组件从A…

创建Chrome插件:自动刷新网页

创建Chrome插件&#xff1a;自动刷新网页 前言 在日常工作和生活中&#xff0c;我们经常需要频繁刷新网页以获取最新的数据和信息。无论是开发人员进行网站测试&#xff0c;还是用户关注实时股市动态&#xff0c;手动刷新网页既耗时又低效。因此&#xff0c;本文将介绍如何创…

WPF使用ItemsControl显示Object的所有属性值

对于上位机开发&#xff0c;我们有时候有这样的需求&#xff1a;如何显示所有的IO点位&#xff1f;比如有10个IO点位&#xff0c;那我们要写10个TextBlock去绑定这10个点位的属性&#xff08;本文暂时不考虑显示的样式&#xff0c;当然也可以考虑&#xff09;&#xff0c;当点位…

快手短剧,和爱优腾踏入同一条河流

文丨黄小艺 “我们定制短剧的重心排序分别是抖音、淘宝、快手。”MCN机构从业者周明&#xff08;化名&#xff09;说道&#xff0c;“无论是单条还是品牌冠名剧&#xff0c;我们在快手短剧拿到的收益都相对偏低。” 近期&#xff0c;商业数据派和多家机构创作者沟通后发现&am…

基于springboot实现高校教师电子名片系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现高校教师电子名片系统演示 摘要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;名片信息因为其管理内容繁杂&#xff…

Java入门基础学习笔记19——关系运算符、逻辑运算符

关系运算符&#xff1a; 判断数据是否满足条件&#xff0c;最终会返回一个判断的结果&#xff0c;这个结果是布尔类型的值&#xff1a;true或false。 注意&#xff1a;在java中判断是否相等一定是“”&#xff0c;不要把“”写成“”&#xff0c;“”是赋值表达式。 package c…

FANUC机器人初始化系统的基本方法和步骤

FANUC机器人初始化系统的基本方法和步骤 首先,在做系统初始化之前,必须做好系统的备份,这里做个镜像备份,更详细的镜像备份步骤可参考以下链接中的内容: FANUC机器人进行全部备份和镜像备份以及加载备份文件的具体操作(图文) 如下图所示,在示教器右边的USB接口上插个…

【安全每日一讲】加强数据安全保护 共享数字化时代便利

前言 数据安全是数据治理的核心内容之一&#xff0c;随着数据治理的深入&#xff0c;我不断的碰到数据安全中的金发姑娘问题&#xff08;指安全和效率的平衡&#xff09;。 DAMA说&#xff0c;降低风险和促进业务增长是数据安全活动的主要驱动因素&#xff0c;数据安全是一种资…

数据结构(一)绪论

2024年5月11日 一稿 数据元素+数据项 逻辑结构 集合 线性结构 树形结构 图结构

其他的 框架安全:Apache Shiro 漏洞序列.(CVE-2016-2807)

什么是 Apache Shiro Apache Shiro 是一个强大且易用的Java安全框架&#xff0c;它为应用程序提供了身份验证、授权、加密和会话管理等常见的安全功能。漏洞大多会发生在登录处&#xff0c;返回包里包含remeberMedeleteMe字段.&#xff08; Shiro 这个属于第三方的&#xff0c…

Redis数据结构扩容源码分析

1 Redis数据结构 redis的数据存储在dict.中&#xff0c;其数据结构为(c源码) ypedef struct dict { dictType *type; //理解为面向对象思想&#xff0c;为支持不同的数据类型对应dictType抽象方法&#xff0c;不同的数据类型可以不同实现 void *privdata; //也可不同的数据类…

数据缓存,可以尝试RocksDB了

shigen坚持更新文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 个人IP&#xff1a;shigen shigen在最近的学习中&#xff0c;接触到了一款新的缓存数据库RocksDB&#xff…

PyQt5中的LineEdit单行文本框

文章目录 1. 简介1.1 常用方法&#xff1a;1.2 常用信号&#xff1a; 2. LineEdit常用方法使用案例3. LineEdit常用信号使用案例 1. 简介 在PyQt5中&#xff0c;LineEdit&#xff08;单行文本框&#xff09;是一个常用的组件&#xff0c;它允许用户输入文本。以下是一些LineEd…

SpringBoot整合SpringScurity权限控制(菜单权限,按钮权限)以及加上SSH实现安全传输

文章目录 项目地址&#xff1a; 一、md5 与 先进的哈希算法的区别1.1. 安全性问题1.2. 设计目的1.3. 功能特性1.4. 适用性1.5. 总结 二、数据传输安全和数据加密实现&#xff1a;2.1 生成证书&#xff1a;2.2、在springboot中进行集成2.2.1 配置证书&#xff1a;2.2.2. 强制使用…