首页 > 电脑常识 > 运维

java与多线程详解

admin 运维 2021-05-25 09:25:21 多线程 并发编程 jvm Java thread 
后台-系统设置-扩展变量-手机广告位-内容正文底部

文章目录

  • 前言
  • 串行,并行和并发
  • 同步和异步
  • 阻塞和非阻塞
  • 任务
    • Runnable
    • Callable
    • Future
    • CompletionStage
  • 线程创建
    • Thread
    • 线程池
      • ThreadPoolExecutor
      • ThreadFactory
    • 执行器
      • Executor
      • ExecutorService
      • ScheduledExecutorService
      • CompletionService
  • 线程优先级
  • 线程状态
  • 线程安全
    • 共享状态(临界区)
    • 为什么会有线程安全问题
    • 解决线程安全问题
      • 不在线程之间共享可变状态
      • 将可变状态改为不可变状态
      • volatile
      • 在访问可变状态时使用锁同步机制
        • 基于对象锁的synchronized同步机制
        • 基于可重入锁的Lock同步机制
        • 基于信号量的Semaphore同步机制
        • 基于闭锁的CountDownLatch同步机制
        • 基于栅栏的CyclicBarrier同步机制
      • 在访问可变状态时使用无锁同步机制
        • CAS比较交换算法
        • 原子变量
    • 同步容器
      • Vector
      • HashTable
      • Collections.synchronizedXxx
    • 并发容器
      • ConcurrentLinkedQueue
      • BlockingQueue
      • TransferQueue
      • ConcurrentMap
      • ConcurrentSkipListMap和ConcurrentSkipListSet
      • CopyOnWriteArraySet和CopyOnWriteArrayList
    • 线程安全设计模式
      • 单例模式
      • 不变模式
      • 生产者消费者模式
      • Future模式
  • 线程的中断
  • 线程阻塞
    • sleep
    • join
    • yield
    • LockSupport
  • 进程
    • Process
    • ProcessBuilder

前言

本文随着作者自身技术的增加随时会更新而不会做出任何提示

串行,并行和并发

cpu以线程为处理单位,同一时间单个核心只能处理一个线程,串行,并行和并发就是用于描述cpu处理线程的方式,

方式说明串行单核心,物理上单线程并行多核心,物理上多线程并发单核心,逻辑上多线程

在这里插入图片描述
线程的调度由操作系统完成,我们无法控制.在java中我们仅能能控制线程的数量,一个线程可以执行多个方法,为了保证线程之间的安全性,我们必须控制线程对方法调用的时机,这就称为多线程并发编程,

同步和异步

同步和异步用于描述方法调用与线程之间的关系.

无依赖关系的方法

假定现有两个方法(M1和M2)需要执行,当两个方法之间没有依赖关系时,执行图如下
在这里插入图片描述

有依赖关系的方法

当方法M2依赖于方法M1时,执行图如下

在这里插入图片描述

阻塞和非阻塞

阻塞非阻塞用来形容同步异步中多线程之间的相互影响

阻塞

当main线程(其它线程)在等待其它线程(main线程)结束或完成对一个临界条件的操作时,如果main线程(其它线程)什么也不做而是一直检查是否可以继续执行,那么main线程(其它线程)就进入了阻塞状态

在这里插入图片描述

非阻塞

当main线程(其它线程)在等待其它线程(main线程)结束或完成对一个临界条件的操作时,如果main线程(其它线程)开始了其它不相关的任务并被动等待其它线程(main线程)的通知,那么main线程(其它线程)就进入了非阻塞状态

在这里插入图片描述

任务

Runnable

实现此接口可以得到一个无返回值的任务

void run()  

Callable

实现此接口可得到一个有返回值的任务

V call()  

Future

Future表示一个任务的生命周期,并提供相应的方法来判断是否已经完成或取消,以及获取任务的结果和取消任务等.

在这里插入图片描述

boolean cancel(boolean mayInterruptIfRunning) 尝试取消执行此任务,参数表示当前任务是否可以接受中断(这并不表示它能检测并处理中断)
V get() 等待计算完成,然后检索其结果。  
V get(long timeout, TimeUnit unit) 如果需要等待最多在给定的时间计算完成,然后检索其结果(如果可用)。  
boolean isCancelled() 如果此任务在正常完成之前被取消,则返回 true 。  
boolean isDone() 返回 true如果任务已完成。 

FutureTask

Future一个具体的实现

FutureTask(Callable<V> callable) 创建一个 FutureTask ,它将在运行时执行给定的 Callable 。  
FutureTask(Runnable runnable, V result) 创建一个 FutureTask ,将在运行时执行给定的 Runnable ,并安排 get将在成功完成后返回给定的结果。  

CompletionStage

CompletionStage提供了一种异步计算的方式,我们注册一个回调函数,一旦一个结果可用,就会在某个线程中使用这个结果调用这个回调函数.

方法参数说明thenApplyT->U对结果应用一个函数thenAcceptT->void对结果应用一个函数,返回值为voidthenComposeT->CompletableFuture<U>对结果调用函数并执行返回的futurehandle(T,Throwable)->U处理结果或错误,生成一个新结果whenComplete(T,Throwable)->void处理结果或错误,生成一个void新结果exceptionallyThrowable->T从错误计算一个结果completeOnTimeoutT.long,TimeUnit如果超时,生成给定值作为结果orTimeoutlong,TimeUnit如果超时,生成一个TimeoutException异常thenRunRunnable执行Runnable,结果为voidthenCombineCompletableFuture,<T,U>->V执行两个动作,并用给定的函数组合结果thenAcceptBothCompletableFuture,<T,U>->void执行两个动作,结果为voidrunAfterBothCompletableFuture<?>,Runnable两个都完成后执行runnableapplyToEitherCompletableFuture<T>,T->V得到其中一个结果时,传入给定的函数acceptEitherCompletableFuture<T>,T->void得到其中一个结果时,传入给定的函数runAfterEitherCompletableFuture<?>,Runnable其中一个完成后执行runnablestatic allOfCompletableFuture<?>…所有给定的future都完成后完成,结果为voidstatic angOfCompletableFuture<?>…任意给定的future都完成后完成,结果为void

CompletableFuture

CompletionStage一个具体的实现

CompletableFuture() 

线程创建

Thread

Thread类表示线程,要想创建一个线程,必须显式的调用该类的start()方法,而不是直接调用任务的run方法.

Thread(Runnable target) 接收一个任务创建一个线程
Thread(Runnable target, String name) 接收一个任务和线程名字创建一个线程
void start() 启动线程
boolean isAlive()   判断线程是否还活着
static Thread currentThread()  获取当前线程
void setDaemon(boolean on)  将当前线程设置为守护线程
boolean isDaemon() 判断当前线程是否为守护线程
String getName()  获取线程的名字
void setName(String name)  设置线程的名字

Runnable线程

public static void main(String[] args) throws ExecutionException, InterruptedException {
    FutureTask<String>  haveReturnTask= new FutureTask<>(() -> {
        System.out.println("haveReturnThread is running");
    },"this is my returnValue");
    new Thread(haveReturnTask).start();
    String returnValue = haveReturnTask.get();
}

Callable线程

public static void main(String[] args) throws ExecutionException, InterruptedException {
    FutureTask<String>  haveReturnTask= new FutureTask<>(() -> {
        System.out.println("haveReturnThread is running");
        return "this is my returnValue";
    });
    new Thread(haveReturnTask).start();
    String returnValue = haveReturnTask.get();
}

线程池

线程池用于改变为每一个任务都创建一个线程的状况,它将任务和线程解耦,通过复用已有线程来执行任务,极大提高了程序性能.Executors工具类提供了各种类型的线程池.

FixedThreadPool

FixedThreadPool是一个固定长度的线程池,每当提交一个任务时就创建一个线程,直到达到线程池的最大数量

static ExecutorService newFixedThreadPool(int nThreads)  

SingleThreadExecutor

SingleThreadExecutor是一个单线程线程池,如果这个线程异常结束,会创建另一个线程来代替,它依照任务在队列中的顺序来串行执行.

static ExecutorService newSingleThreadExecutor()  
static ScheduledExecutorService newSingleThreadScheduledExecutor()  

CachedThreadPool

CachedThreadPool是一个可根据需求调整线程数量的线程池,如果线程池的当前规模超过了处理请求时,那么将回收空闲的线程,而当需求增加时,则可以增加新的线程.

static ExecutorService newCachedThreadPool()  

ScheduledThreadPool

ScheduledThreadPool是一个固定长度的线程池,而且以延迟或定时的方式执行任务

static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)  

SingleThreadScheduledExecutor

static ScheduledExecutorService	newSingleThreadScheduledExecutor()

ThreadPoolExecutor

所有线程池的创建都是创建的ThreadPoolExecutor实例,通过该实例可以自定义线程池

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
corePoolSize:线程池中线程数量
maximumPoolSize:最大线程数量
keepAliveTime:超过corePoolSize的线程空闲线程在多长时间内会被销毁
unit:keepAliveTime的单位
workQueue:任务队列,提交但未被执行的任务
threadFactory:线程工厂,一般默认即可
handler:拒绝策略,当任务来不及处理是=时,如何拒绝任务
protected void	afterExecute(Runnable r, Throwable t)
protected void	beforeExecute(Thread t, Runnable r)
protected void	finalize()
这三个方法用于扩展线程池

RejectedExecutionHandler拒绝策略

AbortPolicy:直接抛出异常
CallerRunsPolicy:只要线程池没关闭,就直接在调用者线程内执行任务
DiscardOldestPolicy:丢弃最老的任务,并提交当前任务
DiscardPolicy:丢弃无法处理的任务

ThreadFactory

ThreadFactory用于创建线程池中的线程,

Thread	newThread(Runnable r)

执行器

执行器用于利用线程池中的线程执行任务.
在这里插入图片描述

Executor

void execute(Runnable command)  

ExecutorService

ExecutorService在Executor的基础上增加了一些用管理于生命周期和任务提交的便利方法.

<T> Future<T> submit(Callable<T> task)  
Future<?> submit(Runnable task) 
<T> Future<T> submit(Runnable task, T result)   
提交指定的任务来执行
<T> T invokeAny(Collection<? extends Callable<T>> tasks)  
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)  
执行给定的任务,返回其中一个任务的结果,如果超时,第二个方法会抛出一个TimeoutException异常
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)  
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) 
执行给定的任务,返回所有任务的结果,如果超时,第二个方法会抛出一个TimeoutException异常
void shutdown()  不再接受新的任务,同时等待已经提交的任务完成包括那些还未开始执行的任务
List<Runnable> shutdownNow()  不再接收新的任务,尝试关闭正在执行的任务,并且不在启动尚未执行的任务,返回尚未启动的任务
boolean isShutdown() 如果服务关闭,则返回true
boolean awaitTermination(long timeout, TimeUnit unit)  等待
boolean isTerminated()  如果任务在服务关闭后完成,则返回true

ScheduledExecutorService

ScheduledExecutorService在ExecutorService的基础上添加了计划任务的功能,它可以在指定的时间对任务进行调度.

ScheduledFuture<?>	schedule(Runnable command, long delay, TimeUnit unit)
<V> ScheduledFuture<V>	schedule(Callable<V> callable, long delay, TimeUnit unit)
在delay时间后执行任务
ScheduledFuture<?>	scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
在initialDelay时间后执行任务,在period时间间隔内重复执行
ScheduledFuture<?>	scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
在initialDelay时间后执行任务,在上个任务执行完成后延时delay继续执行下一个任务

CompletionService

CompletionService将Executor和BolckingQueue的功能融合在一起.可以将Callable任务交给它来执行,然后使用类似于队列操作的方法来获取已完成的结果.

Future<V> poll() 
Future<V> poll(long timeout, TimeUnit unit) 
移除并返回下一个已完成的结果,如果没有可用的已完成的结果,则返回null,第二个方法会等待给定的时间.
Future<V> take() 移除下一个已完成的结果,如果没有可用的已完成的结果,则阻塞 
Future<V> submit(Callable<V> task) 
Future<V> submit(Runnable task, V result) 
提交一个任务给底层的执行器

ExecutorCompletionService

CompletionService一个具体的实现

ExecutorCompletionService(Executor executor)  
ExecutorCompletionService(Executor executor, BlockingQueue<Future<V>> completionQueue)  

线程优先级

void setPriority(int newPriority) 设置线程优先级
int getPriority()  获取线程优先级

每个线程都有一个优先级,每当线程调度器有机会选择新线程时,它首先选择具有较高优先级的线程.但是,线程优先级是高度依赖于系统的.当虚拟机依赖于宿主机平台的线程实现机制时,Java 线程的优先级被映射到宿主机平台的优先级上,优先级个数也许更多,也许更少.

线程状态

Thread.State getState()  获取线程状态

在这里插入图片描述

状态说明NEW当实例化一个线程对象时,该线程还没有开始运行,这意味着它的状态是新创建.当一个线程处于新创建状态时,程序还没有开始运行线程中的代码.在线程运行之前还有一些基础工作要做.RUNNABLE一旦调用线程实例的start方法,线程就处于可运行状态.一个处于可运行状态的线程可能正在运行也可能没有运行, 因为操作系统并不会给每一个线程都分配一个单独的处理器.BLOCKED当一个线程试图获取一个内部的对象锁,但该锁被其它线程持有时,该线程就会进人阻塞状态,当锁被释放并且线程调度器允许该线程持有它的时候,该线程将返回到原来的状态.WAITING当线程等待某个条件出现时,它自己就进入等待状态.TIMED_WAITING当线程等待某个条件出现并设置了最大的等待时间时,它自己就进入计时等待状态.TERMINATED当线程正常结束或不正常结束时,就处于终止状态

线程安全

共享状态(临界区)

对象的共享状态是指存储在字段内的数据,当多个线程访问可变的共享状态时就会引发线程安全问题

为什么会有线程安全问题

内存可见性

内存可见性是指当某个线程对一个共享状态进行修改时,其它线程是否立即知道这个修改.出现内存可见性的原因是jvm会给每个线程分配一个缓存,每个线程在操作主存内的共享状态之前会先将该状态读到缓存中,但是由于线程在缓存内的动作不能及时传入到主存中,从而引发线程安全问题.

原子性

原子性是指一个线程的某个对共享状态的操作一旦开始就不会被其它线程干扰,出现原子性的原因是一条java语句可能对应多个字节码指令,而jvm以字节码指令为单位执行并且在必要时还会对这些字节码进行重新排序,还有一个原因是对于long和double这种64为数据而言,在局部变量表中需要占用两个局部变量槽

解决线程安全问题

解决线程安全问题就是解决内存可见性和原子性的过程.

不在线程之间共享可变状态

无状态对象

如果一个对象不包括任何字段,也不依赖与其它字段,计算过程中的临时状态仅存在于操作数栈上的局部变量表中,那么这种对象被称为无状态对象,无状态对象一定是线程安全的.

ThreadLocal

可以为每个线程创建一个共享状态的副本,并将这些副本放到ThreadLocal内,ThreadLocal会保证每个线程只会访问到自己的副本.

void set(T value)
T get()
void remove()

将可变状态改为不可变状态

如果一个共享状态在对象实例化之后就不能被改变,那么这种对象被称为不可变对象,不可变对象一定是线程安全的.

volatile

当把一个状态声明为volatile后,编译器和执行器就会意识到这个状态是共享的,因此该状态不会被存储在寄存器等不可见的地方.但是volatile只能保证状态的可见性而并不能保证修改状态动作的原子性,所以仅当满足以下所有条件时才应该使用volatile:

  • 对状态的修改操作不依赖状态的当前值,或者确保只有单个线程可以修改状态
  • 该状态不会和其它状态有关联
  • 在访问状态时不需要加锁

在访问可变状态时使用锁同步机制

在某个线程修改可变状态时,通过某种方式防止其它线程修改或访问这个状态,从而确保其它线程只能在修改操作完成之前或之后读取和修改该状态,而不是在修改状态的过程中.这里所说的可变状态可能是多个彼此之间存在联系的可变状态的集合,在这种情况下,应该将它们看成一个整体,而不是单独把它们拿出来.

基于对象锁的synchronized同步机制

java中的每个对象都有一个内置锁,每个对象的内置锁最多只能由一个不同线程获得,但是同一个线程可以获得多次,synchronized关键字基于对象锁的特性以实现同步代码块,一个同步代码块由一个对象锁和一个被保护的代码块组成,当一个线程进入同步代码块时就会获得某个对象的对象锁,并在退出同步代码块时自动释放该锁,也就是说任何一个执行同步代码块的线程都不可能看到有其它线程正在执行由同一个对象锁保护的代码块,这样就解决了线程安全问题.同步代码块有以下三种形式.

synchronized实例方法

synchronized实例方法获得的对象锁就是当前调用方法的实例的对象锁

synchronized public void method(){
    //synchronizedCode
}

synchronized静态方法

synchronized静态方法获得的对象锁就是方法所在类的Class实例的对象锁

synchronized static public void method(){
	//synchronizedCode
}

synchronized代码块

synchronized代码块可以指定获得哪个对象的对象锁

public void method(){
	//...
    synchronized (/*AnyObject*/){
        //synchronizedCode
    }
    //...
}

线程协作

当在锁对象上调用wait方法时,当前线程就会进入等待状态,直到其它线程调用这个锁对象的notify方法才会被激活

void wait() 加入锁对象的等待序列
void wait(long timeout) 计时等待
void notify() 随机挑选一个等待线程激活
void notifyAll() 激活所有的等待线程

基于可重入锁的Lock同步机制

Lock提供了一种无条件的,可轮询的,定时的以及可中断的锁获取操作,所有加锁的方法都是显式的.

void lock()  获得锁
void lockInterruptibly()  可中断锁
boolean tryLock()  轮询锁
boolean tryLock(long time, TimeUnit unit)  定时锁
Condition newCondition()  绑定一个Condition实例
void unlock()  释放锁

在这里插入图片描述

ReentrantLock

ReentrantLock提供了与synchronized相同的功能,并且在此基础上还为处理锁的不可用性问题提供了更高的灵活性.

ReentrantLock() 

ReentrantReadWriteLock

在一个资源可以被多个读操作访问,或者被一个写操作访问,但两者不能同时进行的情况下,可以使用读写锁.

ReentrantReadWriteLock() 
ReentrantReadWriteLock.ReadLock readLock()  返回一个读锁
ReentrantReadWriteLock.WriteLock writeLock()  返回一个写锁

StampedLock

StampedLock是读写锁的升级版,它在读写锁的基础上使得读写锁之间不会相互阻塞.

Condition线程协作

Condition是synchronized线程协作的升级版

void await()
boolean	await(long time, TimeUnit unit)
void awaitUninterruptibly() 不会在等待过程中响应中断
void signal()
void signalAll()

基于信号量的Semaphore同步机制

信号量用来控制同时访问某个共享状态或同时执行某个指定操作的线程数量.在信号量中管理着一组虚拟的许可,许可的数量可以根通过构造函数指定.在执行任务时要首先获取许可,并在操作完成后释放许可.如果没有许可

Semaphore(int permits) 创建一个信号量并给定许可数量
void acquire()  
boolean tryAcquire() 
boolean tryAcquire(long timeout, TimeUnit unit)  
void acquire(int permits)  
boolean tryAcquire(int permits) 
boolean tryAcquire(long timeout, TimeUnit unit)   
从信号量获取许可阻塞至真正获得
void release()  
void release(int permits)  
释放许可,将其返回给信号量

基于闭锁的CountDownLatch同步机制

闭锁可以使一个或多个线程等待一组事件的发生.

CountDownLatch(int count) 构造一个给定计数闭锁
void await()  
boolean await(long timeout, TimeUnit unit)  
使当前线程等待直到锁存计数到零为止
void countDown()  减少锁存器的计数,如果计数达到零,释放所有等待的线程

基于栅栏的CyclicBarrier同步机制

栅栏类似于闭锁,它们之间的区别在于闭锁用于等待事件,栅栏用于等待线程,并且栅栏是可以重复使用的.

CyclicBarrier(int parties) 
CyclicBarrier(int parties, Runnable barrierAction)  
创建一个新的栅栏当给定数量的线程(线程)等待它时,它将跳闸,并且当屏障跳闸时执行预定义的动作。  
int await()  等待
boolean isBroken()  查询栅栏是否打开
void reset()  重置栅栏

在访问可变状态时使用无锁同步机制

CAS比较交换算法

该算法含有参数:V(要更新的变量),E(预期值),N(新值).仅当V值等于E时才会将V的值设为N,如果V值和E值不相等,那么说明有其它线程做了更新,则当前线程什么都不做.最后,CAS返回V的真实值.

原子变量

原子变量是一些直接使用CAS算法操作的线程安全的类型.原子变量有以下分类

AtomicInteger,AtomicLong,AtomicBoolean,AtomicReference,AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray

原子变量和源自数组维护了对象的值,只有值满足期望时才能被修改.

AtomicStampedReference,AtomicMarkableReference

AtomicStampedReference不仅维护对象的值,还维护了一个时间戳,当AtomicStampedReference对应的数值被修改时,除了更新数据本身外,还必须更新时间戳,只有时间戳和对象值都满足期望值,对象值的写入才能成功.AtomicMarkableReference与AtomicStampedReference类似,只是时间戳换成了一个布尔值

AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicReferenceFieldUpdater

更新器类可以让一些普通字段也享受CAS带来的线程安全性.但是有一些条件要遵循:

  • 字段必须是可访问的
  • 字段必须是volatile
  • 不支持static字段

DoubleAdder,LongAdder,DoubleAccumulator,LongAccumulator

累加器,DoubleAccumulator,LongAccumulator支持自定义在自定义运算

同步容器

同步容器的同步策略

同步容器将它们的状态封装起来,并对每个公有方法都进行同步,使得每次只有一个线程能访问容器的状态.

同步容器的问题

当多个线程交替使用多个同步容器的方法时,即使这些方法是同步的,也需要再次进行同步.并且同步容器的性能极差.

Vector

Vector与ArrayList的作用一样,接口也基本相同,但HashTable内的所有方法都是同步的

HashTable

HashTable的作用和HashMap一样,接口也基本相同,但HashTable内的所有方法都是同步的

Collections.synchronizedXxx

static < E> List synchronizedList(List< E> c )
static < E> Set synchronizedSet(Set< E> c )
static <K , V > Map<K, V > synchronizedMap(Map<K , V > c )

并发容器

并发容器针对多个线程并发访问设计,通过并发容器来替代同步容器,可以极高的提高伸缩性并降低风险.
在这里插入图片描述
在这里插入图片描述

ConcurrentLinkedQueue

构造一个可被多线程安全访问的无上限非阻塞队列

ConcurrentLinkedQueue() 

BlockingQueue

阻塞队列提供了可阻塞的put和take方法,以及支持定时的offer和poll方法.如果队列为空,那么获取元素的操作将一直阻塞,如果队列已满,那么插入元素的操作将一直阻塞.

void put(E e)
E take()
boolean offer(E e, long timeout, TimeUnit unit) 添加给定的元素,如果成功返回true,必要时阻塞,直至元素已经添加或超时  
E poll(long timeout, TimeUnit unit)  移除并返回队首元素,必要时阻塞,直至元素可用或超时,失败时返回null

LinkedBlockingQueue

实现为链表的阻塞队列

LinkedBlockingQueue() 
LinkedBlockingQueue(int capacity) 

ArrayBlockingQueue

实现为循环数组的阻塞队列

ArrayBlockingQueue(int capacity) 

PriorityBlockingQueue

阻塞优先队列

PriorityBlockingQueue() 
PriorityBlockingQueue(int initialCapacity) 
PriorityBlockingQueue(int initialCapacity, Comparator<? super E> comparator) 

DelayQueue

构造一个包含Delayed元素的无上限阻塞队列,只有那些延迟已经到期的元素才能从队列中删除.

DelayQueue() 

SynchronousQueue

SynchronousQueue不会为队列中的数据维护存储空间,与其他队列不同的是,它维护一组线程,这些线程在等待着把元素加入或移除队列.因为SynchronousQueue没有存储功能,因此take和put会一直阻塞,直到拥有另一个线程从队列中获取元素时

SynchronousQueue() 

TransferQueue

TransferQueue类似于BlockingQueue和SynchronousQueue的组合,主要体现在transfer方法,当有消费者线程阻塞等待时,调用transfer方法的生产者线程不会将元素存入队列,而是直接将元素传递给消费者;如果调用transfer方法的生产者线程发现没有正在等待的消费者线程,则会将元素入队,然后会阻塞等待,直到有一个消费者线程来获取该元素。

void transfer(E e)  
boolean tryTransfer(E e, long timeout, TimeUnit unit)  
传输一个值,或者尝试在给定超时时间内传输这个值,这个调用将阻塞,直到另一个线程将元素删除

LinkedTransferQueue

实现为链表的转让阻塞队列

LinkedTransferQueue() 

ConcurrentMap

ConcurrentMap并不是在每个方法上都在同一个锁上同步并使得每次只能有一个线程访问容器,而是使用分段锁机制来实现更大程度上的共享,在这种机制下,允许读操作和一定数量的写操作并发访问.

ConcurrentHashMap

构造一个可被多线程安全访问的散列映射表,默认的初始值为16,如果每个桶的平均负载超过装载因子,表的大小会重新调整,默认值为0.75,并发级别是估计的并发书写器的线程数.

ConcurrentHashMap()  
ConcurrentHashMap(int initialCapacity)  
ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) 

ConcurrentSkipListMap和ConcurrentSkipListSet

跳表是一种采用了用空间换时间思想的数据结构。它会随机地将一些节点提升到更高的层次,以创建一种逐层的数据结构,以提高操作的速度。

CopyOnWriteArraySet和CopyOnWriteArrayList

读取不需要加锁,并且写入也不会阻塞读操作,只有写入和写入之间需要进行同步等待

线程安全设计模式

单例模式

概要

单例模式确保一个类只有一个实例,并提供一个全局访问点,单例模式需要注意多线程和多个类加载器分别加载的问题,这都可能导致单例模式失效.

demo

package cn.superstallion;

/**
 * 单例模式demo
 */
public class SingletonPattern {
    public static void main(String[] args) {
        President1 president = President1.getInstance();
    }
}

//一个学校只能有一个正校长
class President1 {
    
    private static President1 president1;
    private President1(){
        
    }
    
   synchronized static public President1 getInstance(){
        if (president1==null){
            president1=new President1();
        }
        return president1;
    }
}

//性能优化版1
class President2{
    static private President2 president2=new President2();
    
    private President2(){
        
    }
    
    synchronized static public President2 getInstance(){
        return president2;
    }
    
}

//性能优化版2
class President3{
    static volatile private President3 president3;
    
    private President3(){
        
    }
    
    public static President3 getInstance(){
        if (president3==null){
            synchronized (President3.class){
                if (president3==null){
                    president3=new President3();
                }
            }
        }
        return president3;
    }
}

不变模式

概要

不变模式确保一个类对象被创建后,期内部的状态和数据不再发生任何变化.

demo

public final class Person{
	private final String name;
	private final Integer age;
	
	public Person(String name,Integer age){
		this.name=name;
		this.age=age;
	}
	
	public String getName(){
		return this.name;
	}

	public Integer getAge(){
		return this.age;
	}
}

生产者消费者模式

概要

在生产者消费者模式中,通常有两种线程,即若干个生产者线程和若干个消费者线程,生产者线程负责提交用户请求,消费者线程负责处理生产者提交的任务,生产者消费者通过共享内存缓冲区进行通信.

demo

public class Producer <T>implements Runnable{
	private BlockingQueue<T> queue;

	public Producer(BlockingQueue<T> queue){
		this.queue=queue;
	}
}

public class Consumer <T>implements Runnable{
	private BlockingQueue<T> queue;

	public Producer(BlockingQueue<T> queue){
		this.queue=queue;
	}
}

Future模式

概要

Future模式无法立即给出想要的数据,但是会返回一个契约,将来可以凭借这个契约去获取想到的信息.

线程的中断

void interrupt() 向线程发送中断请求,并将中断标志设置为true
boolean isInterrupted()  返回线程的中断标志
static boolean interrupted() 返回线程的中断标志并清除

除了正常中断或抛出一个未被捕获的异常而导致线程中断之外,java没有可以强制中断线程的方法,但是可以通过调用interrupt方法请求中断,当此方法被调用时,线程的中断标志将被设置为true(这是每一个线程都具有的boolean标志),在每个线程内都应该不时地检测这个标志,以判断线程是否被请求中断,并决定是否接受请求.

public static void main(String[] args){
    //创建一个线程
    Thread testThread = new Thread(()->{
        System.out.println("testThread is start");
        //判断中断标志是否被置位,如果被置位那么就退出循环终止当前线程
        while (!Thread.currentThread().isInterrupted()){
            System.out.println("testThread is running");
        }
        System.out.println("testThread is interrupted");
    });
    //运行线程
    testThread.start();
    try {
        //让main等待一微秒,便于测试
        Thread.sleep(1);
        //在main线程内请求中断testThread
        testThread.interrupt();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

如果一个线程处于阻塞或等待状态,那么就无法检测中断标志,这可能会导致一个线程永远不会中断,为了应对这种状况,一些导致线程阻塞或等待的方法中断标志设置为true时会立即抛出一个InterruptedException异常并将中断标志设置为false.

//方式一
public static void main(String[] args){
    //创建一个线程
    Thread testThread = new Thread(()->{
        System.out.println("testThread is start");
        while (true){
            //一直阻塞
            try {
                Thread.sleep(9999);
            } catch (InterruptedException e) {
                //捕获到异常直接退出循环,从而正常中断线程
                break;
            }
        }
        System.out.println("testThread is interrupted");
    });
    //运行线程
    testThread.start();
    try {
        //让main等待一微秒,便于测试
        Thread.sleep(1);
        //在main线程内请求中断testThread
        testThread.interrupt();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
//方式二
public static void main(String[] args) {
    //创建一个线程
    Thread testThread = new Thread(() -> {
        System.out.println("testThread is start");
        //在循环外捕获异常
        try {
            while (true) {
                Thread.sleep(9999);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("testThread is interrupted");
    });
    //运行线程
    testThread.start();
    try {
        //让main等待一微秒,便于测试
        Thread.sleep(1);
        //在main线程内请求中断testThread
        testThread.interrupt();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

在这里插入图片描述

线程阻塞

sleep

static void sleep(long millis)  线程休眠

join

void join()  等待终止指定的线程
void join(long millis)  计时等待终止执行的线程

yield

static void yield()  使当前正在执行的线程交出运行权

LockSupport

LockSupport可以在县城内任意位置让线程阻塞,它不需要先获取某个对象的锁,也不会抛出InteruptedException异常.

static void	park() 阻塞当前线程
static void	parkNanos(long nanos) 阻塞指定时间
static void	parkUntil(long deadline) 最多阻塞指定时间
static void	unpark(Thread thread) 解除阻塞参数线程

进程

Process

Process类在一个单独的操作系统进程中执行一个命令,允许我们在与标准输入,输出和错误流交互.

InputStream getErrorStream() 
InputStream getInputStream()   
OutputStream getOutputStream()  
获取流
int waitFor()  
boolean waitFor(long timeout, TimeUnit unit)  
等待进程结束并生成退出值
int exitValue()  返回进程的推出值,非零退出值表示一个错误
boolean isAlive()  线程是否还或者
void destroy() 
Process destroyForcibly()  
终止进程

ProcessBuilder

ProcessBuilder类允许我们配置Process实例.

ProcessBuilder(String... command) 用给定的命令和参数构造一个进程构造器
ProcessBuilder directory(File directory)  设置进程的工作目录
ProcessBuilder inheritIO()  让进程使用虚拟机的标准输入,输出错误流
ProcessBuilder redirectInput(ProcessBuilder.Redirect source)  
ProcessBuilder redirectOutput(ProcessBuilder.Redirect destination)  
ProcessBuilder redirectError(ProcessBuilder.Redirect destination)  
重定向标准输入,输出错误流
Map<String,String> environment()  设置环境变量
Process start()  开启进程

文章来源:https://blog.csdn.net/qq_45295475/article/details/105365685

后台-系统设置-扩展变量-手机广告位-内容正文底部
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。
本文地址:https://www.jcdi.cn/fwq1/30746.html

留言与评论(共有 0 条评论)
   
验证码:
后台-系统设置-扩展变量-手机广告位-评论底部广告位

教程弟

https://www.jcdi.cn/

统计代码 | 京ICP1234567-2号

Powered By 教程弟 教程弟

使用手机软件扫描微信二维码