current并发包

news2024/12/29 7:43:19

并发包

current并发包、在JDK1.5之前Java并没有提供线程安全的一些工具类去操作多线程,需要开发人员自行编写实现线程安全,但仍然无法完全避免低性能、死锁、资源管理等问题。在JDK1.5时新增了java.util.current并发包,其中提供了许多供我们使用的并发编程工具类。本文对于典型的并发包做出讲解

ConcurrentHashMap

Java集合框架提供了存储容器HashMap用于存储键值对,但是HashMap是线程不安全的。在并发编程中,我们向HashMap添加大量数据时,可能会出现各种预料之外的问题。

同时Java也提供了线程安全的集合类HashTable,打开HashTable的底层我们会发现HashTable的所有方法都利用synchtonized进行了上锁机制来保证了线程安全,但是利用这种阻塞同步的机制来保证线程安全的同时会大大降低程序的性能和执行效率,这也是为什么HashTable被淘汰的原因

在JDK1.5之后Java就提供了保证性能高效、线程安全的键值对存储容器ConcurrentHashMap

下面我们看下HashMap、HashTable、ConcurrentHashMap的对比

public class Demo01 {
    //public static Map<String,String> maps = new HashMap<String, String>();
    //public static Map<String,String> maps = new Hashtable<String, String>();
    public static Map<String,String> maps = new ConcurrentHashMap<String, String>();
    public static void main(String[] args) throws Exception {
        Runnable task = new Temp();
        Thread t1 = new Thread(task,"A线程");
        Thread t2 = new Thread(task,"B线程");
        t1.start();
        t2.start();
        // 保证t1和t2先执行完
        t1.join();
        t2.join();
        System.out.println("最终集合长度:"+maps.size());
    }
}
class Temp implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 500000; i++) {
 Demo01.maps.put(Thread.currentThread().getName()+i,Thread.currentThread().getName()+i);
        }
    }
}

如上述代码所示,我们启动两条线程执行同一任务:向容器中添加50万条数据,预期最终容器中的数据将会达到100万条。

利用HashMap存储时,发现程序会出现各种各样的异常状况

  1. 程序卡顿,不报异常也不停止

  2. 报异常

    java.lang.ClassCastException: java.util.HashMap$Node cannot be cast to java.util.HashMap$TreeNode
    
  3. 最终产生错误数据

利用HashTable存储时,发现HashTable可以准确存储。并且对比HashTable和ConcurrentHashMap两者的存储速度,发现大差小不差甚至HashTable还要更快。那么为什么还要说HashTable效率低下呢?

是因为我们只是测试了对数据进行的写操作,而没有测试其他的像查询、修改等操作。综合来讲ConcurrentHashMap的各项性能优于HashTbale,所以我们在需要考虑线程安全时,就可以采用ConcurrentHashMap进行存储数据

那么ConcurrentHashMap是如何既保证线程安全又不失高性能的存储数据呢?

首先明确它的底层实现机制是用CAS机制+synchronized分段式锁,属于是悲观和乐观相结合

HashTable工作时会将整个哈希表进行上锁,此时所有其他线程都将被阻塞,效率低下

在这里插入图片描述

ConcurrentHashMap工作时利用synchronized进行分段式上锁,我们知道哈希表底层基于数组实现,数组中每个位置形成槽位以便后续成链或者转换树结构。而分段式上锁就是将当前线程所存储的该位置进行上锁,其他位置仍可以被其他线程进行操作。

在这里插入图片描述

CountDownLatch倒计数触发

CountDownLatch同样是current包下的一个同步工具,它的主要作用就是使当前线程等待一条或多条线程执行完毕后再执行当前线程。同时提供了两个主要方法来控制线程的交替执行

// 创建CountDownLatch
CountDownLatch cdl = new CountDownLatch(1);
cdl.await()// 让出cpu,使当前线程等待
cdl.CountDown() // 计数器减1,只有当计数器为零时才会唤醒被await的线程

CountDownLatch提供了一个构造器用于参数Count,在创建时就给定计数个数。每次调用CountDown方法就减一知道减为0时才会执行被await等待的线程。

我们来看下面这个示例,目的是顺序打印出“A、B、C”

public class Demo02 {
    public static void main(String[] args) {
        CountDownLatch count = new CountDownLatch(1);
        new ThreadA(count).start();
        new ThreadB(count).start();
    }
}
class ThreadA extends Thread{
    private CountDownLatch count;
    public ThreadA(CountDownLatch count) {
        this.count = count;
    }
    @Override
    public void run() {
        System.out.println("A");
        // 使当前线程等待  等待打印B之后宰继续执行打印A
        try {
            count.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("C");
    }
}
class ThreadB extends Thread{
    private CountDownLatch count;
    public ThreadB(CountDownLatch count) {
        this.count = count;
    }
    @Override
    public void run() {
        System.out.println("B");
        // 当前线程执行完后倒计数减一
        count.countDown();
    }
}

但是有序线程执行先后 顺序不确定,也有可能打印出“B、A、C”

CyclicBarrier循环屏障

CyclicBarrier与CountDownLatch很容易弄混

CountDownLatch:使一条或多条线程等待其他线程执行完毕之后再执行自己,内部使用倒计数,最终执行被await等待的线程

CyclicBarrier:阻塞一个线程组,内部采用正计数。当被阻塞的线程达到某个数量时才能执行指定的任务。我们每调用一次await代表阻塞了一条线程。

假设示例:五个人进入会议室执行开会任务

// 六条线程:五个员工进入会议室、一个开会
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        // 创建循环屏障
        CyclicBarrier cb = new CyclicBarrier(5,new Metting());
        for (int i = 1; i <= 4; i++) {
            new Employee(i+"号员工",cb).start();
        }
    }
}
class Employee extends Thread{
    private CyclicBarrier cb;
    public Employee(String s, CyclicBarrier cb) {
        super(s);
        this.cb = cb;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"进入会议室");

        try {
            Thread.sleep(1000);
            cb.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
class Metting implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"组织会议,会议开始");
    }
}

上述代码所示:

CyclicBarrier cb = new CyclicBarrier(5,new Metting());

我们创建了一个循环屏障用于控制线程执行,当被await阻塞的线程数==5时将会执行newMetting的Runnable线程任务

同时会发现最后一个到达会议室的人(线程)将会组织会议开始,这说明我们调用了await方法并不是将该线程阻塞。是由于CyclicBarrier底层由线程池实现,每一条线程执行完毕之后都会被线程池回收而不是阻塞

Semaphore指示灯

Semaphore用于设置一个或多个线程可以同时执行即控制线程的并发数量,其他线程被阻塞。常用于限流操作。同时可以设置公平锁和非公平锁

Semaphore的使用与Lock工具有些类似,同样是提供了两个方法用于上锁和解锁。只是Semaphore可以自由的控制能拿到锁的线程数

Semaphore提供了如下两个构造器

public Semaphore(int permits) // permits为允许执行的线程数
public Semaphore(int permits, boolean fair)
    // fair为true表示公平锁,等待时间最长的线程将在下次进入 反之是不公平锁

Semphore提供的两个操作锁方法

public void acquire()  // 表示获得许可
public void release()  // 表示释放许可

示例:

public class SemaphoreDemo {
    public static void main(String[] args) {
        // 创建任务
        Service service = new Service();
        for (int i = 1; i <= 5; i++) {
            new MyThread(i+"号线程",service).start();
        }
    }
}
// 线程类
class MyThread extends Thread{
    private Service service;
    public MyThread(String name,Service service){
        super(name);
        this.service = service;
    }
    @Override
    public void run() {
        try {
            service.testMethod();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}
// 抽离业务代码
class Service{
    // 创建Semaphore对象 并指定线程数
    private Semaphore sp = new Semaphore(2);
    public void testMethod() throws Exception {
        // 获取许可
        sp.acquire();
        System.out.println(Thread.currentThread().getName()+"进入  时间:"+System.currentTimeMillis());
        Thread.sleep(200);
        System.out.println(Thread.currentThread().getName()+"执行成功");
        System.out.println(Thread.currentThread().getName()+"离开  时间:"+System.currentTimeMillis());
        // 释放许可
        sp.release();
    }
}

如上述程序所示,我们在创建Semaphore时指定了允许的并发数量为2,那么业务代码同时只能被两个线程执行,一旦一条线程执行完毕之后将会释放许可,立刻会有其他线程获得许可进入执行

Exchanger交换者

Exchanger用于线程间的通信、数据交换。Exchanger提供了一个同步点exchange方法:public V exchange(V x)互相交换数据的两条线程必须都运行到了同步点才能执行交换数据的操作,只有一方到达时就会进行等待,等待时间可以由开发人员设定

我们先来看下面的示例

public class ExchangerDemo {
    public static void main(String[] args) {
        // 创建交换者
        Exchanger<String> exchanger = new Exchanger<>();
        // 创建两条线程进行交换数据
        new ThreadN("线程N",exchanger).start();
        new ThreadP("线程P",exchanger).start();
    }
}
class ThreadN extends Thread{
    private Exchanger<String> exchanger;
    public ThreadN(String name,Exchanger<String> exchanger) {
        super(name);
        this.exchanger = exchanger;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"给线程P:"+"我是线程N");
        try {
            String exchange = exchanger.exchange("我是线程N");
            System.out.println("线程N拿到数据:"+exchange);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
class ThreadP extends Thread{
    private Exchanger<String> exchanger;
    public ThreadP(String name,Exchanger<String> exchanger) {
        super(name);
        this.exchanger = exchanger;
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"给线程N:"+"我是线程P");
        try {
            String exchange = exchanger.exchange("我是线程P");
            System.out.println("线程P拿到数据:"+exchange);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

根据最终打印,可以发现两者交换了数据。这两条线程拥有的是同一个交换者对象,所以可以实现数据交换。

前文提到过我们可以自定义线程等待的时间,就是再同步点exchange处等待另一条线程执行到此的时间。利用exchange方法定义等待时间

public V exchange(V x, long timeout, TimeUnit unit)
    // timeout等待的时间数值  unit时间单位
    // 示例:只等待五秒
exchanger.exchange("111","5000", TimeUnit.SECONDS)

超出了规定的等待时间,正在等待的线程将被回收并抛出java.util.TimeoutException超时异常,所以交换数据的双方必须都执行到同步点才能进行数据交换

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

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

相关文章

【自学Python】Python截取字符串

Python截取字符串 Python截取字符串教程 在 Python 中&#xff0c;我们需要截取 字符串&#xff0c;不需要使用特定的 函数&#xff0c;只需要使用下标索引加上切片的形式&#xff0c;就可以实现字符串的截取。 Python字符 Python 中没有单个字符的概念&#xff0c;单个字符…

uni-app 微信小程序通过Vue3 Hooks 实现动态填充页面剩余高度

应用场景 在uni-app开发微信小程序等项目时&#xff0c;经常会遇到这样的页面布局需求&#xff1a;上半部分高度固定&#xff0c;下半部分自动占满剩余高度&#xff0c;如下图所示应用场景&#xff1a;上半部分为固定高度或内容填充高度的内容区域下半部分为scroll-view滑动区…

河北稳控科技振弦采集模块配置工具VMTool的常见功能

河北稳控科技振弦采集模块配置工具VMTool的常见功能 一、实时数据读取 当 VMTool 与模块为连接状态时&#xff08; 4.3.1 模块的连接与断开&#xff09;&#xff0c; 勾选实时数据区的【 自动读取】 复选框&#xff0c; VMTool 开始自动向模块发送实时数据读取指令&#xff0c…

如何用 Java 来构建一个简单的速率限制器?

速率限制 现实世界中的用户是残暴的&#xff0c;并且没耐心&#xff0c;充满着各种不确定性。在高并发系统中&#xff0c;可能会出现服务器被虚假请求轰炸的情况&#xff0c;因此您可能希望控制这种情况。 一些实际使用情形可能如下所示&#xff1a; API配额管理-作为提供者…

28.函数指针变量作为函数的参数,容易混淆的指针概念,特殊指针,main函数传参

1.函数指针变量作为函数的参数 #include<stdio.h> int add(int x, int y) {return x y; } int sub(int x, int y) {return x - y; } int mux(int x, int y) {return x * y; } int dive(int x, int y) {return x / y; } int process(int(*p)(int, int),int x,int y) {in…

Redis基础命令操作三之集合类型SET

SET集合 特点&#xff1a;集合中存储的元素是惟一的。 命令举例说明SADD sadd [key] [value1 value2 value3]key对应的集合中添加元素SMEMBERSsmembers [key]获取key对应的集合的所有元素SISMEMBERsismember [key] [value]判断value是否在key对应的集合中存在SCARDscard [key…

excel日期函数:EDATE与DATE到底谁更胜一筹

平时的工作中&#xff0c;经常会遇到计算职工转正日期、合同到期日、职工退休日期以及产品有效期截止日等等与日期有关系的问题。这些问题看似复杂&#xff0c;实际上只需要一个很简单的函数就能搞定&#xff0c;这个函数就是EDATE。今天分享EDATE函数的几个应用实例&#xff0…

【Linux】Linux 权限和权限管理

文章目录Linux权限的概念Linux权限管理文件访问者的分类&#xff08;人&#xff09;文件类型和访问权限&#xff08;事物属性)目录权限默认权限粘滞位关于权限的总结Linux权限的概念 权限是用来限制人的&#xff0c;权限人事物属性 权限存在的意义是便于系统安全管理的 Linux下…

Python处理第一类切比雪夫多项式

第一类切比雪夫多项式简介 Chebyshev多项式是一种非常重要的正交多项式&#xff0c;在逼近理论中有重要应用&#xff0c;第一类切比雪夫多项式的根可用于多项式插值&#xff0c;对弥补龙格现象有很大的帮助。其表达形式为 Tncos⁡(narccos⁡x)T_n\cos(n\arccos x) Tn​cos(nar…

vite+vue3+Ts搭建基础项目框架

随着前端技术的更新&#xff0c;程序员们的技术栈也要不断跟上&#xff0c;本来想躺平&#xff0c;不料却被推着走。 上个月开发团队新来一个项目需求&#xff0c;要求开发技术栈vue2更换成vue3&#xff0c;毫无准备的小编一脸懵&#xff0c;嗯&#xff1f;怎么说 换就换了&am…

读写分离有哪些坑?

在上一篇文章中,我和你介绍了一主多从的结构以及切换流程。今天我们就继续聊聊一主多从架构的应用场景:读写分离,以及怎么处理主备延迟导致的读写分离问题。 我们在上一篇文章中提到的一主多从的结构,其实就是读写分离的基本结构了。这里,我再把这张图贴过来,方便你理解…

【Linux】Linux环境下如何实现自动化编译——make/makefile入门

文章目录 前言 一、make/Makefile 的使用 1.示例 2.编写 Makefile 文件 2.1生成 2.2清理 二、make 如何知道生成的可执行文件是否最新 前言 在Linux 环境下编写好C语言代码之后&#xff0c;我们需要使用编译工具gcc 将其翻译为可执行文件。可是&#xff0c;如果对代码进…

Apache Spark 机器学习 特征抽取 4-1

特征数据集是用于在机器学习中进行训练&#xff0c;有关特征的算法的分类如下所示&#xff1a; 抽取&#xff08;Extraction&#xff09;&#xff0c;从原始数据集中提取出对应的特征集 转换&#xff08;Transformation&#xff09;&#xff0c;缩放特征、转换特征以及修改特征…

线程池(关于变量捕获、线程数、针对ThreadPoolExecutor的构造方法参数的解释、自实现线程池)

目录&#xff1a;一、前言二、关于变量捕获三、针对ThreadPoolExecutor的构造方法参数的解释四、自实现线程池一、前言相比较于进程&#xff0c;创建线程 / 销毁线程 的开销是相对较小的&#xff0c;但是太过频繁的创建线程 / 销毁线程&#xff0c;其开销也很大。这时候我们就需…

C 语言编译链接

前言 一个 C 程序究竟是怎么变成可执行程序的&#xff0c;这其间发生了什么&#xff1f;本文将带你简要了解 C 程序编译过程&#xff0c;文章为 《程序员的自我修养—链接、装载与库》的读书笔记&#xff0c;更为详细的过程可以阅读原书。 比如下面一个经典的 C 程序&#xf…

百度飞浆在pycharm中的使用(含官网安装和cuda)

uieGitHub 安装cuda 1 获取版本 我的是 CUDA Toolkit 11.7.1 (August 2022), Versioned Online Documentation 为了防止后期版本不对应&#xff0c;我这里小心谨慎安装了August对应的月份。 C:\Users\89735>nvidia-smi Mon Dec 19 21:31:28 2022 ------------------------…

一眼万年,这3款顶级神软,内存满了也绝不卸载

免费软件都不好用&#xff1f;不&#xff01;下面3款良心软件&#xff0c;颠覆你的认知&#xff0c;功能强大到离谱&#xff0c;值得收藏往后有需要直接使用。 1、桌面运维助手 这是一款堪称神器的国产电脑优化工具&#xff0c;集硬件管理、系统管理、辅助工具于一体&#xff0…

Effective C++条款39:明智而审慎地使用private继承(Use private inheritance judiciously)

Effective C条款39&#xff1a;明智而审慎地使用private继承&#xff08;Use private inheritance judiciously&#xff09;条款39&#xff1a;明智而审慎地使用private继承1、private 继承2、在private继承和复合之间做出正确选择3、使用private继承比组合更加合理的例子4、牢…

wsl安装CUDA

NVCC 昨天已经安装好了gpu版的pytorch&#xff0c;对于一般的代码应该就可以运行了。但有些代码中需要用到cuda算子&#xff0c;需要配置nvcc环境。对于这个我也没能搞太清楚&#xff0c;网上的说法不一&#xff0c;我使用conda安装pytorch时也安装了cudatoolkit&#xff0c;按…

c++11 标准模板(STL)(std::forward_list)(八)

定义于头文件 <forward_list> template< class T, class Allocator std::allocator<T> > class forward_list;(1)(C11 起)namespace pmr { template <class T> using forward_list std::forward_list<T, std::pmr::polymorphic_…