JAVA并发编程3--多线程程序

1.创建线程的方法:

案例:计算1-1000的整数和

实现Runnable接口

步骤:
1.创建一个实现了Runnable接口的类
2.实现类去实现Runnable中的抽象方法:run()
3.创建实现类的对象
4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
5.通过Thread类的对象调用start() ① 启动线程 ②调用当前线程的run()–>调用了Runnable类型的target的run()

import java.util.concurrent.atomic.AtomicLong;public class ThreadLearn {// 使用 AtomicLong 替代 Long,保证线程安全且避免空指针问题public static AtomicLong result = new AtomicLong(0);static int count = 100000;public static CountDownLatch countDownLatch = new CountDownLatch(count);public static ExecutorService executor=Executors.newFixedThreadPool(10);public static void main(String[] args) throws InterruptedException, ExecutionException {long start = System.currentTimeMillis();Thread[] threads = new Thread[count];for (int i = 0; i < count; i++) {MyThread myThread = new MyThread(100);threads[i] = new Thread(myThread);threads[i].start();}countDownLatch.await();System.out.println(result.get()); // 使用 get 方法获取 AtomicLong 的值System.out.println(System.currentTimeMillis() - start);}
}
class MyThread implements Runnable {private int count;public MyThread(int count) {this.count = count;}@Overridepublic void run() {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}// 使用 AtomicLong 的 addAndGet 方法进行原子更新ThreadLearn.result.addAndGet(sum);ThreadLearn.countDownLatch.countDown();}
}

继承Thread类

步骤:
1.创建一个继承于Thread类的子类
2.重写Thread类的run() --> 将此线程执行的操作声明在run()中
3.创建Thread类的子类的对象
4.通过此对象调用start()执行线程

import java.util.concurrent.atomic.AtomicLong;public class ThreadLearn {// 使用 AtomicLong 替代 Long,保证线程安全且避免空指针问题public static AtomicLong result = new AtomicLong(0);static int count = 100000;public static CountDownLatch countDownLatch = new CountDownLatch(count);public static ExecutorService executor=Executors.newFixedThreadPool(10);public static void main(String[] args) throws InterruptedException, ExecutionException {long start = System.currentTimeMillis();Thread[] threads = new Thread[count];for (int i = 0; i < count; i++) {MyThread1 myThread = new MyThread1(100);threads[i] = new Thread(myThread);threads[i].start();}countDownLatch.await();System.out.println(result.get()); // 使用 get 方法获取 AtomicLong 的值System.out.println(System.currentTimeMillis() - start);}
}
class MyThread1 extends Thread {private long count;MyThread1(int count) {this.count = count;}@Overridepublic void run() {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}// 使用 AtomicLong 的 addAndGet 方法进行原子更新ThreadLearn.result.addAndGet(sum);ThreadLearn.countDownLatch.countDown();}
}

实现Callable接口

步骤:
1.创建一个实现Callable的实现类
2.实现call方法,将此线程需要执行的操作声明在call()中
3.创建Callable接口实现类的对象
4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
6.获取Callable中call方法的返回值

package Reflection;import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicLong;public class ThreadLearn {// 使用 AtomicLong 替代 Long,保证线程安全且避免空指针问题public static AtomicLong result = new AtomicLong(0);static int count = 100000;public static CountDownLatch countDownLatch = new CountDownLatch(count);public static void main(String[] args) throws InterruptedException, ExecutionException {
//        long start = System.currentTimeMillis();
//        Thread[] threads = new Thread[count];
//        for (int i = 0; i < count; i++) {
//            MyThread1 myThread = new MyThread1(100);
//            threads[i] = new Thread(myThread);
//            threads[i].start();
//        }
//        countDownLatch.await();
//        System.out.println(result.get()); // 使用 get 方法获取 AtomicLong 的值
//        System.out.println(System.currentTimeMillis() - start);FutureTask<Integer> futureTask=new FutureTask<>(new MyThread2(100));Thread thread=new Thread(futureTask);thread.start();Integer o = futureTask.get();System.out.println(o);}
}class MyThread implements Runnable {private int count;public MyThread(int count) {this.count = count;}@Overridepublic void run() {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}// 使用 AtomicLong 的 addAndGet 方法进行原子更新ThreadLearn.result.addAndGet(sum);ThreadLearn.countDownLatch.countDown();}
}class MyThread1 extends Thread {private long count;MyThread1(int count) {this.count = count;}@Overridepublic void run() {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}// 使用 AtomicLong 的 addAndGet 方法进行原子更新ThreadLearn.result.addAndGet(sum);ThreadLearn.countDownLatch.countDown();}
}class MyThread2 implements Callable<Integer> {private int count;public MyThread2(int count) {this.count=count;}@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}return sum;}
}

使用线程池

package Reflection;import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;public class ThreadLearn {// 使用 AtomicLong 替代 Long,保证线程安全且避免空指针问题public static AtomicLong result = new AtomicLong(0);static int count = 100000;public static CountDownLatch countDownLatch = new CountDownLatch(count);//线程池public static ExecutorService executor=Executors.newFixedThreadPool(10);public static void main(String[] args) throws InterruptedException, ExecutionException {Future<Integer> submit = executor.submit(new MyThread2(100));Integer i=submit.get();System.out.println(i);}
}class MyThread2 implements Callable<Integer> {private int count;public MyThread2(int count) {this.count=count;}@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}return sum;}
}

2.统计多线程程序的运行时间

如果要统计多线程程序的执行时间,主线程等待所有子线程完成,那么总时间可能接近实际所有线程执行完毕的时间。所以可能需要确保主线程在所有子线程结束后才结束,然后计算整个程序的运行时间。

使用join方法

使用thread.join,` 让一个线程等待另一个线程执行完毕。在多线程编程中,有时我们需要确保某些线程在其他线程完成特定任务后再继续执行,这时就可以使用 join() 方法来实现线程间的同步。

package Reflection;import java.util.concurrent.atomic.AtomicLong;public class ThreadLearn {// 使用 AtomicLong 替代 Long,保证线程安全且避免空指针问题public static AtomicLong result = new AtomicLong(0);public static void main(String[] args) throws InterruptedException {long start = System.currentTimeMillis();int count = 10000; // 将线程数量和数组长度统一为 10000Thread[] threads = new Thread[count];for (int i = 0; i < count; i++) {MyThread myThread = new MyThread(100);threads[i] = new Thread(myThread);threads[i].start();}for (Thread thread : threads) {thread.join();}System.out.println(result.get()); // 使用 get 方法获取 AtomicLong 的值System.out.println(System.currentTimeMillis() - start);}
}class MyThread implements Runnable {private int count;public MyThread(int count) {this.count = count;}@Overridepublic void run() {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}// 使用 AtomicLong 的 addAndGet 方法进行原子更新ThreadLearn.result.addAndGet(sum);}
}

在这里插入图片描述

使用CountDownLatch

CountDownLatch 是 Java 并发包 java.util.concurrent 中的一个同步辅助类,主要用于协调多个线程之间的执行顺序。它可以让一个或多个线程等待其他一组线程完成它们的操作后再继续执行,从而确保程序的执行逻辑按照预期进行,避免出现数据不一致或逻辑错误的问题。

package Reflection;import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;public class ThreadLearn {// 使用 AtomicLong 替代 Long,保证线程安全且避免空指针问题public static AtomicLong result = new AtomicLong(0);static int count = 100000;public static CountDownLatch countDownLatch = new CountDownLatch(count);public static void main(String[] args) throws InterruptedException {long start = System.currentTimeMillis();Thread[] threads = new Thread[count];for (int i = 0; i < count; i++) {MyThread myThread = new MyThread(100);threads[i] = new Thread(myThread);threads[i].start();}countDownLatch.await();System.out.println(result.get()); // 使用 get 方法获取 AtomicLong 的值System.out.println(System.currentTimeMillis() - start);}
}class MyThread implements Runnable {private int count;public MyThread(int count) {this.count = count;}@Overridepublic void run() {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}// 使用 AtomicLong 的 addAndGet 方法进行原子更新ThreadLearn.result.addAndGet(sum);ThreadLearn.countDownLatch.countDown();}
}

执行结果:
在这里插入图片描述

CountDownLatch 的执行过程
1. 初始化阶段
  • 首先,需要创建一个 CountDownLatch 实例,在创建时要传入一个初始计数值。这个计数值代表了需要等待完成的操作数量。例如在代码中 CountDownLatch countDownLatch = new CountDownLatch(count);,这里的 count 就是初始计数值,它表明有 count 个任务需要完成,CountDownLatch 会将这个值存储在内部作为计数器的初始状态。
2. 任务线程启动阶段
  • 通常会有一个或多个任务线程被创建并启动去执行特定的任务。这些线程可能会并发地执行各自的任务,就像代码里通过循环创建并启动多个 Thread 实例,每个线程都去执行 MyThread 类的 run 方法中的任务。
3. 等待阶段
  • 有一个或多个线程(通常是主线程)会调用 CountDownLatchawait() 方法。当调用这个方法时,调用线程会进入阻塞状态,它会一直等待,直到 CountDownLatch 的内部计数器值变为 0。这意味着它在等待所有任务都完成。
4. 任务完成与计数器递减阶段
  • 每个任务线程在完成自己的任务后,会调用 CountDownLatchcountDown() 方法。这个方法的作用是将 CountDownLatch 的内部计数器值减 1。随着越来越多的任务线程完成任务并调用 countDown() 方法,计数器的值会不断减小。
5. 唤醒等待线程阶段
  • CountDownLatch 的内部计数器值减到 0 时,所有之前调用 await() 方法并处于阻塞状态的线程会被唤醒。这些线程会从 await() 方法处继续执行后续的代码逻辑。
6. 后续处理阶段
  • 被唤醒的线程可以继续执行后续的操作,比如代码中主线程在被唤醒后会打印最终的计算结果以及程序执行所花费的时间。
主要方法
  • CountDownLatch(int count) 构造方法:用于创建一个 CountDownLatch 实例,并初始化其内部计数器的值为 count。这个 count 表示需要等待完成的操作数量,必须是一个正整数。
  • void await() 方法:调用该方法的线程会进入阻塞状态,直到 CountDownLatch 的内部计数器值变为 0。如果在等待过程中当前线程被中断,会抛出 InterruptedException 异常。
  • boolean await(long timeout, TimeUnit unit) 方法:调用该方法的线程会等待一段时间,最多等待 timeout 时间(由 unit 指定时间单位)。如果在这段时间内计数器值变为 0,则线程会被唤醒并返回 true;如果超过指定时间计数器值仍不为 0,则线程会被唤醒并返回 false。同样,如果在等待过程中线程被中断,会抛出 InterruptedException 异常。
  • void countDown() 方法:该方法会将 CountDownLatch 的内部计数器值减 1。当计数器值减到 0 时,所有正在等待的线程会被唤醒。如果计数器值已经为 0,调用该方法不会产生任何效果。
  • long getCount() 方法:该方法用于返回 CountDownLatch 当前的计数器值。可以通过这个方法来查看还有多少个任务未完成。

3.如何解决多线程对共享变量操作的线程安全问题

package Reflection;public class ThreadLearn {public static long result ;public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 10000; i++) {MyThread myThread = new MyThread(1000);Thread thread = new Thread(myThread);thread.start();}Thread.sleep(10*1000);System.out.println(result);}}
class MyThread implements Runnable {private int count;public MyThread(int count) {this.count = count;}@Overridepublic void run() {int sum = 0;for (int i = 0; i <= count; i++) {sum+=i;}ThreadLearn.result+=sum;System.out.println(sum);}
}

执行结果:
​​在这里插入图片描述

代码功能概述

这段 Java 代码主要的功能是创建 10000 个线程,每个线程会计算从 0 到 1000 的整数之和,然后将这个和累加到 ThreadLearn 类的静态变量 result 中。最后,主线程等待 10 秒后输出 result 的值。

代码存在的问题

  1. 线程安全问题ThreadLearn.result 是一个共享变量,多个线程同时对其进行写操作(ThreadLearn.result += sum;),这会导致数据竞争(Data Race)问题,最终的 result 值可能是错误的。
  2. 等待时间不确定性:使用 Thread.sleep(10 * 1000) 来等待所有子线程完成,这种方式不够可靠,因为不同的机器性能不同,可能会导致有些线程还未执行完,主线程就已经输出结果。

你提供的代码在多线程环境下存在线程安全问题,主要是因为多个线程同时对静态变量 ThreadLearn.result 进行写操作,这可能会导致数据竞争和不一致的结果。下面为你介绍几种解决该问题的方法。

方法一:使用 synchronized 关键字

synchronized 关键字可以用来修饰方法或代码块,保证同一时刻只有一个线程能够访问被修饰的代码,从而避免多线程对共享资源的并发访问问题。

package Reflection;public class ThreadLearn {public static long result;public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 10000; i++) {MyThread myThread = new MyThread(1000);Thread thread = new Thread(myThread);thread.start();}Thread.sleep(10 * 1000);System.out.println(result);}
}class MyThread implements Runnable {private int count;public MyThread(int count) {this.count = count;}@Overridepublic void run() {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}// 使用 synchronized 块保证线程安全synchronized (ThreadLearn.class) {ThreadLearn.result += sum;}System.out.println(sum);}
}

解释:在 run 方法中,使用 synchronized (ThreadLearn.class)ThreadLearn.result += sum; 这行代码进行同步,这样同一时刻只有一个线程能够执行该代码块,从而保证了对 result 变量的线程安全访问。

执行结果:
在这里插入图片描述

方法二:使用 AtomicLong 类

AtomicLong 是 Java 提供的一个原子类,它提供了一些原子操作方法,可以保证对长整型变量的原子性更新,避免了使用 synchronized 带来的性能开销。

package Reflection;import java.util.concurrent.atomic.AtomicLong;public class ThreadLearn {// 使用 AtomicLong 代替 longpublic static AtomicLong result = new AtomicLong(0);public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 10000; i++) {MyThread myThread = new MyThread(1000);Thread thread = new Thread(myThread);thread.start();}Thread.sleep(10 * 1000);System.out.println(result.get());}
}class MyThread implements Runnable {private int count;public MyThread(int count) {this.count = count;}@Overridepublic void run() {int sum = 0;for (int i = 0; i <= count; i++) {sum += i;}// 使用 AtomicLong 的 addAndGet 方法进行原子更新result.addAndGet(sum);System.out.println(sum);}
}

解释:将 ThreadLearn 类中的 result 变量类型改为 AtomicLong,并使用 addAndGet 方法来更新 result 的值。addAndGet 方法是原子操作,能够保证在多线程环境下对 result 的更新是线程安全的。

执行结果:
在这里插入图片描述

在这里插入图片描述

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

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

相关文章

django中间件,中间件给下面传值

1、新建middleware.py文件 # myapp/middleware.py import time from django.http import HttpRequest import json from django.http import JsonResponse import urllib.parse class RequestTimeMiddleware:def __init__(self, get_response):self.get_response get_respons…

Vision Transformer:打破CNN垄断,全局注意力机制重塑计算机视觉范式

目录 引言 一、ViT模型的起源和历史 二、什么是ViT&#xff1f; 图像处理流程 图像切分 展平与线性映射 位置编码 Transformer编码器 分类头&#xff08;Classification Head&#xff09; 自注意力机制 注意力图 三、Coovally AI模型训练与应用平台 四、ViT与图像…

深入浅出:探索 DeepSeek 的强大功能与应用

深入浅出&#xff1a;探索 DeepSeek 的强大功能与应用 在人工智能技术飞速发展的今天&#xff0c;自然语言处理&#xff08;NLP&#xff09;作为其重要分支&#xff0c;正逐渐渗透到我们生活的方方面面。DeepSeek 作为一款功能强大的 NLP 工具&#xff0c;凭借其易用性和高效性…

为AI聊天工具添加一个知识系统 之97 详细设计之38 Token 之3 前端开发

本文要点 要点 本项目&#xff08;为使用各种聊天工具的聊天者 加挂一个知识系统&#xff09;前端 的用户界面上 主要 有 知识图谱、认知地图和语义网络&#xff0c;和 聊天工具的chat嵌入 同时还必须有知识树 成熟的果实&#xff08;对外的网页 ) 以及 组建和参与利益相关组…

轮子项目--消息队列的实现(3)

上一篇文章中我把一些关键的类以及表示出来&#xff0c;如何对这些类对应的对象进行管理呢&#xff1f;管理分为硬盘和内存上&#xff0c;硬盘又分为数据库&#xff08;管理交换机&#xff0c;队列和绑定&#xff09;和文件&#xff08;管理消息&#xff09;&#xff0c;本文就…

Svelte前端框架

Svelte 简介 Svelte 是一个现代的前端框架&#xff0c;用于构建高效、响应式的用户界面。与 React、Vue 和 Angular 等传统框架不同&#xff0c;Svelte 在构建时将组件编译为高效的纯 JavaScript 代码&#xff0c;而不是在浏览器中运行一个庞大的运行时库。这使得 Svelte 应用具…

【转载】开源鸿蒙OpenHarmony社区运营报告(2025年1月)

●截至2025年1月31日&#xff0c;开放原子开源鸿蒙&#xff08;OpenAtom OpenHarmony&#xff0c;简称“开源鸿蒙”或“OpenHarmony”&#xff09;社区累计超过8200名贡献者&#xff0c;共63家成员单位&#xff0c;产生51.2万多个PR、2.9万多个Star、10.5万多个Fork、68个SIG。…

@SneakyThrows:是Java异常处理的“魔法外挂“,还是隐藏的“定时炸弹“?

引言&#xff1a;当Java的异常机制成为"甜蜜的负担" Java的检查型异常&#xff08;Checked Exception&#xff09;设计本意是提升代码健壮性&#xff0c;但开发者常常陷入两难&#xff1a; 要么用try-catch层层包裹代码导致"金字塔噩梦"&#xff0c;要么在…

双周报Vol.65:新增is表达式、字符串构造和数组模式匹配增强、IDE模式匹配补全增强...多项技术更新!

MoonBit更新 新增 is 表达式 这个表达式的语法形式为 expr is pat&#xff0c;这个表达式为 Bool 类型&#xff0c;当 expr 符合 pat 这个模式的时候返回 true&#xff0c;比如&#xff1a; fn use_is_expr(x: Int?) -> Unit {if x is Some(i) && i > 10 { .…

Git 与持续集成 / 持续部署(CI/CD)的集成

一、引言 在当今快速发展的软件开发领域&#xff0c;高效的代码管理和持续的交付流程是项目成功的关键因素。Git 作为一款分布式版本控制系统&#xff0c;已经成为了开发者们管理代码的标配工具&#xff1b;而持续集成 / 持续部署&#xff08;CI/CD&#xff09;则是一种能够加…

百问网imx6ullpro调试记录(linux+qt)

调试记录 文章目录 调试记录进展1.开发板相关1.1百问网乌班图密码 1.2 换设备开发环境搭建串口调试网络互通nfs文件系统挂载 1.3网络问题1.4系统启动1.5进程操作 2.QT2.1tslib1.获取源码2.安装依赖文件3.编译 2.2qt移植1.获取qt源码2.配置编译器3.编译 2.3拷贝到开发板1.拷贝2.…

开发中用到的设计模式

目录 开发中用到的设计模式 工厂模式 设计理念 好处 体现的编程思想 适配器模式 概念 策略模式和适配器模式的区别 选择策略模式而非适配器模式的原因 设计模式的开发原则 开发中用到的设计模式 在开发过程中&#xff0c;常见的设计模式会根据不同的业务场景和需求被…

1064 - You have an error in your SQL syntax;

在创建数据库表建立外键是遇到了如下报错 1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near position(position_id) ) at line 8 数据库表sql如下&#xff1a; --职位表 CR…

无人机 ,遥控器与接收机之前的通信

目录 1、信号类型 2、工作频率 3、通信协议 3.1、PPM 协议 3.2、SBUS 协议 3.3、CRSF 协议 无人机的遥控器和接收机之间常用的信号、频率、协议等相关信息如下&#xff1a; 1、信号类型 模拟信号 特点&#xff1a;信号的幅度、频率或相位等参数是连续变化的&#xff0c…

【c++】四种类型转换形式

【c】四种类型转换形式 编译时: static_cast&#xff08;静态转换&#xff09; const_cast&#xff08;去常性转换&#xff09; reinterpret_cast&#xff08;重新解释转换&#xff0c;直接转换地址&#xff09; 运行时: dynamic_cast&#xff08;动态转换&#xff0c;运行时类…

Cisco ASR1002查看资源占用的几条命令

查看平台资源 show platform resource 该命令用于显示整个平台的资源使用情况&#xff0c;包括 CPU、内存等 example: ASR1002# show platform resources **State Acronym: H - Healthy, W - Warning, C - Critical Resource…

Day 1:认知革命与DeepSeek生态定位

目标&#xff1a;建立对大模型技术范式的系统性认知&#xff0c;掌握DeepSeek的核心技术特性与生态价值 一、大模型技术演进&#xff1a;从GPT到DeepSeek 1.1 技术发展里程碑 2017-Transformer突破&#xff1a;Self-Attention机制如何突破RNN的序列建模瓶颈 2018-GPT初代&…

Python自动化办公之Excel拆分

在日常办公中&#xff0c;我们经常需要将包含多个Sheet页的Excel文件拆分成多个独立的Excel文件。例如&#xff0c;在发送Excel表给各部门确认时&#xff0c;出于控制知悉范围最小等保密性考虑&#xff0c;每个部门只需要查看和确认自己部门对应的Sheet页。手动拆分Excel文件非…

【CXX-Qt】1.1 Rust中的QObjects

本文涉及到了使用CXX-Qt将Rust、C和QML集成到Qt应用程序中的各个方面。下面&#xff0c;我将提供一个简单的示例&#xff0c;演示如何使用CXX-Qt来创建一个Rust结构体并将其作为QObject子类暴露给C和QML。 一、设置CXX-Qt环境 首先&#xff0c;确保您已经安装了Rust、CXX和CX…

Conda命令整理

Conda 是一个功能强大的包和环境管理工具&#xff0c;广泛用于 Python 开发中。除了基本的包和环境管理功能外&#xff0c;Conda 还提供了许多高级用法和技巧&#xff0c;帮助用户更高效地管理和维护 Python 环境。 1. 管理 Conda 本身 命令描述示例conda --version查看 Cond…