首页 > 其他分享 >阻塞队列、线程池、原子性及并发工具类

阻塞队列、线程池、原子性及并发工具类

时间:2022-10-08 20:03:50浏览次数:54  
标签:队列 创建 int 线程 数组 arrayBlockingQueue 共享 性及


目录

​一、阻塞队列​

​二、线程池​

​静态方法创建线程池:​

​使用ThreadPoolexecutor类创建线程池:​

​三、原子性​

​四、并发工具类​

​HashTable类​

​ConcurrentHashMap类​

​CountDownLatch类​

​Semaphore类​


一、阻塞队列

ArrayBlockingQueue类:底层是数组,有界,没有无参构造方法

LinkedBlockingQueue类:底层是链表,无界但最多能存放int的最大值,无参构造方法默认容量就是最大值

常用方法:

put(Object o):将参数放入队列,如果放不进去会阻塞

take():取出第一个数据,取不到会阻塞

使用阻塞队列实现生产者消费者模式:

public class Test {
public static void main(String[] args) throws
InterruptedException {
ArrayBlockingQueue<String> arrayBlockingQueue = new
ArrayBlockingQueue<>(1);
Foodie foodie = new Foodie(arrayBlockingQueue);
Cooker cooker = new Cooker(arrayBlockingQueue);
foodie.start();
cooker.start();
}
}
class Foodie extends Thread{
private ArrayBlockingQueue<String> arrayBlockingQueue;
public Foodie(ArrayBlockingQueue<String> arrayBlockingQueue){
this.arrayBlockingQueue = arrayBlockingQueue;
}
@Override
public void run() {
while (true) {
try {
String take = arrayBlockingQueue.take();
System.out.println("生产者消费了一个"+take);} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Cooker extends Thread{
private ArrayBlockingQueue<String> arrayBlockingQueue;
public Cooker(ArrayBlockingQueue<String> arrayBlockingQueue){
this.arrayBlockingQueue = arrayBlockingQueue;
}
@Override
public void run() {
while (true) {
try {
arrayBlockingQueue.put("汉堡包");
System.out.println("生产者放了一个汉堡包");} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

二、线程池

每一个线程的启动和结束都是比较消耗时间和资源的,如果在系统中用到很多线程,大量的线程启动和结束操作会导致性能变卡,响应变慢,为了解决这个问题,引入了线程池的设计思想,线程池就是一种生产者消费者模式

主要思想是创建若干个线程放入池子,有任务需要处理时将任务提交到线程池中的任务队列,任务处理完后线程池并不会销毁,而是继续在线程池中等待下一个任务

静态方法创建线程池:

1、使用Executors类中的静态方法static ExecutorService newCachedThreadPool()创建线程池,默认线程池是空的,根据需要创建线程,超过60秒未被使用的线程则销毁,最多创建int最大值个线程

2、使用Executors类中的静态方法static ExecutorService newFixedThreadPool(int nThreads)创建线程池,默认线程池是空的,根据需要创建线程,参数表示线程池最多能够创建的线程,创建的线程将一直存到直到显式调用shutdown()方法

3、这两个方法返回值类型是ExecutorService接口,这个接口里边定义了操作线程的方法,常用的两个方法是:

submit(task):task是需要执行的任务,可以是实现Runnable接口或Callable接口的类对象,也可以是Lambda表达式

shutdown():用于任务执行后关闭线程池

使用ThreadPoolexecutor类创建线程池:

1、上述使用静态方法创建的线程池实际上是使用类该类来创建并返回线程池

2、常用构造方法:

public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler
){}

corePoolSize :核心线程数量

maximumPoolSize :最大线程数量

keepAliveTime :空闲线程存活时间的值

unit :存活时间的单位

workQueue :任务队列

threadFactory :线程工厂,指定创建线程的方式

handler :任务拒绝策略,当任务队列已满,新任务不能提交到线程池时触发对新任务

的处理策略

3、任务拒绝策略

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectExecutionException异

常,默认的任务拒绝策略

ThreadPoolExecutor.DiscardPolicy:丢弃任务但不抛出异常,不推荐使用

ThreadPoolExecutor.DiscardOldestPolicy:抛弃队列中等待最久的任务然后将当

前任务加入任务队列

ThreadPoolExecutor.CallerRunsPolicy:调用任务的run()方法绕过线程池直接执行

三、原子性

原子性是指一个操作是不可中断的,要么全部执行成功要么全部执行失败。即使是多个线程一起执行,一个操作一旦开始,就不会被其他线程所干扰。

volatile不能保证原子性,synchronized可以保证原子性。

原子性操作类,既能保证原子性又比synchronized高效,如Atomiclnteger

1、构造方法

public AtomicInteger():创建初始值为0的对象

public AtomicInteger(int value):创建指定值的对对象

2、常用方法

方法名

说明

int get()

获取值

int getAndIncrement()

以原子方式将当前值加1,返回加1前的旧值

int incrementAndGet()

以原子方式将当前值加1,返回加1后的新值

int addAndGet(int value)

以原子方式将当前值与参数相加,并返回结果

int getAndSet(int value)

以原子方式将当前值设置为参数的值,返回旧值

3、原理(底层使用自旋+CAS算法)

自旋:就是重新获取共享变量的操作

CAS算法:

● 线程在修改共享数据时查看共享数据的值与变量副本的值是否相同

● 如果相同说明共享变量的值没有被其他线程修改,可以直接将新值赋给共享数据

● 如果不相同,说明在对变量副本进行操作时有其他线程修改了共享数据,此时不能

修改共享数据,而是重新获取共享数据的值

4、sychronized与CAS的区别

相同点:在多线程的情况下,都可以保证共享数据的安全性

不同的:

1、sychronized总是从最坏的角度出发,认为每次获取数据时,别的线程都有可能修改,所以每次操作共享数据前,都会上锁(悲观锁

2、CAS是从乐观的角度出发,假设每次获取数据时别的线程都不会修改,所以不上锁,只是在修改共享数据时再查看其他线程有没有修改共享数据,如果有就重新获取新的共享数据,如果没有就直接修改共享数据(乐观锁

四、并发工具类

HashTable类

● HashMap是线程不安全的,为了保证数据安全性可以使用线程安全的HashTable代替

● HashTable效率比较低下

● HashTable采用sychronized悲观锁,当有线程访问时会将整个集合加锁

ConcurrentHashMap类

● ConcurrentHashMap是线程安全的,效率较高

● 在JDK7和JDK8中实现的原理有区别

● JDK7原理

        ■ 使用无参构造创建对象时,创建一个默认长度16,加载因子为0.75的数组,数组名为

segment,并且这个数组无法扩容

        ■ 再创建长度为2的小数组,将该小数组地址存入segment数组的0索引,segment其他索引均为null,这个小数组作为模板数组

        ■ 在添加元素时会根据元素的哈希值计算出在segment的应存入位置的索引。如果为null

则按照模板数组创建小数组,创建完毕后会进行二次哈希,计算出在小数组中应存入位置的索引,然后直接存入;如果不是null则直接找到小数组进行二次哈希,计算出在小数组中应存入位置的索引,如果小数组需要扩容则扩容到两倍,然后存入,如果小数组不需要扩容就查看该位置有无元素,如果没有元素直接存,如果有元素就调用equals()方法比较,相同的话不存,不相同就形成哈希桶结构

        ■ 根据添加的原理,该集合实际上是创建了16个哈希表结构

        ■ 保证线程安全的方式是,当线程对segment某个索引处的哈希表进行操作时对该索引处加锁,而其他索引则不加锁

● JDK8原理

        ■ 使用无参构造创建对象时并不会创建底层数组,而是在第一次添加数据时初始化长度为16,加载因子为0.75的数组

        ■ 添加元素时计算应存入的索引,如果索引为null,则利用CAS算法将元素添加到此处;如果不为null,则利用volatile关键字获取当前位置最新的节点地址,将当前元素挂在它下面变成链表,当链表长度大于8时转为红黑树

        ■ 保证数据安全的方式是对链表或者红黑树头节点加锁,配合悲观锁保证多线程操作集合时的安全性

CountDownLatch类

Semaphore类

标签:队列,创建,int,线程,数组,arrayBlockingQueue,共享,性及
From: https://blog.51cto.com/u_15815415/5738620

相关文章

  • 同步代码块、同步方法解决数据安全问题、线程安全的类及Lock锁
    目录​​一、同步代码块解决数据安全问题​​​​二、同步方法解决数据安全问题​​​​三、线程安全的类​​​​四、Lock锁​​一、同步代码块解决数据安全问题安全问题出......
  • 文件的属性及类型
    目录文件的属性及类型文件属性文件类型查看命令-file系统的链接文件链接文件的种类软链接文件硬链接文件硬链接和软连接的区别企业面试题文件的属性及类型文件属性[root......
  • ThreadLocal本地局部线程demo
    ThreadLocal本地局部线程demoimportorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importjava.util.HashMap;importjava.util.Map;/***本工具只能保存一......
  • Java中如何实现两个线程交替运行呢?
    今天笔者收到老师的一个题目,让我准备两个流程,依次实现输出以下信息 如:  线程A打印字母a,线程B打印数字1线程A打印字母b,线程B打印数字2线程A打印字母......
  • Python多线程
    一、概念线程是CPU分配资源的基本单位,当程序开始运行,这个程序就变成了一个进程;当有多线程编程时,一个进程包含多个线程(含主线程),使用线程可以实现程序大的开发任务。多线......
  • Java小白自学笔记——线程
    一、线程的相关概念1.程序:是为完成特定任务,用某种语言编写的一组指令的集合。简单地说:就是我们写的代码2.进程:(1)进程是指运行中的程序,启动了一个进程,操作系统就......
  • Java_多线程
    Java_多线程1.实现多线程1.1进程和线程【理解】进程:是正在运行的程序是系统进行资源分配和调用的独立单位每一个进程都有它自己的内存空间和系统资源线程:是进程......
  • TTD 专题 (第一篇):C# 那些短命线程都在干什么?
    一:背景1.讲故事在分析的众多dump中,经常会遇到各种奇葩的问题,仅通过dump这种快照形式还是有很多问题搞不定,而通过perfview这种粒度又太粗,很难找到问题之所在,真的很头疼,比如......
  • 为什么说js是单线程?
    为了搞清楚这个问题,我们需要先了解这几个东西:1.什么是线程?什么是进程?他们之间的关系?2.什么是任务队列(EventQueue)?3.什么是事件循环?4.为什么说js是单线程?5.为什么j......
  • Delphi xe TTask 执行多线程,其实...完成任务后,一直在后台执行东西的。
    usesSystem.Threading;//定义变量线程池lpThreadPool:TThreadPool;//定义事件procedureTForm1.log(lgoInfo:string);beginMemo1.Lines.Add('');Me......