首页 文章 精选 留言 我的

精选列表

搜索[高并发],共10000篇文章
优秀的个人博客,低调大师

Java并发编程笔记——J.U.C之executors框架:executors框架设计理念

一、executors框架简介 juc-executors框架是整个J.U.C包中类/接口关系最复杂的框架,真正理解executors框架的前提是理清楚各个模块之间的关系,高屋建瓴,从整体到局部才能透彻理解其中各个模块的功能和背后的设计思路。 网上有太多文章讲executors框架,要么泛泛而谈,要么一叶障目不见泰山,缺乏整体视角,很多根本没有理解整个框架的设计思想和模块关系。本文将对整个executors框架做综述,介绍各个模块的功能和联系,后续再深入探讨每个模块,包括模块中的各个工具类。 从Executor谈起 Executor是JDK1.5时,随着J.U.C引入的一个接口,引入该接口的主要目的是解耦任务本身和任务的执行。我们之前通过线程执行一个任务时,往往需要先创建一个线程,然后调用线程的start方法来执行任务: newThread(new(RunnableTask())).start(); 上述RunnableTask是实现了Runnable接口的任务类。而Executor接口解耦了任务和任务的执行,该接口只有一个方法,入参为待执行的任务: public interface Executor { /** * 执行给定的Runnable任务. * 根据Executor的实现不同, 具体执行方式也不相同. * * @param command the runnable task * @throws RejectedExecutionException if this task cannot be accepted for execution * @throws NullPointerException if command is null */ void execute(Runnable command); } 我们可以像下面这样执行任务,而不必关心线程的创建: Executor executor = someExecutor; // 创建具体的Executor对象 executor.execute(new RunnableTask1()); executor.execute(new RunnableTask2()); ... 由于Executor仅仅是一个接口,所以根据其实现的不同,执行任务的具体方式也不尽相同,比如: ①同步执行任务 class DirectExecutor implements Executor { public void execute(Runnable r) { r.run(); } } DirectExecutor是一个同步任务执行器,对于传入的任务,只有执行完成后execute才会返回。 ②异步执行任务 /** Fallback if ForkJoinPool.commonPool() cannot support parallelism */ static final class ThreadPerTaskExecutor implements Executor { public void execute(Runnable r) { new Thread(r).start(); } } ThreadPerTaskExecutor是一个异步任务执行器,对于每个任务,执行器都会创建一个新的线程去执行任务。 注意:Java线程与本地操作系统的线程是一一映射的。Java线程启动时会创建一个本地操作系统线程;当该Java线程终止时,对应操作系统线程会被回收。由于CPU资源是有限的,所以线程数量有上限,所以一般由线程池来管理线程的创建/回收,而上面这种方式其实是线程池的雏形。 ③对任务进行排队执行 class SerialExecutor implements Executor { final Queue<Runnable> tasks = new ArrayDeque<Runnable>(); final Executor executor; Runnable active; SerialExecutor(Executor executor) { this.executor = executor; } public synchronized void execute(final Runnable r) { tasks.offer(new Runnable() { public void run() { try { r.run(); } finally { scheduleNext(); } } }); if (active == null) { scheduleNext(); } } protected synchronized void scheduleNext() { if ((active = tasks.poll()) != null) { executor.execute(active); } } } SerialExecutor 会对传入的任务进行排队(FIFO顺序),然后从队首取出一个任务执行。 以上这些示例仅仅是给出了一些可能的Executor实现,J.U.C包中提供了很多Executor的具体实现类,我们以后会具体讲到,这里关键是理解Executor的设计思想——对任务和任务的执行解耦。 增强的Executor——ExecutorService Executor接口提供的功能很简单,为了对它进行增强,J.U.C又提供了一个名为ExecutorService接口,ExecutorService也是在JDK1.5时,随着J.U.C引入的: public interface ExecutorService extends Executor 可以看到,ExecutorService继承了Executor,它在Executor的基础上增强了对任务的控制,同时包括对自身生命周期的管理,主要有四类: 关闭执行器,禁止任务的提交; 监视执行器的状态; 提供对异步任务的支持; 提供对批处理任务的支持。 public interface ExecutorService extends Executor { /** * 关闭执行器, 主要有以下特点: * 1. 已经提交给该执行器的任务将会继续执行, 但是不再接受新任务的提交; * 2. 如果执行器已经关闭了, 则再次调用没有副作用. */ void shutdown(); /** * 立即关闭执行器, 主要有以下特点: * 1. 尝试停止所有正在执行的任务, 无法保证能够停止成功, 但会尽力尝试(例如, 通过 Thread.interrupt中断任务, 但是不响应中断的任务可能无法终止); * 2. 暂停处理已经提交但未执行的任务; * * @return 返回已经提交但未执行的任务列表 */ List<Runnable> shutdownNow(); /** * 如果该执行器已经关闭, 则返回true. */ boolean isShutdown(); /** * 判断执行器是否已经【终止】. * <p> * 仅当执行器已关闭且所有任务都已经执行完成, 才返回true. * 注意: 除非首先调用 shutdown 或 shutdownNow, 否则该方法永远返回false. */ boolean isTerminated(); /** * 阻塞调用线程, 等待执行器到达【终止】状态. * * @return {@code true} 如果执行器最终到达终止状态, 则返回true; 否则返回false * @throws InterruptedException if interrupted while waiting */ boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; /** * 提交一个具有返回值的任务用于执行. * 注意: Future的get方法在成功完成时将会返回task的返回值. * * @param task 待提交的任务 * @param <T> 任务的返回值类型 * @return 返回该任务的Future对象 * @throws RejectedExecutionException 如果任务无法安排执行 * @throws NullPointerException if the task is null */ <T> Future<T> submit(Callable<T> task); /** * 提交一个 Runnable 任务用于执行. * 注意: Future的get方法在成功完成时将会返回给定的结果(入参时指定). * * @param task 待提交的任务 * @param result 返回的结果 * @param <T> 返回的结果类型 * @return 返回该任务的Future对象 * @throws RejectedExecutionException 如果任务无法安排执行 * @throws NullPointerException if the task is null */ <T> Future<T> submit(Runnable task, T result); /** * 提交一个 Runnable 任务用于执行. * 注意: Future的get方法在成功完成时将会返回null. * * @param task 待提交的任务 * @return 返回该任务的Future对象 * @throws RejectedExecutionException 如果任务无法安排执行 * @throws NullPointerException if the task is null */ Future<?> submit(Runnable task); /** * 执行给定集合中的所有任务, 当所有任务都执行完成后, 返回保持任务状态和结果的 Future 列表. * <p> * 注意: 该方法为同步方法. 返回列表中的所有元素的Future.isDone() 为 true. * * @param tasks 任务集合 * @param <T> 任务的返回结果类型 * @return 任务的Future对象列表,列表顺序与集合中的迭代器所生成的顺序相同, * @throws InterruptedException 如果等待时发生中断, 会将所有未完成的任务取消. * @throws NullPointerException 任一任务为 null * @throws RejectedExecutionException 如果任一任务无法安排执行 */ <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException; /** * 执行给定集合中的所有任务, 当所有任务都执行完成后或超时期满时(无论哪个首先发生), 返回保持任务状态和结果的 Future 列表. */ <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException; /** * 执行给定集合中的任务, 只有其中某个任务率先成功完成(未抛出异常), 则返回其结果. * 一旦正常或异常返回后, 则取消尚未完成的任务. */ <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException; /** * 执行给定集合中的任务, 如果在给定的超时期满前, 某个任务已成功完成(未抛出异常), 则返回其结果. * 一旦正常或异常返回后, 则取消尚未完成的任务. */ <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; } 关于Future,其实就是Java多线程设计模式中 Future模式,后面我们会专门讲解J.U.C中的Future框架。 Future对象提供了对任务异步执行的支持,也就是说调用线程无需等待任务执行完成,提交待执行的任务后,就会立即返回往下执行。然后,可以在需要时检查Future是否有结果了,如果任务已执行完毕,通过 Future.get()方法可以获取到执行结果——Future.get()是阻塞方法。 周期任务的调度——ScheduledExecutorService 在工业环境中,我们可能希望提交给执行器的某些任务能够定时执行或周期性地执行,这时我们可以自己实现Executor接口来创建符合我们需要的类,Doug Lea已经考虑到了这类需求,所以在ExecutorService的基础上,又提供了一个接口——ScheduledExecutorService,该接口也是在JDK1.5时,随着J.U.C引入的: public interface ScheduledExecutorService extends ExecutorService ScheduledExecutorService提供了一系列schedule方法,可以在给定的延迟后执行提交的任务,或者每个指定的周期执行一次提交的任务,我们来看下面这个示例: public class TestSche { public static void main(String[] args) { //创建了一个ScheduledExecutorService 实例 ScheduledExecutorService executorService=new ScheduledThreadPoolExecutor(1); final ScheduledFuture<?>scheduledFuture=executorService.scheduleAtFixedRate(new BeepTask(),10,10 ,TimeUnit.SECONDS); executorService.schedule(new Runnable() { @Override public void run() { scheduledFuture.cancel(true); } },1,TimeUnit.HOURS); //1小时取消任务 } private static class BeepTask implements Runnable{ @Override public void run() { System.out.println("beep!..."); } } } 上述示例先创建一个ScheduledExecutorService类型的执行器,然后利用scheduleAtFixedRate方法提交了一个“蜂鸣”任务,每隔10s该任务会执行一次。 注意:scheduleAtFixedRate方法返回一个ScheduledFuture对象,ScheduledFuture其实就是在Future的基础上增加了延迟的功能。通过ScheduledFuture,可以取消一个任务的执行,本例中我们利用schedule方法,设定在1小时后,执行任务的取消。 ScheduledExecutorService完整的接口声明如下: public interface ScheduledExecutorService extends ExecutorService { /** * 提交一个待执行的任务, 并在给定的延迟后执行该任务. * * @param command 待执行的任务 * @param delay 延迟时间 * @param unit 延迟时间的单位 */ public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit); /** * 提交一个待执行的任务(具有返回值), 并在给定的延迟后执行该任务. * * @param command 待执行的任务 * @param delay 延迟时间 * @param unit 延迟时间的单位 * @param <V> 返回值类型 */ public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit); /** * 提交一个待执行的任务. * 该任务在 initialDelay 后开始执行, 然后在 initialDelay+period 后执行, 接着在 initialDelay + 2 * period 后执行, 依此类推. * * @param command 待执行的任务 * @param initialDelay 首次执行的延迟时间 * @param period 连续执行之间的周期 * @param unit 延迟时间的单位 */ public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit); /** * 提交一个待执行的任务. * 该任务在 initialDelay 后开始执行, 随后在每一次执行终止和下一次执行开始之间都存在给定的延迟. * 如果任务的任一执行遇到异常, 就会取消后续执行. 否则, 只能通过执行程序的取消或终止方法来终止该任务. * * @param command 待执行的任务 * @param initialDelay 首次执行的延迟时间 * @param delay 一次执行终止和下一次执行开始之间的延迟 * @param unit 延迟时间的单位 */ public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit); } 至此,Executors框架中的三个最核心的接口介绍完毕,这三个接口的关系如下图: 二、生产executor的工厂 通过第一部分的学习,读者应该对Executors框架有了一个初步的认识,Executors框架就是用来解耦任务本身与任务的执行,并提供了三个核心接口来满足使用者的需求: Executor:提交普通的可执行任务 ExecutorService:提供对线程池生命周期的管理、异步任务的支持 ScheduledExecutorService:提供对任务的周期性执行支持 既然上面三种执行器只是接口,那么就一定存在具体的实现类,J.U.C提供了许多默认的接口实现,如果要用户自己去创建这些类的实例,就需要了解这些类的细节,有没有一种直接的方式,仅仅根据一些需要的特性(参数)就创建这些实例呢?因为对于用户来说,其实使用的只是这三个接口。 JDK1.5时,J.U.C中还提供了一个Executors类,专门用于创建上述接口的实现类对象。Executors其实就是一个简单工厂,它的所有方法都是static的,用户可以根据需要,选择需要创建的执行器实例,Executors一共提供了五类可供创建的Executor执行器实例。 固定线程数的线程池 Executors提供了两种创建具有固定线程数的Executor的方法,固定线程池在初始化时确定其中的线程总数,运行过程中会始终维持线程数量不变。 可以看到下面的两种创建方法其实都返回了一个ThreadPoolExecutor实例。ThreadPoolExecutor是一个ExecutorService接口的实现类,我们会在后面用专门章节讲解,现在只需要了解这是一种Executor,用来调度其中的线程的执行即可。 /** * 创建一个具有固定线程数的Executor. */ public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } /** * 创建一个具有固定线程数的Executor. * 在需要时使用提供的 ThreadFactory 创建新线程. */ public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory); } 上面需要注意的是ThreadFactory这个接口: public interface ThreadFactory { Thread newThread(Runnable r); } 既然返回的是一个线程池,那么就涉及线程的创建,一般我们需要通过new Thread ()这种方法创建一个新线程,但是我们可能希望设置一些线程属性,比如 名称、守护程序状态、ThreadGroup 等等,线程池中的线程非常多,如果每个线程都这样手动配置势必非常繁琐,而ThreadFactory 作为一个线程工厂可以让我们从这些繁琐的线程状态设置的工作中解放出来,还可以由外部指定ThreadFactory实例,以决定线程的具体创建方式。 Executors提供了静态内部类,实现了ThreadFactory接口,最简单且常用的就是下面这个DefaultThreadFactory: /** * 默认的线程工厂. */ static class DefaultThreadFactory implements ThreadFactory { private static final AtomicInteger poolNumber = new AtomicInteger(1); private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; DefaultThreadFactory() { SecurityManager s = System.getSecurityManager(); group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-"; } public Thread newThread(Runnable r) { Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); if (t.isDaemon()) t.setDaemon(false); if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); return t; } } 可以看到,DefaultThreadFactory 初始化的时候定义了线程组、线程名称等信息,每创建一个线程,都给线程统一分配这些信息,避免了一个个手工通过new的方式创建线程,又可进行工厂的复用。 单个线程的线程池 除了固定线程数的线程池,Executors还提供了两种创建只有单个线程Executor的方法: /** * 创建一个使用单个 worker 线程的 Executor. */ public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } /** * 创建一个使用单个 worker 线程的 Executor. * 在需要时使用提供的 ThreadFactory 创建新线程. */ public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory)); } 可以看到,只有单个线程的线程池其实就是指定线程数为1的固定线程池,主要区别就是,返回的Executor实例用了一个FinalizableDelegatedExecutorService对象进行包装。 我们来看下FinalizableDelegatedExecutorService,该类 只定义了一个finalize方法: static class FinalizableDelegatedExecutorService extends DelegatedExecutorService { FinalizableDelegatedExecutorService(ExecutorService executor) { super(executor); } protected void finalize() { super.shutdown(); } } 核心是其继承的DelegatedExecutorService,这是一个包装类,实现了ExecutorService的所有方法,但是内部实现其实都委托给了传入的ExecutorService 实例: /** * ExecutorService实现类的包装类. */ /** * A wrapper class that exposes only the ExecutorService methods * of an ExecutorService implementation. */ static class DelegatedExecutorService extends AbstractExecutorService { private final ExecutorService e; DelegatedExecutorService(ExecutorService executor) { e = executor; } public void execute(Runnable command) { e.execute(command); } public void shutdown() { e.shutdown(); } public List<Runnable> shutdownNow() { return e.shutdownNow(); } public boolean isShutdown() { return e.isShutdown(); } public boolean isTerminated() { return e.isTerminated(); } public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { return e.awaitTermination(timeout, unit); } public Future<?> submit(Runnable task) { return e.submit(task); } public <T> Future<T> submit(Callable<T> task) { return e.submit(task); } public <T> Future<T> submit(Runnable task, T result) { return e.submit(task, result); } public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException { return e.invokeAll(tasks); } public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException { return e.invokeAll(tasks, timeout, unit); } public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException { return e.invokeAny(tasks); } public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { return e.invokeAny(tasks, timeout, unit); } } 为什么要多此一举,加上这样一个委托层?因为返回的ThreadPoolExecutor包含一些设置线程池大小的方法——比如setCorePoolSize,对于只有单个线程的线程池来说,我们是不希望用户通过强转的方式使用这些方法的,所以需要一个包装类,只暴露ExecutorService本身的方法。 可缓存的线程池 有些情况下,我们虽然创建了具有一定线程数的线程池,但出于资源利用率的考虑,可能希望在特定的时候对线程进行回收(比如线程超过指定时间没有被使用),Executors就提供了这种类型的线程池: /** * 创建一个可缓存线程的Execotor. * 如果线程池中没有线程可用, 则创建一个新线程并添加到池中; * 如果有线程长时间未被使用(默认60s, 可通过threadFactory配置), 则从缓存中移除. */ public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } /** * 创建一个可缓存线程的Execotor. * 如果线程池中没有线程可用, 则创建一个新线程并添加到池中; * 如果有线程长时间未被使用(默认60s, 可通过threadFactory配置), 则从缓存中移除. * 在需要时使用提供的 ThreadFactory 创建新线程. */ public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory); } 可以看到,返回的还是ThreadPoolExecutor对象,只是指定了超时时间,另外线程池中线程的数量在[0, Integer.MAX_VALUE]之间。 可延时/周期调度的线程池 如果有任务需要延迟/周期调用,就需要返回ScheduledExecutorService接口的实例,ScheduledThreadPoolExecutor就是实现了ScheduledExecutorService接口的一种Executor,和ThreadPoolExecutor一样,这个我们后面会专门讲解。 /** * 创建一个具有固定线程数的 可调度Executor. * 它可安排任务在指定延迟后或周期性地执行. */ public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); } /** * 创建一个具有固定线程数的 可调度Executor. * 它可安排任务在指定延迟后或周期性地执行. * 在需要时使用提供的 ThreadFactory 创建新线程. */ public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) { return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory); } Fork/Join线程池 Fork/Join线程池是比较特殊的一类线程池,在JDK1.7时才引入,其核心实现就是ForkJoinPool类。关于Fork/Join框架,我们后面会专题讲解,现在只需要知道,Executors框架提供了一种创建该类线程池的便捷方法。 /** * 创建具有指定并行级别的ForkJoin线程池. */ public static ExecutorService newWorkStealingPool(int parallelism) { return new ForkJoinPool(parallelism, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true); } /** * 创建并行级别等于CPU核心数的ForkJoin线程池. */ public static ExecutorService newWorkStealingPool() { return new ForkJoinPool(Runtime.getRuntime().availableProcessors(), ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true); } 三、总结 至此,Executors框架的整体结构基本就讲解完了,此时我们的脑海中应有大致如下的一幅类继承图: 下面来回顾一下,上面的各个接口/类的关系和作用: Executor 执行器接口,也是最顶层的抽象核心接口, 分离了任务和任务的执行。 ExecutorService 在Executor的基础上提供了执行器生命周期管理,任务异步执行等功能。 ScheduledExecutorService 在ExecutorService基础上提供了任务的延迟执行/周期执行的功能。 Executors 生产具体的执行器的静态工厂 ThreadFactory 线程工厂,用于创建单个线程,减少手工创建线程的繁琐工作,同时能够复用工厂的特性。 AbstractExecutorService ExecutorService的抽象实现,为各类执行器类的实现提供基础。 ThreadPoolExecutor 线程池Executor,也是最常用的Executor,可以以线程池的方式管理线程。 ScheduledThreadPoolExecutor 在ThreadPoolExecutor基础上,增加了对周期任务调度的支持。 ForkJoinPool Fork/Join线程池,在JDK1.7时引入,时实现Fork/Join框架的核心类。 关于ThreadPoolExecutor和ScheduledThreadPoolExecutor,我们会在下一章详细讲解,帮助读者理解线程池的实现原理。至于ForkJoinPool,涉及Fork/Join这个并行框架的讲解,我们后面会专题介绍。 本文参考 https://segmentfault.com/a/1190000016586578#articleHeader10

优秀的个人博客,低调大师

Java并发编程笔记1-竞争条件&初识原子类&可重入锁

我们知道,在多线程访问一个共享变量的时候会发生安全问题。 首先看下面例子: public class Counter { private int count; public void add(){ try{ for (int i = 0;i<200;i++){ Thread.sleep(100); this.count++; System.out.println(this.count); } }catch (Exception e ){ e.printStackTrace(); } } } public class Test { public static void main(String[] args) { final Counter counter = new Counter(); new Thread(new Runnable() { public void run() { counter.add(); } }).start(); new Thread(new Runnable() { public void run() { counter.add(); } }).start(); new Thread(new Runnable() { public void run() { counter.add(); } }).start(); } } 运行结果如下: 如果没发生线程对数据的赃操作,3个线程执行,结果会累加到600的,看上面结果明显没有,并且出现一下重复的数据。这是因为存在3个线程同时操作同一个数字,导致输出重复数字。 解决办法: 1.在方法上加上synchronized关键字。如下图: 虽然结果不会出现重复,但是synchronized效率及其低下,运行速度明显被拖慢。原因是,for循环中的i变量是每个线程都有独自的局部变量,各不影响,但是每个线程都要排队进入这个方法,排队睡觉,这样就导致效率低下 2.在竞争条件上加synchronized,我们知道各个线程其实竞争的是count这个成员变量。因此在此地方加即可。如下图: 这样运行效率比方法一快了很多,因为省去了排队进入方法,排队睡觉。只需要排队取count值即可,这样效率比方法一快。 注意上图中,输出语句并不是竞争条件,并不一定要放在synchroized里面,这里放在里面是为了让线程取到值自增后立即输出,这样输出就不会发生混论,不发生抢占输出问题,一样能累加到600, 如果把输出放在synchronized外面会出现值有重复现象,因为累加后的值并没有立即输出,这样导致输出混乱,但仍然能加到600.知识输出不安全罢了。 3.使用原子类型,比如将上面的代码的int count类型改成AtomicInteger count类型,我们知道获取count的值然后再自加个1是可能会出现问题的,也就是结果出现重复数字。AtomicInteger类型是以同步的方法解决这个问题的。如下图: 结果如下图: 可以看到数字的输出没有严格的排队,但是数据确实给你保证的了,就是完整的加到600.这也恰恰因为不是严格的进行排队,才是的这种方法比前面两种方法的效率大大改进。 理论总结:synchronized是一种内部锁,就是所对象内部给我们提供的,因为每一个对象有一个状态变量,相当于一个锁,进入同步块,改变这个变量。别的线程进入之后就要判断这个变量有没有改变。 一个线程获取它本生已经持有的锁,这是可以成功的。我们知道多个线程同时抢占同一个锁它们是失败的。因为它们之间是互斥的。但是呢,一个线程再次获取一个自己已经拿过的锁是可以成功的,那么它是能够成功的。 看如下例子: public class Widget { public synchronized void doSth(){ } } public class ChildWidget extends Widget { @Override public synchronized void doSth() { super.doSth(); } } public class Test { public static void main(String[] args) { Widget w = new ChildWidget(); w.doSth(); } } 子类调用自己的方法的那个synchroized那个锁是w对象的这个锁,而在子类方法中的super.doSth()父类中的方法的synchronized的锁也是w对象的锁。因此不要被子类继承父类的方法中的锁所迷惑了。 因此这种方式叫做内部锁的可重入机制,也叫可重入锁。

优秀的个人博客,低调大师

🔥🔥🔥一款兼具 “高性能、颜值、活跃” 的分布式任务调度与重试平台 Beta2 版本正式发布

🔥🔥🔥 灵活,可靠和快速的分布式任务重试和分布式任务调度平台 ✅️ 可重放,可管控、为提高分布式业务系统一致性的分布式任务重试平台 ✅️ 支持秒级、可中断、可编排的高性能分布式任务调度平台 特别声明 📢特别声明 为了促进系统的更好发展,我们正式宣布自本版本起,EasyRetry将更名为 SnailJob,并从版本号 1.0.0 开始,请大家尽快迁移至 SnailJob。由于包路径和表结构的调整,新版本将不再兼容之前的EasyRetry版本,EasyRetry版本会继续做 BUG 修复。对此给大家带来的不便,我们深表歉意,并感谢大家的理解与支持 项目特性 易用性 业务接入成本小。避免依赖研发人员的技术水平,保障稳定性 灵活性 能够动态调整配置,启动 / 停止任务,以及终止运行中的任务 操作简单 分钟上手,支持 WEB 页面对任务数据 CRUD 操作。 数据大盘 实时管控系统任务数据 分布式重试任务 支持多样化退避策略、多样化重试类型、流量管控等 分布式调度任务 提供丰富的任务触发策略、任务分片、停止恢复、失败重试等 工作流任务编排 仿钉钉设计的流程编排引擎,支持复杂的功能编排、失败重试、告警等 任务数据管理 可以做到数据不丢失、数据一键回放 容器化部署 服务端支持 docker 容器部署 高性能调度平台 支持服务端节点动态扩容和缩容 支持多样化的告警方式 邮箱、企业微信、钉钉、飞书、自定义告警 支持多种流行数据库 mysql、mariadb、sqlserver、oracle、postgres 数据库 开源组件对比 项目 Quartz Elastic-Job XXL-JOB Snail Job 定时调度 Cron Cron Cron 1. 定时任务 2. 秒级任务(无需依赖外部中间件) 3. 固定频率 重试任务 不支持 不支持 不支持 1.支持本地&远程重试模式 2.支持各种常用组件的重试 比如dubbo/feign 3.支持多种退避策略 4.丰富的重试风暴管控手段 ...... 任务编排 不支持 不支持 不支持 可视化任务编排 分布式计算 不支持 静态分片 广播 1. 广播执行 2. 集群执行 3. 静态分片 4. 动态分片(开发中) 多语言 Java 1. Java 2. 脚本任务 1. Java 2. 脚本任务 1. Java(已支持) 2. 脚本任务(开发中) 3. HTTP任务(开发中) 4. Kettle(开发中) 可视化 无 弱 1. 历史记录 2. 运行日志(不支持存储)3. 监控大盘 1. 历史记录 2. 运行日志(支持持久化) 3. 监控大盘 4. 操作记录 5. 查看日志堆栈 可运维 无 启用、禁用任务 1. 启用、禁用任务 2. 手动运行任务 3. 停止任务 1. 启用、禁用任务 2. 手动运行任务 3. 停止任务 报警监控 无 邮件 邮件 1. 邮件 2. 钉钉 3. 企微 4. 飞书 5、Webhook 性能 每次调度通过DB抢锁,对DB压力大 ZooKeeper是性能瓶颈 采用Master节点调度,Master节点压力大 系统采用多bucket模式,借助负载均衡算法,确保每个节点能够均衡处理任务,同时支持无限水平扩展,轻松应对海量任务调度 接入成本 只依赖DB接入成本低 需引入Zookeeper增加系统复杂性和维护成本 只依赖DB接入成本低 只依赖DB接入成本低 更新日志 1. 定时任务支持超时检查机制 2. 工作流执行超时检查机制 3. 组名称和重试场景名称支持短横线(-)格式 4. 调整定时任务、重试场景、通知场景配置的表单布局 5. 每次“编辑”定时任务,cron表达式就会被重置成默认值 6. 修复工作流任务显示不正确 7. 修复clearOfflineNode出现死锁 8. 优化其他已知问题 核心功能 项目地址 官网: https://snailjob.opensnail.com/ gitee: https://gitee.com/aizuda/snail-job github:https://github.com/aizuda/snail-job 体验地址:https://snailjob.opensnail.com/docs/preview.html 方便的话给项目一个 star,你的支持是我们前进的动力! 先睹为快

优秀的个人博客,低调大师

🔥🔥🔥一款兼具 “高性能、颜值、活跃” 的分布式任务调度与重试平台 Beta1 版本正式发布

🔥🔥🔥 灵活,可靠和快速的分布式任务重试和分布式任务调度平台 一文读懂 AizuDa/EasyRetry > ✅️ 可重放,可管控、为提高分布式业务系统一致性的分布式任务重试平台 > ✅️ 支持秒级、可中断、可编排的高性能分布式任务调度平台 📢特别声明 为了促进系统的更好发展,我们正式宣布自本版本起,EasyRetry将更名为SnailJob,并从版本号1.0.0开始,请大家尽快迁移至SnailJob。由于包路径和表结构的调整,新版本将不再兼容之前的EasyRetry版本, EasyRetry版本会继续做BUG修复。对此给大家带来的不便,我们深表歉意,并感谢大家的理解与支持。 易用性业务接入成本小。避免依赖研发人员的技术水平,保障稳定性 灵活性能够动态调整配置,启动 / 停止任务,以及终止运行中的任务 操作简单一分钟上手,支持 WEB 页面对任务数据 CRUD 操作。 数据大盘实时管控系统任务数据 分布式重试任务支持多样化退避策略、多样化重试类型、流量管控等 分布式调度任务提供丰富的任务触发策略、任务分片、停止恢复、失败重试等 工作流任务编排仿钉钉设计的流程编排引擎,支持复杂的功能编排、失败重试、告警等 任务数据管理可以做到数据不丢失、数据一键回放 容器化部署服务端支持 docker 容器部署 高性能调度平台支持服务端节点动态扩容和缩容 支持多样化的告警方式邮箱、企业微信、钉钉、飞书、自定义告警 支持多种流行数据库mysql、mariadb、sqlserver、oracle、postgres 数据库 开源组件对比 项目 Quartz Elastic-Job XXL-JOB SnailJob 定时调度 Cron Cron Cron 1. 定时任务 2. 秒级任务 (无需依赖外部中间件) 3. 固定频率 重试任务 不支持 不支持 不支持 1. 支持本地 & 远程重试模式 2. 支持各种常用组件的重试 比如 dubbo/feign 3. 支持多种退避策略 4. 丰富的重试风暴管控手段 ...... 任务编排 不支持 不支持 不支持 可视化任务编排 分布式计算 不支持 静态分片 广播 1. 广播执行 2. 集群执行 3. 静态分片 多语言 Java 1. Java 2. 脚本任务 1. Java 2. 脚本任务 1. Java 2. 脚本任务 3. HTTP 任务 4. Kettle 可视化 无 弱 1. 历史记录 2. 运行日志(不支持存储)3. 监控大盘 1. 历史记录 2. 运行日志 (支持持久化) 3. 监控大盘 4. 操作记录 5. 查看日志堆栈 可运维 无 启用、禁用任务 1. 启用、禁用任务 2. 手动运行任务 3. 停止任务 1. 启用、禁用任务 2. 手动运行任务 3. 停止任务 报警监控 无 邮件 邮件 1. 邮件 2. 钉钉 3. 企微 4. 飞书 性能 每次调度通过 DB 抢锁,对 DB 压力大 ZooKeeper 是性能瓶颈 采用 Master 节点调度,Master 节点压力大 系统采用多 bucket 模式,借助负载均衡算法,确保每个节点能够均衡处理任务,同时支持无限水平扩展,轻松应对海量任务调度 接入成本 只依赖 DB 接入成本低 需引入 Zookeeper 增加系统复杂性和维护成本 只依赖 DB 接入成本低 只依赖 DB 接入成本低 项目地址 一文读懂 AizuDa/EasyRetry 一文读懂 AiZuDa/EasyRetry 重试组件的意义 官网:https://snailjob.opensnail.com/ gitee:https://gitee.com/aizuda/snail-job github:https://github.com/aizuda/snail-job 体验地址:http://preview.easyretry.com/ 账号: developer 密码: 123456 方便的话给项目一个 star,你的支持是我们前进的动力!

优秀的个人博客,低调大师

Altman 以访客身份回到 OpenAI,和公司管会面谈判

据 The Information 报道,OpenAI 首席战略官 Jason Kwon 在员工备忘录中表示,上周五离职的 Sam Altman 等高管或将会回到公司。 而 Altman 本人也在周一推文写道“这是我第一次也是最后一次带这玩意”,配图是他手持 OpenAI 访客工牌的自拍,表示他持访客证明造访了 OpenAI 总部,与公司董事会讨论某事。 有消息称,包括微软在内的投资者正在向 OpenAI 董事会施压,要求他们同意 Sam Altman 等离职高管回归 OpenAI 工作。 目前尚不能确定 Altman 等人是否会回到 OpenAI 继续工作。有消息称,Altman 正计划同前 OpenAI 总裁 Greg Brockman 一起成立一间新的 AI 公司。

优秀的个人博客,低调大师

颜值 Linux 发行版 Solus 4.4 发布,支持安全启动

Solus 4.4 版本已发布。 Solus 4.4 版本被称为“Harmony”,由 Linux 内核 6.3 提供支持,具有安全启动支持、Intel Arc 支持、对各种光传感器和加速计的更好支持,以及对 AMD Radeon RX 7600、7900 XT 和 7900 XTX GPU 的支持,支持 NVIDIA 40 系列 GPU,并支持配备 ATH11K Wi-Fi 卡的笔记本电脑。 此版本中对内核进行了调整,默认启用 zram,为具有 3GB 或更少 RAM 的计算机的用户提供更好的体验。此外,Solus 内核默认启用CONFIG_NFT_FIB_INET选项,以允许 Firewalld/nftables 防火墙工作。 Budgie 桌面 Solus 4.4 的旗舰版使用最新的 Budgie 10.7 桌面环境,默认使用 Cinnamon 的 Nemo 文件管理器而不是 GNOME 的 Nautilus、提供 Budgie 菜单中的双 GPU 支持、通知声音、新的 Budgie 屏幕截图应用程序以及新的电源对话框会话管理。 GNOME Solus 4.4 GNOME 版附带最新的 GNOME 43 系列版本 43.5。 默认启用深色模式 将默认 shell 主题更改为 Adwaita 将默认 GTK 主题更改为 Adwaita-Dark MATE 桌面 Solus 4.4 MATE 版附带最新的 MATE 1.27 系列版本 1.27.1。 未来MATE 版本可能换成Xfce 版本 Plasma Solus 4.4 Plasma Edition 附带最新的 Plasma Desktop 5.27.5、KDE ​​Frameworks 5.106.0、KDE ​​Gear 23.04.2 以及 QT 5.15.9 的 KDE 分支 更多详情查看发行公告:https://getsol.us/2023/07/08/solus-4-4-released/

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

用户登录
用户注册