说明:Java & Go 并发编程序列的文章,根据每篇文章的主题或细分标题,分别演示 Java 和 Go 语言当中的相关实现。更多该系列文章请查看:Java & Go 并发编程系列
说到 Go 语言的并发编程,就不得不说 Go 语言自带的支持并发安全的数据类型channel(通道),这种类型理解和使用起来非常简单,同时也是 Go 语言并发编程思想的重要体现。
一个 channel 相当于一个先进先出的队列,支持发送和接收操作,用<-
表示,箭头的方向代表数据的传输方向,并且发送操作之间是互斥的,接收操作之间也是互斥的。使用 channel 可以很方便的在多个 Goroutine(用户级线程,也称协程)之间进行通信。
channel 分为缓冲通道和非缓冲通道(容量为0)。
本文将从以下维度做一个类比。以下工具都是并发安全的,其中SynchronousQueue 和 LinkedBlockingQueue 均实现了 BlockingQueue 接口。
零容量 | 有限容量 | |
---|---|---|
Go | unbuffered channel | buffered channel |
Java | SynchronousQueue | LinkedBlockingQueue |
SynchronousQueue VS 非缓冲通道
Go 语言的非缓冲通道,只有在发送操作和接收操作配对上了,发送方和接收方才能得以继续执行,否则将会阻塞在发送或者接收操作。本质上就是以同步的方式来传递数据。这正是 Java 中的 SynchronousQueue 具有的特性。
代码场景:以下代码模拟 A 和 B 两个人参与一项接力任务,由 A 把接力棒交给 B。先用 Java 的方式实现,然后是 Go 语言来实现。
「Java」SynchronousQueue
SynchronousQueue 默认的构造函数,是基于非公平的访问策略。通过指定入参为 true,表示基于公平的访问策略,即最早被阻塞的线程会先获得执行的机会。这也更符合 Go 语言 channel 的特性。
// 注意这里声明了 fair 参数为 true
SynchronousQueue<Integer> queue = new SynchronousQueue<>(true);
// 新起一个线程代表接力成员A
new Thread(() -> {
System.out.printf