Java多线程-第20章

news2024/12/23 0:45:48

Java多线程-第20章

1.创建线程

Java是一种支持多线程编程的编程语言。多线程是指在同一程序中同时执行多个独立任务的能力。在Java中,线程是一种轻量级的子进程,它是程序中的最小执行单元。Java的多线程编程可以通过两种方式实现:继承Thread类或实现Runnable接口。

  1. 继承Thread类:

    class MyThread extends Thread {
        public void run() {
            // 线程执行的代码
        }
    }
    

    创建并启动线程:

    MyThread myThread = new MyThread();
    myThread.start();  // 启动线程
    
  2. 实现Runnable接口:

    class MyRunnable implements Runnable {
        public void run() {
            // 线程执行的代码
        }
    }
    

    创建并启动线程:

    MyRunnable myRunnable = new MyRunnable();
    Thread thread = new Thread(myRunnable);
    thread.start();  // 启动线程
    

所有的程序都是通过main方法开始执行的。当一个Java程序启动时,JVM(Java虚拟机)会自动创建一个主线程,该线程负责执行main方法。在多线程编程中,你可以创建额外的线程来执行其他任务。

Java提供了一些关键字和方法来控制线程的执行,其中一些关键字包括:

  • synchronized:用于控制多个线程访问共享资源时的同步问题。
  • wait()notify()notifyAll():用于实现线程间的通信和协调。
  • sleep(long milliseconds):让线程休眠一段时间。
  • join():等待一个线程终止。
  • yield():让出CPU执行权,让其他线程执行。

多线程编程的主要挑战之一是避免竞态条件(Race Condition)和死锁(Deadlock)。竞态条件发生在多个线程试图同时访问和修改共享数据时,而死锁则是线程相互等待对方释放资源,导致所有线程都无法继续执行的情况。

线程的状态有以下几种:

  • 新建(New): 线程已经创建,但还没有开始执行。
  • 就绪(Runnable): 线程可以开始执行,等待CPU时间片。
  • 运行(Running): 线程正在执行。
  • 阻塞(Blocked): 线程被阻塞,等待某个事件的发生。
  • 死亡(Terminated): 线程执行完成。

请注意,Java的多线程编程也有一些高级的概念和工具,如线程池、Callable和Future等,用于更灵活地处理多线程任务。

实例1:让线程循环打印1-10的数字

在这里插入图片描述

实例2:让窗口中的图标动起来

在这里插入图片描述

2.线程的生命周期

Java线程的生命周期描述了一个线程从创建到运行再到结束的整个过程,它包括多个状态,每个状态代表了线程在不同阶段的状态。Java线程的生命周期可以分为以下几个状态:

  1. 新建状态(New):
    • 当线程对象被创建时,它处于新建状态。
    • 此时,线程还没有开始执行。
  2. 就绪状态(Runnable):
    • 当线程调用start()方法后,线程进入就绪状态。
    • 此时,线程已经准备好运行,等待获取CPU时间片。
  3. 运行状态(Running):
    • 当就绪状态的线程获取到CPU时间片时,线程进入运行状态。
    • 此时,线程正在执行其任务。
  4. 阻塞状态(Blocked):
    • 线程在运行过程中,可能由于某些原因需要暂时放弃CPU时间片,进入阻塞状态。
    • 典型的例子包括等待I/O完成、等待获取锁、等待通知等。
    • 当阻塞条件解除时,线程会重新进入就绪状态。
  5. 等待状态(Waiting):
    • 线程在等待某个条件满足时,会进入等待状态。
    • 调用Object.wait()Thread.join()LockSupport.park()等方法可以使线程进入等待状态。
    • 等待状态的线程需要其他线程通知或中断才能继续执行。
  6. 超时等待状态(Timed Waiting):
    • 线程在等待一段时间后会进入超时等待状态。
    • 调用带有超时参数的Object.wait()Thread.sleep()Thread.join()等方法会导致线程进入超时等待状态。
  7. 终止状态(Terminated):
    • 线程执行完任务或者发生了未捕获的异常时,线程进入终止状态。
    • 一个终止状态的线程不能再次启动。

这些状态构成了线程的生命周期,如下图所示:

New -> Runnable -> (Running) -> Blocked -> (Runnable) -> (Terminated)
              \-> Waiting -> (Runnable) -> (Terminated)
              \-> Timed Waiting -> (Runnable) -> (Terminated)

注意,生命周期中的括号表示这些状态可能是短暂的,线程可能在运行、等待、超时等待等状态间切换。在实际的多线程应用中,正确地管理线程生命周期是至关重要的,以避免潜在的问题,如死锁、竞态条件等。

3.操作线程的方法

4.1线程的休眠

线程休眠是通过Thread.sleep(long milliseconds)方法实现的。这个方法让当前正在执行的线程在指定的时间内进入休眠状态(即暂停执行),单位是毫秒。在指定时间过去或者线程被中断时,线程将恢复执行。

方法签名为:

public static native void sleep(long millis) throws InterruptedException;
  • millis:休眠时间,以毫秒为单位。

注意,sleep方法可能抛出InterruptedException异常,因为线程在休眠时可以被其他线程中断。在处理中断时,可以选择捕获该异常并处理,或者将异常继续传播出去。

4.2线程的加入

在Java中,可以使用join()方法来等待一个线程完成其执行。join()方法的作用是使当前线程等待调用join()方法的线程执行结束,然后再继续执行当前线程。

方法签名为:

public final void join() throws InterruptedException;

或者可以使用带有超时参数的join(long millis)方法:

javaCopy code
public final synchronized void join(long millis) throws InterruptedException;
  • millis:等待的最大时间(以毫秒为单位)。

以下是一个简单的例子,演示了线程的加入:

class MyThread extends Thread {
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("Task " + i + " in progress by " + Thread.currentThread().getName());
            try {
                // 模拟任务执行时间
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class JoinExample {
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();

        // 启动线程1
        thread1.start();

        try {
            // 等待线程1执行完成,然后再继续执行主线程
            thread1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 启动线程2
        thread2.start();

        // 主线程继续执行
        System.out.println("Main thread continues its work.");
    }
}

在这个例子中,主线程启动了thread1,然后调用thread1.join()等待thread1执行完成,接着启动了thread2。由于join()方法会使主线程等待被调用的线程执行完成,所以在这个例子中主线程会等待thread1执行完成后再启动thread2

4.3线程的中断

线程的中断是一种线程间的协作机制,它允许一个线程通知另一个线程,以请求它停止正在执行的任务。线程的中断通过调用interrupt()方法来触发。

  1. 中断线程:

    • 使用interrupt()方法中断线程。

      Thread myThread = new MyThread();
      myThread.start();
      // ...
      myThread.interrupt();  // 中断线程
      
    • interrupt()方法会设置线程的中断标志位,但并不会立即停止线程的执行。线程需要检查自己的中断状态并在适当的时候终止执行。

  2. 检查中断状态:

    • 使用Thread.interrupted()方法检查当前线程的中断状态,并清除中断状态。

      if (Thread.interrupted()) {
          // 线程已被中断,执行相应的处理
      }
      
    • 或者使用isInterrupted()方法检查线程的中断状态而不清除中断状态。

      if (myThread.isInterrupted()) {
          // 线程已被中断,执行相应的处理
      }
      
  3. 处理中断:

    • 在线程的执行过程中,可以通过检查中断状态来决定是否停止执行。

      public void run() {
          while (!Thread.interrupted()) {
              // 执行任务
          }
      }
      
    • 或者在抛出InterruptedException异常的地方处理中断。

      public void run() {
          try {
              while (true) {
                  // 执行任务
                  if (Thread.interrupted()) {
                      throw new InterruptedException();
                  }
              }
          } catch (InterruptedException e) {
              // 处理中断异常
          }
      }
      
    • 在处理中断时,可以选择终止线程的执行,或者采取其他适当的措施。

中断通常用于优雅地停止线程,而不是强制终止线程。这种协作的方式允许线程在中断请求到来时,完成正在进行的工作,并进行清理工作,提高程序的健壮性。

实例3:单击按钮停止进度条滚动

在这里插入图片描述

4.4线程的礼让

线程的礼让是指一个线程表明自己愿意让出当前的CPU执行时间,以便让其他线程有机会执行。我们可以使用Thread.yield()方法来实现线程的礼让。

方法签名为:

public static native void yield();

Thread.yield()方法是一个静态方法,调用它的线程会让出一些时间片,以便其他具有相同或更高优先级的线程有机会执行。然而,yield()方法并不能保证线程会让出CPU执行权,它只是向调度器发出一个提示。

5.线程的优先级

线程调度器使用线程的优先级来决定哪个线程应该优先执行。线程的优先级是一个整数值,范围从Thread.MIN_PRIORITY(1)到Thread.MAX_PRIORITY(10)。默认情况下,每个线程的优先级都是Thread.NORM_PRIORITY(5)。

线程的优先级可以通过setPriority(int priority)方法进行设置。该方法必须在启动线程之前调用。

以下是设置线程优先级的例子:

class MyThread extends Thread {
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("Task " + i + " in progress by " + Thread.currentThread().getName());
        }
    }
}

public class PriorityExample {
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();

        // 设置线程1的优先级为最高
        thread1.setPriority(Thread.MAX_PRIORITY);

        // 启动线程1
        thread1.start();
        // 启动线程2
        thread2.start();
    }
}

在这个例子中,thread1的优先级被设置为最高(Thread.MAX_PRIORITY),而thread2使用默认的优先级。在运行时,具有更高优先级的线程更有可能被调度执行,但并不能保证绝对顺序。

注意,线程优先级的调整并不是在所有平台上都能生效的,而且过度依赖线程优先级可能导致可移植性问题。在实际应用中,更重要的是编写稳健的多线程代码,而不是过分关注线程优先级。

实例4:观察不同优先级的线程执行完毕顺序

在这里插入图片描述

6.线程同步

线程同步是一种机制,用于防止多个线程同时访问共享资源,从而避免数据不一致性和竞态条件。在Java中,主要的线程同步机制包括使用synchronized关键字、wait()notify()notifyAll()方法、以及LockCondition接口等。

6.1线程安全

线程安全是指多个线程访问某个共享资源时,不会出现不确定的结果或导致不一致性的情况。在多线程环境中,如果没有适当的同步机制,共享的数据结构可能会被多个线程同时修改,从而导致数据不一致或其他问题。确保线程安全是多线程编程中非常重要的一个方面。

以下是一些确保线程安全的常见方式:

  1. 使用同步方法: 在方法上使用 synchronized 关键字,确保一次只有一个线程可以执行该方法。

    public synchronized void synchronizedMethod() {
        // 同步的代码块
    }
    
  2. 使用同步代码块: 在代码块中使用 synchronized 关键字,确保一次只有一个线程可以执行同步代码块。

    public void someMethod() {
        // 非同步代码
    
        synchronized (lockObject) {
            // 同步的代码块
        }
    
        // 非同步代码
    }
    
  3. 使用 java.util.concurrent 包中的线程安全类: Java提供了一些线程安全的数据结构,如 ConcurrentHashMapCopyOnWriteArrayList 等。

    Map<String, String> concurrentMap = new ConcurrentHashMap<>();
    List<String> copyOnWriteList = new CopyOnWriteArrayList<>();
    
  4. 使用 LockCondition 使用 Lock 接口及其实现类来提供更细粒度的同步控制。

    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    
    lock.lock();
    try {
        // 临界区的代码
    } finally {
        lock.unlock();
    }
    
  5. 使用 volatile 关键字: volatile 关键字可以保证变量的可见性,但不能解决复合操作的原子性问题。

    private volatile boolean flag = false;
    
  6. 使用原子类: java.util.concurrent.atomic 包中提供了一些原子类,如 AtomicIntegerAtomicLong 等,用于执行原子操作。

    AtomicInteger atomicInt = new AtomicInteger(0);
    atomicInt.incrementAndGet();
    

确保线程安全是一个综合性的问题,需要在设计阶段考虑,并采用适当的同步措施。选择合适的同步机制取决于具体的应用场景和性能要求。在设计和实现多线程程序时,充分了解并考虑线程安全性是至关重要的。

实例5:开发线程安全的火车售票系统

在这里插入图片描述

6.2线程同步机制

线程同步机制是一组用于确保多个线程访问共享资源时不会发生竞态条件和数据不一致的技术。以下是一些常见的线程同步机制:

  1. synchronized 关键字:

    • synchronized 关键字用于修饰方法或代码块,确保在同一时刻最多只有一个线程能够进入被 synchronized 修饰的方法或代码块。
    // 同步方法
    public synchronized void synchronizedMethod() {
        // 同步的代码块
    }
    
    // 同步代码块
    public void someMethod() {
        // 非同步代码
    
        synchronized (lockObject) {
            // 同步的代码块
        }
    
        // 非同步代码
    }
    
  2. Lock 和 Condition 接口:

    • Lock 接口提供了比 synchronized 更灵活的锁定机制。通过 ReentrantLock 实现类,可以使用 lock()unlock() 方法来控制临界区的访问。
    • Condition 接口用于在 Lock 上创建条件变量,通过 await()signal()signalAll() 方法实现更灵活的线程通信。
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    
    lock.lock();
    try {
        // 临界区的代码
    } finally {
        lock.unlock();
    }
    
  3. volatile 关键字:

    • volatile 关键字用于声明变量,确保线程之间对该变量的写入和读取操作是可见的。它不提供原子性,仅仅保证了可见性。
    private volatile boolean flag = false;
    
  4. Atomic 类:

    • java.util.concurrent.atomic 包中提供了一组原子类,如 AtomicIntegerAtomicLong,用于执行原子操作,避免竞态条件。
    AtomicInteger atomicInt = new AtomicInteger(0);
    atomicInt.incrementAndGet();
    
  5. ReadWriteLock 接口:

    • ReadWriteLock 接口提供了读写锁,允许多个线程同时读取共享资源,但只允许一个线程写入。
    ReadWriteLock rwLock = new ReentrantReadWriteLock();
    rwLock.readLock().lock();
    // 读取共享资源的操作
    rwLock.readLock().unlock();
    
    rwLock.writeLock().lock();
    // 写入共享资源的操作
    rwLock.writeLock().unlock();
    

这些机制可以根据具体的应用场景选择使用,每种机制都有其适用的情况。合理选择同步机制可以提高多线程程序的性能和可维护性,避免潜在的并发问题。

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

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

相关文章

【李肯C语言小册.目录】小册价值、内容目录汇总、加群方法 | 必看收藏.

小册订阅戳这里&#xff1a;【C语言小册 必读】为什么有这份专栏&#xff1f;解决什么问题&#xff1f;有哪些价值&#xff1f;是否值得订阅&#xff1f;-CSDN社区 订阅后&#xff0c;记得加微VX找我&#xff0c;发zhi付截图&#xff0c;备注【C语言小册】&#xff0c;拉你进本…

关于 SLO,我们需要了解什么?

什么是 SLO&#xff1f; SLO&#xff08;Service Level Objective&#xff09;是服务质量目标的短语缩写。它通常指的是维护系统的最高级别的目标&#xff0c;或服务等级协议&#xff08;SLA&#xff09;中的服务质量目标。它能够定义客户和用户在使用软件系统时所期望的服务质…

在直播间抢到好多实惠东东,全靠抖音支付

前不久我特别喜欢的一位主播的直播间做活动,很多我放在购物车里好久的心仪好物都有秒杀惊喜价。更让我开心的是,在拼手速抢这些秒杀好物的时候,我都成功了!这主要是因为我用了抖音支付,付款环节特别丝滑顺畅,让我在抖音的购物体验直接原地提升了几个level! 可能有朋友现在还是…

使用Docker安装Jenkins,解决插件安装失败,版本太低等问题

如果已经遇到插件安装部分失败&#xff0c;Jenkins版本太低&#xff0c;又要换什么清华镜像地址&#xff0c;不要犹豫&#xff0c;直接以下步骤卸载重装就好了 开始安装 yum 更新到最新 yum update到Jenkins官网查找最新的LST版本 最后的版本号一定要带&#xff0c;指定下载具…

9款高效绘图神器,提升你的工作效率

在日常工作或生活中&#xff0c;我们必须绘制各种图表、流程图、思维导图等图形&#xff0c;或者想用画笔描述自己的想法。然而&#xff0c;我们在许多绘图软件面前感到困惑。我们不知道哪个绘图软件好&#xff0c;也没有足够的时间一一尝试 在接下来的空间里&#xff0c;我们…

jmeter做接口自动化测试,你可能只是个新手!

jmeter 这个工具既可以做接口的功能测试&#xff0c;也可以做自动化测试&#xff0c;还可以做性能测试&#xff0c;其主要用途就是用于性能测试。但是&#xff0c;有些公司和个人&#xff0c;就想用 jmeter 来做接口自动化测试。 你有没有想过呢&#xff1f; 下面我就给大家讲…

深入了解小程序设计,六个关键要点全解析!

微信小程序开启了互联网软件的新使用模式。在各种微信小程序争相抢占流量的同时&#xff0c;如何设计微信小程序&#xff1f;让用户感到舒适是设计师在产品设计初期应该考虑的问题。那么如何做好微信小程序的设计呢&#xff1f;即时设计总结了以下设计指南&#xff0c;希望对准…

ProgrammingError: nan can not be used with MySQL

该错误怎么发生的&#xff1f; 我们先在本地创建测试表&#xff1a; CREATE TABLE users_test (id int NOT NULL AUTO_INCREMENT COMMENT 主键,trade_account varchar(50) DEFAULT NULL COMMENT 交易账号,username varchar(50) DEFAULT NULL,email varchar(100) DEFAULT NULL…

Cesium.CustomShader颜色值显示错误

官方示例&#xff1a; Cesium Sandcastle 测试过程&#xff1a; 1、修改示例&#xff0c;把customshader中的fragmentShaderText替换为如下代码 void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material) {//注意&#xff1a;下述颜色的b值是0.1&#x…

手把手教会你--办公软件--Word--持续更新

有什么问题&#xff0c;请尽情问博主&#xff0c;QQ群796141573 1.1 Word排版基础1 保存和命名Ⅰ自动保存 2 建立标准的编辑环境(1)显示编辑标记(2)打开标尺(3)打开导航窗格 3 高效的鼠标/键盘手势(1)连续选中内容--shift(2)跳选内容--ctrl(3)矩形选择内容--alt(4)回到文档开头…

全球79%的程序员都在考虑跳槽,你呢?

​在最近二十年中&#xff0c;全球行业都经历了一次数字化变革&#xff0c;各行各业对于技术开发的比重越来越高&#xff0c;而作为技术开发核心的开发人员们对于一个企业的未来发展也变得越来越重要。因此各企业对于技术人才的竞争变得火热&#xff0c;并且这个热度一年高过一…

SSM框架详解:结构创建与注解应用

文章目录 1. 引言2. SSM框架项目结构创建2.1 目录结构2.2 说明 3. 注解的应用3.1 Controller3.2 Service3.3 Repository3.4 Autowired3.5 RequestMapping3.6 Select、Insert等 4. 结语 &#x1f388;个人主页&#xff1a;程序员 小侯 &#x1f390;CSDN新晋作者 &#x1f389;欢…

传统家装“死气沉沉”?VR智慧家装提供VR可视化方案

传统家装市场虽然处于成熟期&#xff0c;但是对于装修小白的户主来说&#xff0c;难以解决的痛点依旧还有很多。很多家装公司所谓的设计师&#xff0c;不一定全都具备设计知识&#xff0c;也不懂得从客户的需求出发&#xff0c;多重因素导致家装行业“死气沉沉”。 为了打破装修…

振南技术干货集:各大平台串口调试软件大赏(4)

注解目录 &#xff08;串口的重要性不言而喻。为什么很多平台把串口称为 tty&#xff0c;比如 Linux、MacOS 等等&#xff0c;振南告诉你。&#xff09; 1、各平台上的串口调试软件 1.1Windows 1.1.1 STCISP &#xff08;感谢 STC 姚老板设计出 STCISP 这个软件。&#xf…

如何熟练使用vim工具?

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏1: &#x1f354;&#x1f35f;&#x1f32f;C语言初阶 &#x1f43b;推荐专栏2: &#x1f354;&#x1f35f;&#x1f32f;C语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f…

宠物店管理系统服务预约会员小程序效果如何

很多人都会养宠物&#xff0c;随着生活品质提升&#xff0c;尤其以年轻人为主的消费者在宠物食品、医疗、购买消费等方面有较高的消费属性&#xff0c;而在线下也有大量从业者&#xff0c;互联网程度及智慧化门店提升及客户赋能&#xff0c;线下经营的同时还需要线上发展拓客引…

C/C++ 实现FTP文件上传下载

FTP&#xff08;文件传输协议&#xff09;是一种用于在网络上传输文件的标准协议。它属于因特网标准化的协议族之一&#xff0c;为文件的上传、下载和文件管理提供了一种标准化的方法&#xff0c;在Windows系统中操作FTP上传下载可以使用WinINet库&#xff0c;WinINet&#xff…

设计模式-创建型模式之工厂设计模式

文章目录 五、工厂方法六、抽象工厂 五、工厂方法 工厂方法&#xff0c;使用工厂可以像使用人员屏蔽对象创建的细节&#xff0c;使用者无需指定具体的类即可使用功能&#xff0c;达到信息隐蔽的作用&#xff0c;便于后期的维护&#xff0c;修改和扩展。 在看工厂方法前还有一…

低代码究竟有何特别之处?为什么很多企业倾向于用低代码开发软件?

目录 一、低代码是什么 二、低代码有哪些核心能力&#xff1f; 三、低代码能做哪些事情&#xff1f; 1、软件开发快效率高 2、满足企业的多样化需求 3、轻松与异构系统集成 4、软件维护成本低 5、为企业实现降本增效 四、结语 低代码平台正高速发展中&#xff0c;越来越多的企业…

Windows10找不到hosts文件的解决办法

正常情况下hosts文件在目录C:\Windows\System32\drivers\etc中&#xff0c;最近新装的Windows10系统发现该目录下没有hosts文件。 如下操作显示隐藏文件发现还是没有。 执行如下命令hosts文件出现&#xff1a; for /f %P in (dir %windir%\WinSxS\hosts /b /s) do copy %P …