摸鱼的杰哥
这个作者很懒,什么都没留下…
展开
-
一文秒懂 Java wait() 和 notify() 方法
但,有一点要注意的是,对于任意一个线程,但在我们允许其继续执行之前,请始终快速检查继续执行该线程所需的条件。对于在此对象的监视器上等待的所有线程(通过使用任何一个重载 wait() 方法 ),notify() 通知将会随机唤醒任何一个线程。本文,我们来讲解下 Java 并发中的基础的基础,核心的核心,Java 并发编程中的最基本的机制之一 – 「虽然从上图中可以看出,有多个方法可以控制一个线程的生命周期,但本章节,我们只讨论。线程同步的问题,我们已经有了个大概的了解,接下来,我们看一个简单的。原创 2023-10-10 09:17:16 · 89 阅读 · 0 评论 -
一文秒懂 Java 之 Runnable 还是 Thread ?
看起来感觉是不是有点复杂,我们只想在单独的线程中运行单个日志操作而已,使用 Thread 的方式看起来有点复杂化了,要么是。为了运行这个任务,有多种方式可供选择,其中之一,就是使用一个 Thread 类。写 Java 代码的时候,我们经常会有这样的疑问:我到底是实现一个。实现了一个接口,因此,如果需要,我们可以自由扩展自另一个基类。只是一个任务,一个在一个单独的线程中运行的任务。同样多的代码,同样多的步骤。当然,这不是最糟糕的,更糟糕的是,所以,有了这些之后,你是怎么想的?更简单的是,一个几行代码的。原创 2023-10-10 08:52:35 · 54 阅读 · 0 评论 -
一文秒懂 Java Thread 生命周期
线程并不是一直都在执行的,调度器会把暂时空闲的线程的 CPU ( 还是在 RUNNABLE 状态 )让出来,让其它需要的线程去运行。如果是一个空转线程,除了 CPU 不需要其它资源,那么很大概率就是 RUNNABLE ,但如果需要其它资源,可能会因为竞争资源而处于其它状态。同样的,如果可以后退,那么我们在堵车的时候可能会等待那么十几分钟,发现确实走不了,就等了呗。虽然代码片段几乎可以在每台机器上提供相同的输出,但在某些特殊情况下,我们可能会得到一些不同的输出,因为线程调度程序的确切行为无法确定。原创 2023-10-09 19:17:18 · 99 阅读 · 0 评论 -
一文秒懂 Java ThreadLocalRandom
请注意,这是一个左闭右开区间,也就是说,上面的实例生成的随机数在 [0,100) 之间,包含了 0 但不包含 100。类全局的提供随机数生成, 使用 ThreadLocalRandom,一个线程获得的随机数不受另一个线程的影响。嗯,差距不是很大,但好歹也是有差距的,因为生成 1000 个随机数是瞬间的事情。记下来,我们看看如何在多线程环境中分别使用这两个类生成随机值,然后再使用 JMH 比较它们的性能。类用于生成随机数,而且呢,这个类也是线程安全的,就是有一点不好,在多线程下,它的性能不佳。原创 2023-10-09 19:01:54 · 81 阅读 · 0 评论 -
一文秒懂 Java java.util.concurrent.Future
用了相当长的篇幅,我们终于讲完了如何创建一个 Future 实例,接下来,我们将进入如何消费(使用) 刚刚创建的 Future 实例。方法有一个可以超时等待的重载版本,这个重载版本接收两个参数,一个是超时的时间,另一个是超时时间的单位。的实例,但最常用的,也是最推荐的做法是使用 Executors 的静态工厂方法。上面这段代码,我们在等待计算任务完成的同时执行了一条输出语句,用于提醒用户当前程序还是在运行的,并没有僵死。接口的对象,然后启动并发线程,并告诉并发线程,一旦你执行完毕,就把结果存储在这个。原创 2023-10-09 08:59:55 · 170 阅读 · 0 评论 -
一文秒懂 Java 守护线程 ( Daemon Thread )
由于守护线程的作用是为用户线程提供服务,并且仅在用户线程运行时才需要,因此一旦所有用户线程完成执行,JVM 就会终止。因此,我们可以推演出: 由于主线程是用户线程,因此在 main() 方法内创建的任何线程默认为用户线程。如果一个线程是普通线程( 用户线程) ,那么它创建的子线程默认也是普通线程( 用户线程 )。守护线程也是一个线程,因此它的创建和启动其实和普通线程没什么区别?如果一个线程是守护线程,那么它创建的子线程默认也是守护线程。咦,按照这个解释,那么大多数 JVM 线程都是守护线程。原创 2023-10-09 08:49:39 · 296 阅读 · 0 评论 -
一文秒懂 Java java.util.concurrent.Locks
除了 Lock 接口之外,java.util.concurrent.lock 包还提供了一个 ReadWriteLock 接口,俗称 「读写锁」,它维护一对锁,一个用于只读操作,一个用于写操作。它提供了相同的并发和内存语义,如使用 synchronized 方法和语句访问的隐式监视器锁,而且可以被子类化。不同的是,锁的获取方法返回的戳记 ( stamp ) 可以用于释放锁定或检查锁定是否仍然有效。相反,我们可以升级到读锁。从上面的对比来看,同步块的所有机制,锁 ( Lock ) 都有相应的 API 对应。原创 2023-07-05 16:37:32 · 164 阅读 · 0 评论 -
一文秒懂 Java BlockingQueue
我们将我们的毒 ( poison ) 丸 ( pill )消息指定为 Integer.MAX_VALUE,因为我们的生产者在正常工作条件下永远不会发送这样的值。这意味着当消费者尝试将元素添加到已经满了的队列时,结果取决于添加元素的方法( offer() 、add() 、put() ) ,它将阻塞,直到有足够的空间可以插入元素。使用有限队列是设计并发程序的好方法,因为当我们将元素插入到已经满了的队列时,这些操作需要等到消费者赶上并在队列中提供一些空间。既然我们有生产者和消费者,我们就可以开始我们的计划。原创 2023-07-05 16:23:14 · 107 阅读 · 0 评论 -
一文秒懂 Java CountDownLatch
然后,我们可以在每个线程完成后调用 countdown(),保证调用 await() 的依赖线程将阻塞,直到工作线程完成。我们重用前面的示例,但是这次开启了了数千个线程而不是 5 个线程,很可能许多早期的线程在后面的线程上调用 start() 之前已经完成了处理。简而言之,CountDownLatch 有一个计数器字段,我们可以根据需要减少它,因此,我们可以使用它来阻止调用线程,直到它被计数到零。接下来,我们修改下测试,直到所有工人都已启动,解锁工人,然后阻止,直到工人完成。原创 2023-07-05 16:15:01 · 117 阅读 · 0 评论 -
一文秒懂系列之 Java 并发编程面试题
对于给定的程序,我们可以观察到具有各种结果的多个不同的执行.但是如果一个程序正确同步,那么它的所有执行似乎都是顺序一致的,这意味着我们可以将多线程程序推断为一系列按顺序发生的动作。例如,一个线程在它持有的监视器上调用 Object.wait()> 法时进入此状态,或者在另一个线程上调用 Thread.join() 方法也会进入此状态。拥有对象监视器的线程( 例如,已进入由对象保护的同步部分的线程 )可以调用 object.wait() 来临时释放监视器并为其他线程提供获取监视器的机会。原创 2023-07-05 16:12:33 · 102 阅读 · 0 评论 -
一文秒懂 Java CompletableFuture
而该函数或表达式的参数则是先前计算步骤的结果,这允许我们在下一个 CompletableFuture 的 lambda 中使用这个值。两个方法都接收一个函数并将其应用于计算结果,但 thenCompose() ( flatMap() )方法接收一个函数,该函数返回相同类型的另一个对象,这样,就允许将这些类的实例组合为构建块。如果你已经知道计算的结果,可以将表示此计算的结果作为参数传递给 completedFuture() 静态方法,这样,Future 的 get() 方法永远不会阻塞,而是立即返回此结果。原创 2023-07-05 15:54:07 · 91 阅读 · 0 评论 -
一文秒懂 Java Google Guava 实现
例如,通过Futures.allAsList() 方法,我们可以在单个 ListenableFuture 中组合多个 ListenableFuture 实例,并会在这些实例在成功完成后将所有的 futures 合并并返回结果。下面的示例中,我们提交了一个无限循环的任务,我们使用了包含 100 毫秒超时时间的已经存在的执行程序服务来运行任务,并在超过配置的超时时间之后终止 VM。简单起见,提交的任务会将当前线程休眠 500 毫秒并阻塞当前线程,并在执行的调用完成后让结果立即可用。原创 2023-07-05 15:45:48 · 260 阅读 · 0 评论 -
一文秒懂 ScheduledThreadPoolExecutor
scheduleWithFixedDelay() 方法类似于 scheduleAtFixedRate() ,它也重复执行给定的任务,但period 参数用于指定前一个任务的结束和下一个任务的开始之间的间隔时间。scheduleAtFixedRate() 方法允许在指定的初始延迟后执行任务,然后以一定的周期重复执行,其中 period 参数用于指定两个任务的开始时间之间的间隔时间,因此任务执行的频率是固定的。下面的代码则演示了如何在 500 毫秒延迟后执行任务,然后每 100 毫秒重复执行一次。原创 2023-07-05 15:44:29 · 145 阅读 · 0 评论 -
一文秒懂 Java ForkJoinPool
因为,即使是使用一个简单的 ThreadPoolExecutor ,也会在不断的递归中快速耗尽线程。在 fork/join 框架中,任何任务都可以生成 ( fork ) 多个子任务并使用 join() 方法等待它们的完成。fork/join 框架的好处是它不会为每个任务或子任务创建新线程,而是实现了 工作窃取 (每个任务都接收自己的节点,并将其值添加到其子节点的值之和上。关于 fork/join 框架的详细信息,你可以访问我们的 一文秒懂 Java Fork/Join。接下来,我们看一个使用。原创 2023-07-05 15:43:55 · 136 阅读 · 0 评论 -
一文秒懂 Java 线程池之 ThreadPoolExecutor
创建的线程池由固定数量的核心线程组成,这些线程在 ThreadPoolExecutor 生命周期内始终存在,除此之外还有一些额外的线程可能会被创建,并会在不需要时主动销毁。例如,Executors.newFixedThreadPool() 静态方法创建了一个 ThreadPoolExecutor ,它的参数 corePoolSize 和 maximumPoolSize 都是相等的,且参数 keepAliveTime 始终为 0 ,也就意味着此线程池中的线程数始终相同。因此队列实际上从不包含任何内容。原创 2023-07-05 15:39:37 · 179 阅读 · 0 评论 -
一文秒懂 Java 线程池 ( Thread Pool )
Java 语言的实现中,把 Java 线程一一映射到操作系统级的线程,而后者是操作系统的资源,这意味着,如果开发者毫无节制地创建线程,那么线程资源就会被快速的耗尽。下面的示例中,我们创建了一个 ExecutorService 的实例,提交了一个任务,然后使用返回的 Future 的 get() 方法等待提交的任务完成并返回值。一台电脑,运行起来后,它的 CPU 是固定的,05 年之前,还是单核时代,也就是一次只能运行一个线程,虽然随着时间的推移,现在的 CPU。本章节涉及到很多前几个章节中阐述的知识点。原创 2023-07-05 15:31:20 · 136 阅读 · 0 评论 -
一文秒懂 Java Fork/Join
然后,join 部分开始工作,将所有子任务的结果递归地连接成单个结果,或者在返回 void 的任务的情况下,程序只是等待每个子任务执行完毕。但如果自己的双端队列中的任务已经执行完毕,双端队列为空时,工作线程就会从另一个忙线程的双端队列尾部或全局入口队列中获取任务,因为这是最大概率可能找到工作的地方。ForkJoinPool 线程池并不会为每个子任务创建一个单独的线程,相反,池中的每个线程都有自己的双端队列用于存储任务 ( double-ended queue )( 或 deque,发音 deck )。原创 2023-02-22 10:50:23 · 273 阅读 · 2 评论 -
一文秒懂 Java ExecutorService
创建完了任务之后,就可以使用多种方法将任务分配给 ExecutorService ,比如 execute() 方法,还有 submit()、invokeAny() 和 invokeAll() 等方法。这些 Future 接口的对象允许我们获取任务执行的结果或检查任务的状态 ( 是正在运行还是执行完毕 )。注意,这个 get() 重载方法,如果在超时时间内正常结束,那么返回的是 Future 类型的结果,如果超时了还没结束,那么将抛出 TimeoutException 异常。原创 2023-02-22 10:48:17 · 375 阅读 · 0 评论