1.java的并发机制原理
2.golang的并发机制原理
3.两者有什么不同,导致了什么问题
a.有点需要理解的东西:
CPU访问存储的方式——多级存储;
CPU执行指令的方式——乱序
下文找到对应的例子体会===会更加清楚
b.感谢老铁的科普(我觉得讲的挺好的文章)
https://blog.csdn.net/fct2001140269/article/details/82634240
c.总结:
存储访问引起的不一致性+CPU为了提高效率引入的并行机制就是并发程序设计的困难,这两个问题结合在一起就是“Memory barrier”(内存屏障、内存栅栏),这不是Java独有的,在任何编程语言中都会存在这个问题,除非你的CPU不是多级存储、没有流水线。
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原理
在第 1 步,两个 goroutine 都到达通道,但哪个都没有开始执行发送或者接收。
在第 2 步,左侧的 goroutine 将它的手伸进了通道,这模拟了向通道发送数据的行为。这时,这个 goroutine 会在通道中被锁住,直到交换完成。
在第 3 步,右侧的 goroutine 将它的手放入通道,这模拟了从通道里接收数据。这个 goroutine 一样也会在通道中被锁住,直到交换完成。
在第 4 步和第 5 步,进行交换。
在第 6 步,两个 goroutine 都将它们的手从通道里拿出来,这模拟了被锁住的 goroutine 得到释放。两个 goroutine 现在都可以去做别的事情了。
创建了一线程并启动,在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