学习中记录差异—java和golang并发的不同点

后续根据学习的深入会再更新理解

本文打算从三点进行阐述

1.java的并发机制原理
2.golang的并发机制原理
3.两者有什么不同,导致了什么问题

1.java的并发机制原理

Synchronized

线程安全的主要诱因:

存在共享数据(临界资源)

存在多条线程共同操作这些共享数据,读写操作时发生指令交错

例如:两个线程对同一个变量进行i++和i–操作,因为每一个操作都会被编译为多条字节码,字节码运行完之后,再回写到主内存的变量中,而多线程的指令会被CPU交错执行导致线程不安全。
学习中记录差异—java和golang并发的不同点_第1张图片

a.有点需要理解的东西
CPU访问存储的方式——多级存储;
CPU执行指令的方式——乱序

下文找到对应的例子体会===会更加清楚

b.感谢老铁的科普(我觉得讲的挺好的文章)
https://blog.csdn.net/fct2001140269/article/details/82634240

c.总结:
存储访问引起的不一致性+CPU为了提高效率引入的并行机制就是并发程序设计的困难,这两个问题结合在一起就是“Memory barrier”(内存屏障、内存栅栏),这不是Java独有的,在任何编程语言中都会存在这个问题,除非你的CPU不是多级存储、没有流水线。

2.golang的并发机制原理

Go 提供了一个更加优雅的解决方案,那就是 channel。
channel 应用

Go 与 Java 的一个很大的区别就是并发模型不同,Go 采用的是 CSP(Communicating sequential processes) 模型;用 Go 官方的说法:

Do not communicate by sharing memory; instead, share memory by communicating.

翻译过来就是:不用使用共享内存来通信,而是用通信来共享内存。

而这里所提到的通信,在 Go 里就是指代的 channel。

a.对于java 不一样的多级存储,go用的是通道channel

自己写了个demo体会一下:

package main

import (
	"fmt"
	"time"
)

//main()相当于是主协程
func main() {
	//匿名子协程
	go func() {
		i := 0
		for {
			if i < 5 {
				i++
				fmt.Println("子协程 i=", i)
				time.Sleep(1 * time.Second)
			}

		}
	}()
	i := 0
	for {
		i++
		fmt.Println("主协程 i=", i)
		time.Sleep(1 * time.Second)
		//主协程第二次后退出
		if i == 10 {
			break
		}
	}
}

这里面主协程的i就会影响到子协程的资源,相同的情况下Java是不会相互影响的

b.golang的channel原理
学习中记录差异—java和golang并发的不同点_第2张图片

在第 1 步,两个 goroutine 都到达通道,但哪个都没有开始执行发送或者接收。
在第 2 步,左侧的 goroutine 将它的手伸进了通道,这模拟了向通道发送数据的行为。这时,这个 goroutine 会在通道中被锁住,直到交换完成。
在第 3 步,右侧的 goroutine 将它的手放入通道,这模拟了从通道里接收数据。这个 goroutine 一样也会在通道中被锁住,直到交换完成。
在第 4 步和第 5 步,进行交换。
在第 6 步,两个 goroutine 都将它们的手从通道里拿出来,这模拟了被锁住的 goroutine 得到释放。两个 goroutine 现在都可以去做别的事情了。

3.两者有什么不同,导致了什么问题

  • 创建了一线程并启动,在java中main也是用户线程,main结束不代表所有用户线程结束,但main不结束,jvm一定不会退出,当不存在用户线程时,jvm进程就退出了(不管main函数所在线程是否已经结束);在Golang中,通过go关键字开启一个协程,执行匿名函数里面的内容,这里需要注意main函数所在线程需要休眠以下,以便等开启的协程执行,这是因为go中只要main函数线程退出则进程就退出。

    在java中创建的线程与os线程一一对应,所以java中的每个线程占用一个时间片来运行;而go中多个协程对应一个os 线程,也就是多个协程对应了一个时间片,go则使用自己的调度策略(非os的调度策略)来让多个协程使用一个时间片来并发的运行(这也是go的并发编程优势)。

    对于锁而言,java中的锁是synchronized或者ReentrantLock,java中synchronized关键字对不同对象使用有不同的效果;对于go来说锁只有一个:sync.Mutex,ync.Mutex是不可重入的锁,多次对sync.Mutex加锁会导致死锁。

    并发同步编程,Java主要依靠wait/notify等方法来实现;而go则是使用chanel来进行协程之间的通信实现。(可以通过两个语言的生产者和消费者设计模式的实现去体会。)

感谢博主的图:https://blog.csdn.net/weixin_39189376/article/details/106179662

你可能感兴趣的:(golang,golang,java)