自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(31)
  • 收藏
  • 关注

原创 线程池的声明需要手动进行

Java 中的 Executors 类定义了一些快捷的工具方法,来帮助我们快速创建线程池。《阿里巴巴 Java 开发手册》中提到,禁止使用这些方法来创建线程池,而应该手动 new ThreadPoolExecutor 来创建线程池。这一条规则的背后,是大量血淋淋的生产事故,最典型的就是 newFixedThreadPool 和 newCachedThreadPool,可能因为资源耗尽导致 OOM 问题。newFixedThreadPool 线程池的工作队列直接 new 了一个 LinkedBlocking

2021-02-18 11:21:47 196

原创 代码加锁

加锁前要清楚锁和被保护的对象是不是一个层面的除了没有分析清线程、业务逻辑和锁三者之间的关系随意添加无效的方法锁外,还有一种比较常见的错误是,没有理清楚锁和要保护的对象是否是一个层面的。我们知道静态字段属于类,类级别的锁才能保护;而非静态字段属于类实例,实例级别的锁就可以保护。加锁要考虑锁的粒度和场景问题在方法上加 synchronized 关键字实现加锁确实简单,也因此我曾看到一些业务代码中几乎所有方法都加了 synchronized,但这种滥用 synchronized 的做法:一是,没必要。通常情况下

2021-02-03 20:30:22 788

原创 ThreadLocal的坑

ThreadLocal 适用于变量在线程间隔离,而在方法或类间共享的场景。如果用户信息的获取比较昂贵(比如从数据库查询用户信息),那么在 ThreadLocal 中缓存数据是比较合适的做法。但有时候会出现用户信息错乱的 Bug 。因为线程的创建比较昂贵,所以 Web 服务器往往会使用线程池来处理请求,这就意味着线程会被重用。这时,使用类似 ThreadLocal 工具来存放一些数据时,需要特别注意在代码运行完后,显式地去清空设置的数据。如果在代码中使用了自定义的线程池,也同样会遇到这个问题。比如:@G

2021-01-29 10:28:33 378

原创 如何设置线程池的大小

一般多线程执行的任务类型可以分为 CPU 密集型和 I/O 密集型,根据不同的任务类型,我们计算线程数的方法也不一样。CPU 密集型任务:这种任务消耗的主要是 CPU 资源,可以将线程数设置为 N(CPU 核心数)+1,比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。I/O 密集型任务:这种任务应用起来,系统会用大部分的时间来处理 I/O 交互

2021-01-25 18:51:24 130

原创 如何优化 I/O 操作

使用缓冲区优化读写流操作在传统 I/O 中,提供了基于流的 I/O 实现,即 InputStream 和 OutputStream,这种基于流的实现以字节为单位处理数据。NIO 与传统 I/O 不同,它是基于块(Block)的,它以块为基本单位处理数据。在 NIO 中,最为重要的两个组件是缓冲区(Buffer)和通道(Channel)。Buffer 是一块连续的内存块,是 NIO 读写数据的中转地。Channel 表示缓冲数据的源头或者目的地,它用于读取缓冲或者写入数据,是访问缓冲的接口。传统 I/.

2021-01-13 11:26:23 259

原创 字符串性能优化

如何构建超大字符串?编程过程中,字符串的拼接很常见。前面我讲过 String 对象是不可变的,如果我们使用 String 对象相加,拼接我们想要的字符串,是不是就会产生多个对象呢?例如以下代码:String str= “ab” + “cd” + “ef”;分析代码可知:首先会生成 ab 对象,再生成 abcd 对象,最后生成 abcdef 对象,从理论上来说,这段代码是低效的。但实际运行中,我们发现只有一个对象生成,这是为什么呢?难道我们的理论判断错了?我们再来看编译后的代码,你会发现编译器自动.

2021-01-05 19:53:37 87

原创 协程:更轻量级的线程

Golang 中的协程在 Golang 中创建协程非常简单,在下面的示例代码中,要让 hello() 方法在一个新的协程中执行,只需要go hello(“World”) 这一行代码就搞定了。你可以对比着想想在 Java 里是如何“辛勤”地创建线程和线程池的吧,我的感觉一直都是:每次写完 Golang 的代码,就再也不想写 Java 代码了。import (“fmt”“time”)func hello(msg string) {fmt.Println("Hello " + msg)}func

2020-12-31 17:14:09 266

原创 Actor模型:面向对象原生的并发模型

Hello Actor 模型Actor 模型本质上是一种计算模型,基本的计算单元称为 Actor,换言之,在 Actor 模型中,所有的计算都是在 Actor 中执行的。在面向对象编程里面,一切都是对象;在 Actor 模型里,一切都是 Actor,并且 Actor 之间是完全隔离的,不会共享任何变量。当看到“不共享任何变量”的时候,相信你一定会眼前一亮,并发问题的根源就在于共享变量,而 Actor 模型中 Actor 之间不共享变量,那用 Actor 模型解决并发问题,一定是相当顺手。的确是这样,所以

2020-12-28 19:11:18 205

原创 经典限流算法:令牌桶算法

Guava 的限流器使用上还是很简单的,那它是如何实现的呢?Guava 采用的是令牌桶算法,其核心是要想通过限流器,必须拿到令牌。也就是说,只要我们能够限制发放令牌的速率,那么就能控制流速了。令牌桶算法的详细描述如下:令牌以固定的速率添加到令牌桶中,假设限流的速率是 r/ 秒,则令牌每 1/r 秒会添加一个;假设令牌桶的容量是 b ,如果令牌桶已满,则新的令牌会被丢弃;请求能够通过限流器的前提是令牌桶中有令牌。这个算法中,限流的速率 r 还是比较容易理解的,但令牌桶的容量 b 该怎么理解呢?b 其实

2020-12-16 14:29:45 1233

原创 生产者 - 消费者模式:支持分阶段提交以提升性能

利用生产者 - 消费者模式还可以轻松地支持一种分阶段提交的应用场景。我们知道写文件如果同步刷盘性能会很慢,所以对于不是很重要的数据,我们往往采用异步刷盘的方式。我曾经参与过一个项目,其中的日志组件是自己实现的,采用的就是异步刷盘方式,刷盘的时机是:ERROR 级别的日志需要立即刷盘;数据积累到 500 条需要立即刷盘;存在未刷盘数据,且 5 秒钟内未曾刷盘,需要立即刷盘。这个日志组件的异步刷盘操作本质上其实就是一种分阶段提交。下面我们具体看看用生产者 - 消费者模式如何实现。在下面的示例代码中,可以

2020-12-08 10:28:18 95

原创 生产者-消费者模式

生产者 - 消费者模式的优点生产者 - 消费者模式的核心是一个任务队列,生产者线程生产任务,并将任务添加到任务队列中,而消费者线程从任务队列中获取任务并执行。下面是生产者 - 消费者模式的一个示意图,你可以结合它来理解。生产者 - 消费者模式示意图从架构设计的角度来看,生产者 - 消费者模式有一个很重要的优点,就是解耦。解耦对于大型系统的设计非常重要,而解耦的一个关键就是组件之间的依赖关系和通信方式必须受限。在生产者 - 消费者模式中,生产者和消费者没有任何依赖关系,它们彼此之间的通信只能通过任务队

2020-12-02 17:34:45 502 1

原创 两阶段终止模式:如何优雅地终止线程?

两阶段终止模式:如何优雅地终止线程?Java 领域用的最多的还是线程池,而不是手动地创建线程。那我们该如何优雅地终止线程池呢?线程池提供了两个方法:shutdown()和shutdownNow()。这两个方法有什么区别呢?要了解它们的区别,就先需要了解线程池的实现原理。我们曾经讲过,Java 线程池是生产者 - 消费者模式的一种实现,提交给线程池的任务,首先是进入一个阻塞队列中,之后线程池中的线程从阻塞队列中取出任务执行。shutdown() 方法是一种很保守的关闭线程池的方法。线程池执行 shut

2020-11-25 16:47:29 148

原创 Thread-Per-Message模式:简单实用的分工方法

Thread-Per-Message 模式的一个最经典的应用场景是网络编程里服务端的实现,服务端为每个客户端请求创建一个独立的线程,当线程处理完请求后,自动销毁,这是一种最简单的并发处理网络请求的方法。网络编程里最简单的程序当数 echo 程序了,echo 程序的服务端会原封不动地将客户端的请求发送回客户端。例如,客户端发送 TCP 请求"Hello World",那么服务端也会返回"Hello World"。下面我们就以 echo 程序的服务端为例,介绍如何实现 Thread-Per-Message

2020-11-13 14:14:45 111

原创 Balking模式:线程安全的单例模式

Balking 模式的经典实现Balking 模式本质上是一种规范化地解决“多线程版本的 if”的方案,对于上面自动保存的例子,使用 Balking 模式规范化之后的写法如下所示,你会发现仅仅是将 edit() 方法中对共享变量 changed 的赋值操作抽取到了 change() 中,这样的好处是将并发处理逻辑和业务逻辑分开。boolean changed=false;//自动存盘操作void autoSave(){synchronized(this){if (!changed) {retu

2020-11-05 09:37:06 88

原创 Guarded Suspension模式:等待唤醒机制的规范实现

Guarded Suspension 模式项目组团建要外出聚餐,我们提前预订了一个包间,然后兴冲冲地奔过去,到那儿后大堂经理看了一眼包间,发现服务员正在收拾,就会告诉我们:“您预订的包间服务员正在收拾,请您稍等片刻。”过了一会,大堂经理发现包间已经收拾完了,于是马上带我们去包间就餐。我们等待包间收拾完的这个过程和小灰遇到的等待 MQ 返回消息本质上是一样的,都是等待一个条件满足:就餐需要等待包间收拾完,小灰的程序里要等待 MQ 返回消息。那我们来看看现实世界里是如何解决这类问题的呢?现实世界里大堂经理

2020-10-23 16:54:03 132

原创 ThreadLocal

ThreadLocal 的工作原理在解释 ThreadLocal 的工作原理之前, 你先自己想想:如果让你来实现 ThreadLocal 的功能,你会怎么设计呢?ThreadLocal 的目标是让不同的线程有不同的变量 V,那最直接的方法就是创建一个 Map,它的 Key 是线程,Value 是每个线程拥有的变量 V,ThreadLocal 内部持有这样的一个 Map 就可以了。你可以参考下面的示意图和示例代码来理解。ThreadLocal 持有 Map 的示意图class MyThreadLocal

2020-10-16 16:48:06 110

原创 Immutability模式:如何利用不变性解决并发问题?

使用 Immutability 模式的注意事项在使用 Immutability 模式的时候,需要注意以下两点:对象的所有属性都是 final 的,并不能保证不可变性;不可变对象也需要正确发布。在 Java 语言中,final 修饰的属性一旦被赋值,就不可以再修改,但是如果属性的类型是普通对象,那么这个普通对象的属性是可以被修改的。例如下面的代码中,Bar 的属性 foo 虽然是 final 的,依然可以通过 setAge() 方法来设置 foo 的属性 age。所以,在使用 Immutability

2020-10-12 08:58:44 262

原创 如何用多线程实现最优的“烧水泡茶”程序?

如何用多线程实现最优的“烧水泡茶”程序?

2020-09-30 10:47:58 198

原创 java并发编程实战-原子类

无锁方案的实现原理其实原子类性能高的秘密很简单,硬件支持而已。CPU 为了解决并发问题,提供了 CAS 指令(CAS,全称是 Compare And Swap,即“比较并交换”)。CAS 指令包含 3 个参数:共享变量的内存地址 A、用于比较的值 B 和共享变量的新值 C;并且只有当内存中地址 A 处的值等于 B 时,才能将内存中地址 A 处的值更新为新值 C。作为一条 CPU 指令,CAS 指令本身是能够保证原子性的。你可以通过下面 CAS 指令的模拟代码来理解 CAS 的工作原理。在下面的模拟程序中

2020-09-28 09:15:53 114

原创 并发容器:都有哪些“坑”需要我们填?

并发容器:都有哪些“坑”需要我们填?Java 中的容器主要可以分为四个大类,分别是 List、Map、Set 和 Queue,但并不是所有的 Java 容器都是线程安全的。例如,我们常用的 ArrayList、HashMap 就不是线程安全的。在介绍线程安全的容器之前,我们先思考这样一个问题:如何将非线程安全的容器变成线程安全的容器?下面我们就以 ArrayList 为例,看看如何将它变成线程安全的。在下面的代码中,SafeArrayList 内部持有一个 ArrayList 的实例 c,所有访问 c

2020-09-20 14:32:12 93

原创 Java并发编程实战-如何让多线程步调一致

CountDownLatch和CyclicBarrier:如何让多线程步调一致?

2020-09-14 09:13:49 101

原创 StampedLock:有没有比读写锁更快的锁?

对于读多写少的场景 StampedLock 性能很好,简单的应用场景基本上可以替代 ReadWriteLock,但是 StampedLock 的功能仅仅是 ReadWriteLock 的子集,在使用的时候,还是有几个地方需要注意一下。StampedLock 在命名上并没有增加 Reentrant,想必你已经猜测到 StampedLock 应该是不可重入的。事实上,的确是这样的,StampedLock 不支持重入。这个是在使用中必须要特别注意的。另外,StampedLock 的悲观读锁、写锁都不支持条件变

2020-09-04 09:53:39 92

原创 Java并发编程实战-ReadWriteLock:如何快速实现一个完备的缓存?

ReadWriteLock:如何快速实现一个完备的缓存?

2020-08-31 15:48:08 117

原创 Java并发编程实战-Semaphore

如何快速实现一个限流器?

2020-08-22 16:30:01 93

原创 Java并发编程实战-_ Lock和Condition

# _ Lock和Condition

2020-08-17 09:27:00 102

原创 java并发编程实战-Java线程

Java线程

2020-08-10 19:57:11 110

原创 java并发编程实战-管程

管程-并发编程的万能钥匙

2020-08-03 08:58:47 165

原创 java并发编程实战-互斥锁

解决原子性问题

2020-07-27 09:42:28 152

原创 java并发编程实战-Java内存模型

Java利用内存模型解决可见性和有序性问题

2020-07-18 17:09:30 113

原创 java并发编程实战之并发编程bug的源头

源头之一:缓存导致的可见性问题一个线程对共享变量的修改,另外一个线程能够立刻看到,我们称为可见性。多核时代,每颗 CPU 都有自己的缓存,这时 CPU 缓存与内存的数据一致性就没那么容易解决了,当多个线程在不同的 CPU 上执行时,这些线程操作的是不同的 CPU 缓存。比如下图中,线程 A 操作的是 CPU-1 上的缓存,而线程 B 操作的是 CPU-2 上的缓存,很明显,这个时候线程 A 对变量 V 的操作对于线程 B 而言就不具备可见性了。这个就属于硬件程序员给软件程序员挖的“坑”。源头之二:线程

2020-07-13 09:38:37 171

原创 netty初识

什么是netty?netty是jboss提供的一个java开源框架,netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可用性的网络服务器和客户端程序。也就是说netty是一个基于nio的编程框架,使用netty可以快速的开发出一个网络应用。由于java 自带的nio api使用起来非常复杂,并且还可能出现 Epoll Bug,这使得我们使用原生的nio来进行网络编程存在很大的难度且非常耗时。但是netty良好的设计可以使开发人员快速高效的进行网络应用开发。netty中的一些

2020-07-06 10:29:00 1187

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除