请解释一下 Java 中的多线程。如何创建一个新的线程?如何保证线程安全?

Java 中的多线程是指在一个程序中同时执行多个任务的能力。这可以显著提高应用程序的性能,尤其是在多核处理器上。多线程编程涉及到创建和管理线程,以及确保线程之间的数据共享和同步。

如何创建一个新的线程?

在 Java 中,创建一个新的线程可以通过多种方式实现:

  1. 继承 Thread 类

    • 继承 Thread 类,并重写 run() 方法。
    • 创建该类的实例,并调用 start() 方法来启动线程。
  2. 实现 Runnable 接口

    • 实现 Runnable 接口,并重写 run() 方法。
    • 创建 Runnable 实例,并传递给 Thread 构造函数。
  3. 使用 Callable 和 Future

    • 实现 Callable<V> 接口,并重写 call() 方法。
    • 使用 ExecutorService 提交 Callable 任务,并通过 Future<V> 获取结果。

示例代码

1. 继承 Thread 类
public class MyThread extends Thread {@Overridepublic void run() {System.out.println("Thread is running...");}public static void main(String[] args) {MyThread thread = new MyThread();thread.start(); // 启动线程}
}
2. 实现 Runnable 接口
public class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("Runnable is running...");}public static void main(String[] args) {MyRunnable runnable = new MyRunnable();Thread thread = new Thread(runnable);thread.start(); // 启动线程}
}
3. 使用 Callable 和 Future
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {return 100; // 返回计算结果}public static void main(String[] args) throws Exception {ExecutorService executor = Executors.newSingleThreadExecutor();Future<Integer> future = executor.submit(new MyCallable());int result = future.get(); // 获取计算结果System.out.println("Result: " + result);executor.shutdown(); // 关闭 ExecutorService}
}

如何保证线程安全?

线程安全是指在多线程环境下,程序能够正确地处理数据共享和竞争条件,以避免出现数据不一致或错误的结果。

1. 使用 synchronized 关键字

synchronized 关键字可以用来锁定对象或方法,确保同一时刻只有一个线程可以访问临界区。

public class Counter {private int count = 0;public synchronized void increment() {count++;}public int getCount() {return count;}
}public class Main {public static void main(String[] args) {Counter counter = new Counter();Thread thread1 = new Thread(() -> {for (int i = 0; i < 10000; i++) {counter.increment();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 10000; i++) {counter.increment();}});thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Final count: " + counter.getCount());}
}
2. 使用 Lock 接口
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class CounterWithLock {private int count = 0;private final Lock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}public int getCount() {return count;}
}public class Main {public static void main(String[] args) {CounterWithLock counter = new CounterWithLock();Thread thread1 = new Thread(() -> {for (int i = 0; i < 10000; i++) {counter.increment();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 10000; i++) {counter.increment();}});thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Final count: " + counter.getCount());}
}
3. 使用 Atomic 类
import java.util.concurrent.atomic.AtomicInteger;public class CounterWithAtomic {private AtomicInteger count = new AtomicInteger(0);public void increment() {count.incrementAndGet();}public int getCount() {return count.get();}
}public class Main {public static void main(String[] args) {CounterWithAtomic counter = new CounterWithAtomic();Thread thread1 = new Thread(() -> {for (int i = 0; i < 10000; i++) {counter.increment();}});Thread thread2 = new Thread(() -> {for (int i = 0; i < 10000; i++) {counter.increment();}});thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Final count: " + counter.getCount());}
}

日常开发使用建议

  1. 合理选择线程创建方式

    • 如果只需要简单地执行一些后台任务,使用 Runnable 接口更为常见。
    • 如果需要返回结果,使用 Callable 接口和 Future 机制更为合适。
    • 避免直接继承 Thread 类,除非确实需要访问 Thread 类的方法。
  2. 避免使用全局变量

    • 全局变量容易引发竞态条件,尽量使用局部变量或线程私有的变量。
  3. 使用线程安全的集合

    • 如果需要在线程间共享集合,使用 Collections.synchronizedCollection 或 ConcurrentHashMap 等线程安全的集合。
  4. 使用线程本地变量

    • 对于线程内部使用的变量,可以考虑使用 ThreadLocal 来避免数据共享问题。

实际开发过程中的注意点

  • 死锁

    • 避免多个线程持有不同的锁,并尝试获取对方持有的锁,否则可能导致死锁。
    • 使用 tryLock 方法尝试获取锁,并设置超时时间。
  • 线程中断

    • 如果某个线程需要长时间阻塞等待(如 I/O 操作),应允许线程被中断。
    • 使用 Thread.interrupted() 检查线程是否已被中断。
  • 资源泄露

    • 确保线程结束后释放所有资源,如文件句柄、数据库连接等。
    • 使用 finally 块或 try-with-resources 语句来确保资源被正确关闭。

示例代码:使用线程本地变量

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadLocalExample {public static class Task implements Runnable {private final ThreadLocal<Integer> localValue = new ThreadLocal<>();@Overridepublic void run() {localValue.set((int) (Math.random() * 100));System.out.println("Thread ID: " + Thread.currentThread().getId() + ", Value: " + localValue.get());}}public static void main(String[] args) {ExecutorService executor = Executors.newFixedThreadPool(5);for (int i = 0; i < 10; i++) {executor.execute(new Task());}executor.shutdown();try {executor.awaitTermination(Long.MAX_VALUE, java.util.concurrent.TimeUnit.NANOSECONDS);} catch (InterruptedException e) {e.printStackTrace();}}
}

在实际开发过程中,合理选择线程创建方式,并遵循一些最佳实践,可以帮助我们编写出更高效、更健壮的多线程程序。通过不断地实践和总结经验,我们可以更好地理解和运用多线程编程技术,从而提高开发效率和代码质量。

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

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

相关文章

docker部署nginx、docker常用命令

1、安装nginx 未加版本号&#xff0c;默认最新版 docker pull nginxdocker pull nginx:版本号2、查看是否拉取成功 2-1、查看镜像 docker images2-2、镜像打包->可给他人使用 docker save -o nginx.tar nginx:latest2-3、读取打包的镜像 记得先走第三步删除镜像&#x…

【Python】Windows环境下更改pip安装源

文章目录 1.前言2.pip临时安装更改源3.pip永久更改安装源3.1方法13.2方法2 1.前言 由于pip的默认的安装源在国外,导致我们在使用pip命令安装Python 库或包时速度特别慢,因此我们可以临时使用国内的源进行下载,或者直接更改pip的下载源 2.pip临时安装更改源 pip install xxx …

C#多态,Override和New的用法

一. 面向对象重要特性之多态 要掌握C#的Override和New关键字的用法&#xff0c;首先要理解多态&#xff1b;这里不赘述各种官方对多态的解释&#xff0c;下面给出个人直白理解&#xff1a; 父类F中声明一个方法M并用virtual修饰其为虚方法&#xff0c;子类S实现了相同签名的方…

MyCat分库分表

本章重点 mycat分表分库技术&#xff08;横向数据切分&#xff09; mycat数据切分规则&#xff08;取余分库&#xff0c;自然月分库&#xff09; mycat全局序列号&#xff08;实现mysql集群主键ID全局自增&#xff09; 一、分库分表 简单来说&#xff0c;就是指通过某种特…

“Interface 和 Type 区别”深度解析

“Interface 和 Type 区别”深度解析 文章目录 一、Interface 和 Type 是什么二、如何使用 Interface 和 Type1. 定义 Interface2. 定义 Type3. 使用 Interface 和 Type4. 区别与联系 三、Interface 和 Type 二者有哪些区别&#xff0c;分别在哪些场景使用1. 区别2. 场景 四、扩…

本地搭建 Whisper 语音识别模型

Whisper 是由 OpenAI 开发的一款强大的语音识别模型&#xff0c;具有出色的多语言处理能力。搭建和使用 Whisper 模型可以帮助您将音频内容转换为文本&#xff0c;这在语音转写、语音助手、字幕生成等应用中都具有广泛的用途。本指南将对如何在本地环境中搭建 Whisper 语音识别…

深入解析反射型 XSS 与存储型 XSS:原理、危害与防范

在网络安全领域&#xff0c;跨站脚本攻击&#xff08;XSS&#xff09;是一种常见的安全漏洞。XSS 攻击可以分为反射型 XSS 和存储型 XSS 两种类型。本文将详细介绍这两种类型的 XSS 攻击的原理、危害和防范措施。 一、反射型 XSS 1、原理 反射型 XSS 攻击也称为非持久性 XSS …

数据丢失要怎么处理,助你一键恢复数据

你平常会用优盘来传输资料吗&#xff1f;如果你也出现过优盘因为病毒或者误操作等原因引起了数据丢失的情况那就继续往下看吧。这篇文章带你了解u盘格式化后数据能恢复吗&#xff0c;带你了解可操作的工具。 1.福昕恢复数据 链接直达&#xff1a;https://www.pdf365.cn/foxit…

集成电路学习:什么是RTOS实时操作系统

RTOS&#xff1a;实时操作系统 RTOS&#xff0c;全称Real Time Operating System&#xff0c;即实时操作系统&#xff0c;是一种专为满足实时控制需求而设计的操作系统。它能够在外部事件或数据产生时&#xff0c;以足够快的速度进行处理&#xff0c;并在规定的时间内控制生产过…

2024国赛数学建模-模拟火算法(MATLAB 实现)

模拟退火算法 1.1 算法原理 模拟退火算法的基本思想是从一给定解开始 ,从邻域 中随机产生另一个解 ,接受 Metropolis准则允许目标函数在 有限范围内变坏 ,它由一控制参数 t决定 ,其作用类似于物 理过程中的温度 T,对于控制参数的每一取值 ,算法持续进 行“产生 —判断 —接受…

Mybatis中ORB映射

目录 1 MyBatis自动ORM失效 2 方案一&#xff1a;列的别名 3 方案二&#xff1a;结果映射&#xff08;ResultMap - 查询结果的封装规则&#xff09; 总结 1 MyBatis自动ORM失效 MyBatis只能自动维护库表”列名“与”属性名“相同时的一一对应关系&#xff0c;二者不同时&am…

已解决 AndroidRuntime java.lang.AbstractMethodError报错

现象 不混淆不报错&#xff0c;混淆后报这个错误 AndroidRuntime java.lang.AbstractMethodError 修复方式&#xff1a; step1 添加混淆规则&#xff0c;使其豁免混淆 step2 报错位置如果使用的是lambda表达式&#xff0c;还原为原来的写法&#xff0c;不用lambda表达式写。 通…

vscode中暂存块功能不能用了

vscode中暂存文件修改可以按每一处暂存&#xff0c;而不用一次暂存整个文件的修改&#xff0c;今天发现这个功能不能用了&#xff0c;不知道啥原因&#xff0c;记录一下。

Android 存储之 SharedPreferences 框架体系编码模板

一、SharedPreferences 框架体系 1、SharedPreferences 基本介绍 SharedPreferences 是 Android 的一个轻量级存储工具&#xff0c;它采用 key - value 的键值对方式进行存储 它允许保存和读取应用中的基本数据类型&#xff0c;例如&#xff0c;String、int、float、boolean …

JavaWeb案例

环境搭建 先创建好数据库&#xff0c;建表并插入数据 create database talis; use talis;-- 部门管理 create table dept(id int unsigned primary key auto_increment comment 主键ID,name varchar(10) not null unique comment 部门名称,create_time datetime not null com…

MISRA C2012学习笔记(8)-Rules 8.13

文章目录 8.13 副作用(Side effects)Rule 13.1 初始化程序列表不得包含持久性副作用Rule 13.2 在所有合法的评估命令下&#xff0c;表达式的值应与其持续的副作用相同Rule 13.3 包含自增()或自减(--)运算符的完整表达式&#xff0c;除由自增或自减运算符引起的副作用外&#xf…

QT QGraphicsView实现预览图片显示缩略图功能

QT QGraphicsView实现预览图片显示缩略图功能QT creator Qt5.15.2 头文件&#xff1a; #ifndef TGRAPHICSVIEW_H #define TGRAPHICSVIEW_H#include <QGraphicsView> #include <QMainWindow> #include <QObject> #include <QWidget>class TGraphicsVie…

TCP的传输速度

如何确定TCP最大传输速度&#xff1f; TCP 的传输速度&#xff0c;受限于发送窗⼝&#xff0c;接收窗⼝以及⽹络设备传输能⼒。 其中&#xff0c;窗⼝⼤⼩由内核缓冲区⼤⼩决定。如果缓冲区与⽹络传输能⼒匹配&#xff0c;那么缓冲区的利⽤率就达到了最⼤化。 如何计算网络传…

vue transition组件

可能不生效的几个注意点 选择器的优先级谨慎合并样式 显示三阶段和隐藏三阶段的class名 1、vue2中显示的初始阶段类名是&#xff1a;v-enter&#xff1b;隐藏的初始阶段类名是&#xff1a;v-leave2、v-enter-active、v-leave-active这两个 class 可以被用来定义动画的持续时间…

设计模式1:C#开发中使用创建型的工厂模式和行为型的策略模式

一、接口设计的好处 三大好处&#xff1a;解耦、可复用、可扩展。 二、简单工厂模式 【三要素】能创建具体产品的工厂、抽象产品&#xff08;接口&#xff09;、具体产品 【基本用法】字符串>创建对象>调用其方法 // 产品接口 public interface IProduct {void Opera…