Java 多线程补充

线程池

Java线程池是一种能够有效管理线程资源的机制,它可以显著提高应用性能并降低资源消耗

线程池的主要优点包括:

  1. 资源利用高效:通过重用已存在的线程,减少了频繁创建和销毁线程带来的系统开销。
  2. 响应速度提升:任务到来时可以迅速被执行,而不必等待新线程的创建。
  3. 管理监控便捷:线程数量有限,避免无限制创建线程导致的资源耗尽和系统不稳定问题,同时便于统一分配、调优和监控。
  4. 功能丰富强大:提供了多种类型的线程池,如定时、定期以及可控线程数的线程池,满足不同的业务需求。

在使用线程池时,需要注意以下几点:

  1. 合理配置参数:创建线程池时需要根据实际需求合理设置线程数量等参数,以避免资源浪费或系统过载。
  2. 预防潜在风险:使用Executors类中的便捷方法虽然简单,但可能会隐藏复杂性,如不当使用可能导致内存溢出(OOM)或线程耗尽等问题。
  3. 选择正确任务类型:理解RunnableCallable接口的区别,并根据任务的特性选择合适的类型提交给线程池执行。

Executors

Java Executors是一个用于创建线程池的工厂类,它提供了一系列的静态工厂方法来简化线程池的创建和管理

Executors类中提供的方法包括:

  • newCachedThreadPool():创建一个可缓存的线程池,适用于执行大量的短期异步任务。线程数量可以根据需要自动扩展,如果有可用的空闲线程,就会重用它们;如果没有可用的线程,就会创建一个新线程。
  • newFixedThreadPool(int nThreads):创建一个固定大小的线程池,适用于执行固定数量的长期任务。线程数量是固定的,不会自动扩展。
  • newSingleThreadExecutor():创建一个单线程的线程池,适用于需要按顺序执行任务的场景。
  • newScheduledThreadPool(int corePoolSize):创建一个固定大小的线程池,用于定时执行任务。线程数量固定,不会自动扩展。

使用Executors的优点包括:

  • 简化线程管理:Executors通过提供工厂方法,隐藏了线程池的复杂性,使得线程池的创建变得简单快捷。
  • 适应不同场景:根据不同的业务需求,可以选择不同类型的线程池,如固定大小、单线程或定时执行等。
  • 提高性能:通过复用线程,降低了资源消耗,提高了系统的响应速度和吞吐量。

以下是Java Executors的代码实现示例:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorsExample {
    public static void main(String[] args) {
        // 创建一个可缓存的线程池
        ExecutorService executorService = Executors.newCachedThreadPool();

        // 提交任务到线程池
        for (int i = 0; i < 10; i++) {
            executorService.execute(new Task());
        }

        // 关闭线程池
        executorService.shutdown();
    }

    static class Task implements Runnable {
        @Override
        public void run() {
            System.out.println("Task executed by thread: " + Thread.currentThread().getName());
        }
    }
}

在这个例子中,我们使用Executors.newCachedThreadPool()方法创建了一个可缓存的线程池。然后,我们通过循环提交了10个任务到线程池中执行。每个任务都是一个实现了Runnable接口的Task类的实例。最后,我们调用executorService.shutdown()方法关闭线程池。

 ThreadLocal

Java中的ThreadLocal是一个用于存储线程局部变量的类,它允许每个线程拥有自己的独立变量副本,从而实现线程间的数据隔离

ThreadLocal的主要作用是解决多线程环境下的数据安全问题,避免多个线程同时访问共享变量时出现数据不一致的情况。通过使用ThreadLocal,每个线程都可以在本地存储自己的私有数据,而不会影响其他线程的数据。

以下是ThreadLocal的一些主要特点:

  1. 线程安全:ThreadLocal为每个线程提供了独立的变量副本,避免了多线程之间的数据竞争和同步问题。
  2. 高效性:相比于使用同步机制来保护共享变量,ThreadLocal可以提供更高的性能,因为它避免了锁的使用和线程阻塞。
  3. 内存泄漏风险:由于ThreadLocal的生命周期与线程相同,如果不及时清理ThreadLocal中的数据,可能会导致内存泄漏。因此,在使用完ThreadLocal后,需要手动调用remove()方法来清除数据。
  4. 可扩展性:ThreadLocal可以通过继承或实现自定义的ThreadLocal子类来扩展其功能,以满足特定的业务需求。
  5. 适用场景:ThreadLocal适用于需要在多线程环境下保持线程间数据隔离的场景,例如数据库连接池、会话管理等。

下面是一个简单的示例代码,演示了如何使用ThreadLocal来存储和获取线程局部变量:

public class ThreadLocalExample {
    private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        // 设置线程局部变量的值
        threadLocal.set("Hello, World!");

        // 获取线程局部变量的值
        String value = threadLocal.get();
        System.out.println(value); // 输出: Hello, World!

        // 清除线程局部变量的值
        threadLocal.remove();
    }
}

在这个例子中,我们创建了一个名为threadLocal的ThreadLocal对象,并使用set()方法设置了线程局部变量的值。然后,我们使用get()方法获取了该值,并将其打印出来。最后,我们调用remove()方法清除了线程局部变量的值,以避免内存泄漏。

原子性

Java中的原子性是指一个操作或者一系列操作要么全部执行成功,要么全部失败,且在执行过程中不会被其他线程打断

原子性是并发编程中的一个重要概念,它保证了线程安全,即在一个线程执行操作时,不受其他线程的干扰。以下是保证原子性的几种方法:

  1. 使用synchronized关键字:通过同步代码块或同步方法来确保在同一时刻只有一个线程能够访问共享资源。
  2. 使用volatile关键字:虽然volatile不能保证复合操作的原子性,但它可以保证单个共享变量的读写操作是原子性的,并且能够保证变量的可见性。
  3. 使用Atomic类:Java提供了一系列的Atomic类(如AtomicInteger、AtomicLong等),它们使用非阻塞算法来实现对单个变量的原子操作。
  4. 使用Lock接口及其实现类:如ReentrantLock,它们提供了比synchronized更灵活的锁定机制,可以控制锁的获取和释放。
  5. 使用CAS操作:比较并交换(Compare-and-Swap)是一种无锁技术,用于实现高效的并发控制。
  6. 使用线程安全的数据结构:如ConcurrentHashMap、CopyOnWriteArrayList等,这些数据结构内部实现了必要的同步措施,以保证并发访问时的线程安全。
  7. 使用信号量、倒计时门闩等同步辅助工具:这些工具可以帮助控制并发线程的执行顺序和数量,从而保证操作的原子性。

以下是Java中保证原子性的代码示例:

  • 使用synchronized关键字保证原子性:
public class AtomicityExample {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}
  • 使用volatile关键字保证单个共享变量的原子性:
public class AtomicityExample {
    private volatile int count = 0;

    public void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}
  • 使用AtomicInteger保证单个共享变量的原子性:
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicityExample {
    private AtomicInteger count = new AtomicInteger(0);

    public void increment() {
        count.incrementAndGet();
    }

    public int getCount() {
        return count.get();
    }
}
  • 使用Lock接口及其实现类ReentrantLock保证原子性:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class AtomicityExample {
    private int count = 0;
    private Lock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        return count;
    }
}
  • 使用CAS操作保证原子性:
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicityExample {
    private AtomicInteger count = new AtomicInteger(0);

    public void increment() {
        int oldValue, newValue;
        do {
            oldValue = count.get();
            newValue = oldValue + 1;
        } while (!count.compareAndSet(oldValue, newValue));
    }

    public int getCount() {
        return count.get();
    }
}

并发工具类-Hashtable

Hashtable是Java集合框架中的一部分,它实现了Map接口,并且支持同步

Hashtable在Java中是一个较早出现的集合类,它提供了一种存储键值对的方式,这些键值对被称为条目,它们存储在一个哈希表中。以下是关于Hashtable的一些详细信息:

  • 实现和接口:Hashtable是java.util包的一部分,并且它是Dictionary接口的具体实现。自Java 2以来,Hashtable还实现了Map接口,这意味着它可以被当作一个映射来使用。
  • 线程安全:与HashMap不同,Hashtable是线程安全的,因为它支持同步。这允许多个线程同时访问Hashtable而不会发生并发问题。
  • 存储方式:Hashtable通过计算对象的哈希码来确定其在内部数组中的存储位置。当两个对象具有相同的哈希码时,它们会被存储在同一个索引位置,形成一个链表。
  • 基本操作:Hashtable提供了常见的操作方法,如put()用于添加或更新键值对,get()用于根据键获取值,remove()用于删除键值对等。
  • 性能考虑:由于Hashtable的同步特性,它在单线程环境下的性能可能不如非同步的HashMap。在高并发场景下,如果对性能有较高要求,可以考虑使用ConcurrentHashMap作为替代方案。
  • 使用示例:Hashtable的使用非常简单,可以像下面这样创建一个Hashtable实例,并向其中添加元素:
Hashtable<Integer, String> hashtable = new Hashtable<>();
hashtable.put(1, "aa");
hashtable.put(4, "dd");
hashtable.put(2, "bb");
hashtable.put(3, "cc");
System.out.println(hashtable);

需要注意的是,虽然Hashtable是线程安全的,但在迭代时仍需手动同步以保证一致性。此外,Hashtable不允许键或值为null,这一点在使用时应特别注意。

并发工具类-CountDownLatch

CountDownLatch是Java并发编程中的一个同步辅助类,它允许一个或多个线程等待直到其他线程完成操作。

以下是CountDownLatch的一些主要特点和使用场景:

基本概念:CountDownLatch通过一个计数器来实现线程之间的同步。计数器的初始值由用户设置,每次调用countDown()方法会使计数器的值减一,当计数器的值减至零时,所有因调用await()方法而在等待的线程被唤醒。

应用场景:CountDownLatch通常用于以下场景:

  • 等待其他线程完成任务后再执行:例如,主线程需要等待其他线程完成初始化操作后才能继续执行。
  • 实现多个线程之间的同步:例如,确保所有线程都准备好后再同时开始执行。

使用示例

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        int threads = 5;
        CountDownLatch latch = new CountDownLatch(threads);

        for (int i = 0; i < threads; i++) {
            new Thread(new Worker(latch)).start();
        }

        latch.await(); // 主线程等待其他线程完成任务
        System.out.println("所有线程任务完成,主线程继续执行...");
    }
}

class Worker implements Runnable {
    private final CountDownLatch latch;

    Worker(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void run() {
        try {
            // 模拟耗时操作
            Thread.sleep((long) (Math.random() * 1000));
            System.out.println(Thread.currentThread().getName() + " 任务完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            latch.countDown(); // 完成任务后,计数器减一
        }
    }
}

注意事项

  • CountDownLatch不能重置计数器,一旦计数器减至零,就不能再被使用。如果需要重复使用,需要创建新的CountDownLatch实例。
  • 在调用countDown()await()方法时可能会抛出InterruptedException,因此需要进行适当的异常处理

并发工具类-Semaphore

Java Semaphore是Java并发编程中的一个同步辅助类,它允许多个线程访问同一资源,但限制同时访问的线程数量。

以下是Semaphore的一些主要特点和使用场景:

基本概念:Semaphore通过一个计数器来实现线程之间的同步。计数器的初始值由用户设置,每次调用acquire()方法会使计数器的值减一,当计数器的值减至零时,其他尝试获取许可的线程将被阻塞。当线程释放许可时,调用release()方法会使计数器的值加一。

应用场景:Semaphore通常用于以下场景:

  • 限制同时访问资源的线程数量:例如,限制同时访问数据库连接的线程数量,以避免过多的连接导致系统负载过高。
  • 实现信号量机制:例如,控制生产者和消费者之间的生产消费速度,确保生产者不会过度生产而使缓冲区溢出。

使用示例

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    public static void main(String[] args) {
        int permits = 5; // 允许同时访问资源的线程数量
        Semaphore semaphore = new Semaphore(permits);

        for (int i = 0; i < 10; i++) {
            new Thread(new Worker(semaphore)).start();
        }
    }
}

class Worker implements Runnable {
    private final Semaphore semaphore;

    Worker(Semaphore semaphore) {
        this.semaphore = semaphore;
    }

    @Override
    public void run() {
        try {
            semaphore.acquire(); // 获取许可
            // 执行任务
            System.out.println(Thread.currentThread().getName() + " 正在执行任务");
            Thread.sleep((long) (Math.random() * 1000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release(); // 释放许可
        }
    }
}

注意事项

  • Semaphore不能重置计数器,一旦计数器减至零,就不能再被使用。如果需要重复使用,需要创建新的Semaphore实例。
  • 在调用acquire()release()方法时可能会抛出InterruptedException,因此需要进行适当的异常处理。

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

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

相关文章

【Git】Github创建远程仓库并与本地互联

创建仓库 点击生成新的仓库 创建成功后会生成一个这样的文件 拉取到本地 首先先确保本地安装了git 可以通过终端使用 git --version来查看是否安装好了git 如果显示了版本信息&#xff0c;说明已经安装好了git&#xff0c;这时候我们就可以进入我们想要clone到问目标文件夹 …

计算机系列之算法分析与设计

21、算法分析与设计 算法是对特定问题求解步骤的一种描述。它是指令的有限序列&#xff0c;其中每一条指令标识一个或多个操作。 它具有有穷性、确定性&#xff08;含义确定、输入输出确定&#xff0c;相同输入相同输出&#xff1b;执行路径唯一&#xff09;、可行性、输入&a…

【SAP ME 38】SAP ME发布WebService配置及应用

更多WebService介绍请参照 【SAP ME 28】SAP ME创建开发组件&#xff08;DC&#xff09;webService 致此一个WebService应用发布成功&#xff0c;把wsdl文件提供到第三方系统调用接口&#xff01; 注意&#xff1a; 在SAP ME官方开发中默认对外开放的接口是WebService接口&am…

01、vue+openlayers6实现自定义测量功能(提供源码)

首先先封装一些openlayers的工具函数&#xff0c;如下所示&#xff1a; import VectorSource from ol/source/Vector; import VectorLayer from ol/layer/Vector; import Style from ol/style/Style; import Fill from ol/style/Fill; import Stroke from ol/style/Stroke; im…

Android GPU渲染SurfaceFlinger合成RenderThread的dequeueBuffer/queueBuffer与fence机制(2)

Android GPU渲染SurfaceFlinger合成RenderThread的dequeueBuffer/queueBuffer与fence机制&#xff08;2&#xff09; 计算fps帧率 用 adb shell dumpsys SurfaceFlinger --list 查询当前的SurfaceView&#xff0c;然后有好多行&#xff0c;再把要查询的行内容完整的传给 ad…

题目----力扣--移除链表元素

题目 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[1,2,3,4,5]示例 2&#xff1a; 输入&…

智慧公厕:让厕所管理变得更智慧、高效、舒适!

公共厕所是城市的重要组成部分&#xff0c;但常常被忽视。它们的管理和养护往往面临着许多问题&#xff0c;例如卫生状况不佳、环境畏畏缩缩、设施老旧等。为了解决这些问题&#xff0c;智慧公厕应运而生。智慧公厕是一种全方位的应用解决方案&#xff0c;将科技与公共厕所管理…

我在洛杉矶采访到了亚马逊云全球首席信息官CISO(L11)!

在本次洛杉矶举办的亚马逊云Re:Inforce全球安全大会中&#xff0c;小李哥作为亚马逊大中华区开发者社区和自媒体代表&#xff0c;跟着亚马逊云安全产品团队采访了亚马逊云首席信息安全官(CISO)CJ Moses、亚马逊副总裁Eric Brandwine和亚马逊云首席高级安全工程师Becky Weiss。 …

搜索的未来:OpenAI 的 GPT 如何彻底改变行业

搜索的未来&#xff1a;OpenAI 的 GPT 如何彻底改变行业 概述 搜索引擎格局正处于一场革命的风口浪尖&#xff0c;而 OpenAI 的 GPT 处于这场变革的最前沿。最近出现了一种被称为“im-good-gpt-2-chatbot”的神秘聊天机器人&#xff0c;以及基于 ChatGPT 的搜索引擎的传言&am…

【C++ 内存管理】深拷贝和浅拷贝你了解吗?

文章目录 1.深拷贝2.浅拷贝3.深拷贝和浅拷贝 1.深拷贝 &#x1f34e; 深拷⻉: 是对对象的完全独⽴复制&#xff0c;包括对象内部动态分配的资源。在深拷⻉中&#xff0c;不仅复制对象的值&#xff0c;还会复制对象所指向的堆上的数据。 特点&#xff1a; &#x1f427;① 复制对…

DCDC中MOS半桥的自举电容,自举电阻问题

一个免费的翻译英文文章的网站&#xff0c;可以将英文数据手册翻译为中文&#xff08;需要挂梯子&#xff0c;不收费&#xff0c;无广告&#xff0c;不需要注册&#xff09;&#xff0c;链接如下&#xff1a; Google 翻译 翻译效果&#xff1a; // 104电容是0.1uf&#xff1b…

Spring AOP(2)

目录 Spring AOP详解 PointCut 切面优先级Order 切点表达式 execution表达式 切点表达式示例 annotation 自定义注解MyAspect 切面类 添加自定义注解 Spring AOP详解 PointCut 上面代码存在一个问题, 就是对于excution(* com.example.demo.controller.*.*(..))的大量重…

Tomcat中服务启动失败,如何查看启动失败日志?

1. 查看 localhost.log 这个日志文件通常包含有关特定 web 应用的详细错误信息。运行以下命令查看 localhost.log 中的错误&#xff1a; sudo tail -n 100 /opt/tomcat/latest/logs/localhost.YYYY-MM-DD.log请替换 YYYY-MM-DD 为当前日期&#xff0c;或选择最近的日志文件日…

【notepad++】使用

1 notepad 下载路径 https://notepad-plus.en.softonic.com/download 2 设置护眼模式 . 设置——语言格式设置——前景色——黑色 . 背景色——RGB &#xff1a;199 237 204 . 勾选“使用全局背景色”、“使用全局前景色” . 保存并关闭

YOLOv5改进 | 注意力机制 | 理解全局和局部信息的SE注意力机制

在深度学习目标检测领域&#xff0c;YOLOv5成为了备受关注的模型之一。本文给大家带来的是能够理解全局和局部信息的SE注意力机制。文章在介绍主要的原理后&#xff0c;将手把手教学如何进行模块的代码添加和修改&#xff0c;并将修改后的完整代码放在文章的最后&#xff0c;方…

RAG查询改写方法概述

在RAG系统中&#xff0c;用户的查询是丰富多样的&#xff0c;可能存在措辞不准确和缺乏语义信息的问题。这导致使用原始的查询可能无法有效检索到目标文档。 因此&#xff0c;将用户查询的语义空间与文档的语义空间对齐至关重要&#xff0c;目前主要有查询改写和嵌入转换两种方…

代码随想录算法训练营第六十天| LeetCode647. 回文子串 、516.最长回文子序列

一、LeetCode647. 回文子串 题目链接/文章讲解/视频讲解&#xff1a;https://programmercarl.com/0647.%E5%9B%9E%E6%96%87%E5%AD%90%E4%B8%B2.html 状态&#xff1a;已解决 1.思路 这道题我只想出来了暴力解法&#xff0c;动规解法并没有想出来。根据视频讲解才把它想出来。…

【hackmyvm】 Animetronic靶机

靶机测试 arp-scanporturl枚举exiftool套中套passwordsudo 提权 arp-scan arp-scan 检测局域网中活动的主机 192.168.9.203 靶机IP地址port 通过nmap扫描&#xff0c;获取目标主机的端口信息 ┌──(root㉿kali)-[/usr/share/seclists] └─# nmap -sT -sV -O 192.16…

基于springboot+vue+Mysql的体质测试数据分析及可视化设计

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

C语言/数据结构——(相交链表)

一.前言 今天在力扣上刷到了一道题&#xff0c;想着和大家一起分享一下这道题——相交链表https://leetcode.cn/problems/intersection-of-two-linked-lists废话不多说&#xff0c;让我们开始今天的分享吧。 二.正文 1.1题目描述 是不是感觉好长&#xff0c;我也这么觉得。哈…
最新文章