b2科目四模拟试题多少题驾考考爆了怎么补救
b2科目四模拟试题多少题 驾考考爆了怎么补救

java.util.concurrent并发工具包大简化Java并发编程

电脑杂谈  发布时间:2021-02-25 12:02:00  来源:网络整理

目录:

名词定义

Go或Java中的线程执行主体Goroutine

背景介绍

内存模型的目的是定义不同执行中变量读取和写入的可见性。了解内存模型在并发编程中非常重要,因为代码的执行顺序和编写的逻辑顺序将不完全相同。即使在编译期间,编译器也可能会重新排列代码以优化CPU执行,并且由于存在CPU缓存,内存中的数据可能无法及时更新,因此可能无法对内存中的同一变量进行读写操作。如预期的那样。

类似于Java的内存模型规范,Go语言也具有内存模型。与JMM相比,Go的内存模型相对简单。 Go的并发模型基于CSP(通信顺序过程)。不同的Goroutine传递。称为Channel的数据结构用于通信。 Java的并发模型基于多线程和共享内存,具有更多概念(违规,锁定,最终,构造,线程,原子等)和场景,当然还有java.util.concurrent并发工具该包大大简化了Java并发编程。

Go内存模型指定了条件,在该条件下,变量的Goroutine修改必须对其他Goroutine可见。

发生之前

在单个Goroutine中,变量的读取和写入与代码的写入顺序一致。例如,以下代码:

package main
import (
    "log"
)
var a, b, c int
func main() {
    a = 1
    b = 2
    c = a + 2
    log.Println(a, b, c)
}

尽管在编译和执行期间,编译器和CPU可能会重新排列代码,例如,先执行b = 2,然后执行a = 1,但是保证c = a + 2在a = 1后执行。这样,最终执行结果必须是1 2 3,而不是1 22。但是以下代码可能会输出0 0 0、1 2 2、0 2 3(b = 2在a = 1之前执行),1 2 3和其他可能性。

package main
import (
    "log"
)
var a, b, c int
func main() {
    go func() {
        a = 1
        b = 2
    }()
    go func() {
        c = a + 2
    }()
    log.Println(a, b, c)
}

发生在定义之前

Happens-before用于指定Go程序中内存操作的本地顺序。如果内存操作事件e1发生在e2之前,那么e2发生在e1之后也为真;如果e1不在e2之前发生,也没有在e2之后发生,则e1和e2是并发的。

在此定义下,如果满足以下条件,则允许变量(v)的内存写操作(w)对内存读操作(r)可见:

r不会在w的开始之前发生(可以在w之后或同时发生);在w和r之间没有发生其他写操作(w’);

为了确保对变量(v)的特定写入操作(w)对于读取操作(r)可见,有必要确保w是r所允许的唯一写入操作,因此如果满足以下条件时,确保变量(v)内存写操作(w)对内存读操作(r)可见:

w在r开始之前发生;对v的所有其他写入操作仅发生在w之前或r之后;

可以看出,后者的协议比前者更为严格。这种情况不需要w或r,也不需要其他并发写入操作。

在单个Goroutine中,由于绝对没有并发性,因此上述两种情况是等效的。变量v的读取操作可以读取上一次写入操作的值(这应该很容易理解)。但是,如果要访问多个Goroutine中的共享变量,则必须使用同步工具建立先发生条件,以确保变量的读取操作可以读取预期的修改值。

为确保并行执行主体对共享变量的顺序访问,请使用锁。 Java和Go在这一点上是一致的。

以下是可以使用的Go语言特定规则。本质上,before-before规则(通过内存屏障和其他指令)确定CPU缓冲区和主内存的同步时间点,以便可以确定变量的读取和写入顺序,这就是我们通常所说的“同步” “。

同步方法初始化如果包p指向包q,则q的init()方法在p之前发生(Java工程师可以比较最终变量的发生在规则之前)main.main()方法在所有包之后发生init ()方法结束。创建Goroutinego语句以创建新的goroutine发生-在执行goroutine之前(这应该很容易理解)

package main
import (
    "log"
    "time"
)
var a, b, c int
func main() {
    a = 1
    b = 2
    go func() {
        c = a + 2
        log.Println(a, b, c)
    }()
    time.Sleep(1 * time.Second)
}

使用这种情况-之前,我们可以确定c = a + 2是发生-aftera = 1和b = 2,因此可以确定结果输出1 2 3,但是如果是以下代码,则输出不确定,可能是1 2 3或0 0 2

func main() {
    go func() {
        c = a + 2
        log.Println(a, b, c)
    }()
    a = 1
    b = 2
    time.Sleep(1 * time.Second)
}

go map 内存模型_新c 内存模型 release/acquire_go模型

销毁Goroutine Goroutine的退出并不能保证在发生任何事件之前发生。

var a string
func hello() {
    go func() { a = "hello" }()
    print(a)
}

因为在上面的代码中a =“ hello”不使用同步事件,所以不能保证此分配对主goroutine可见。实际上,经过高度优化的Go编译器甚至可以完全删除这一行代码go func(){a =“ hello”}()。

对Goroutine变量的更改需要对其他Goroutines可见。除了使用锁进行同步外,您还可以使用Channel。

频道沟通

在Go编程中,推荐使用Channel作为执行机构之间的通信方法。 Go编译器和运行时将尝试对其进行优化。

发送(发送)到通道的操作发生在相应通道的接收操作完成之前。关闭通道发生在-之前从通道接收到最后的返回值0。无缓冲通道的接收操作(接收)发生在相应通道的发送操作完成之前

var c = make(chan int, 10)
var a string
func f() {
    a = "hello, world"
    c <- 0
}
func main() {
    go f()
    <-c
    print(a)
}

上面的代码可以确保输出hello,world,因为根据上面的规则1),a =“ hello,world”发生在c之前,print(a)发生在after之后,并且根据上述规则1) ,a =“ hello,world”巧合-beforeprint(a)。

根据规则2),用close(c)替换c还可以保证hello world的输出,因为close操作是在收到0之前发送的。

var c = make(chan int)
var a string
func f() {
    a = "hello, world"
    <-c
}
func main() {
    go f()
    c <- 0
    print(a)
}

根据规则3),因为c是不带缓冲的Channel,所以a =“ hello,world”发生-在事件发生之前-在c发生之前-在print(a)之前,但是如果c是一个缓冲队列,则由c = make(chan int,1)定义,则结果不确定。

锁定

新c 内存模型 release/acquire_go模型_go map 内存模型

sync包实现了两种锁定数据结构:

sync.Mutex-> java.util.concurrent.ReentrantLocksync.RWMutex-> java.util.concurrent.locks.ReadWriteLock

before-before规则与Java相似:

任何sync.Mutex或sync.RWMutex变量(l),定义n

var l sync.Mutex
var a string
func f() {
    a = "hello, world"
    l.Unlock()
}
func main() {
    l.Lock()
    go f()
    l.Lock()
    print(a)
}

a =“ hello,world”发生在l.Unlock()发生之前-第二个l.Lock()发生在print(a)之前

一次

sync软件包还提供了一次安全的初始化工具。还记得Java的Singleton设计模式,仔细检查,甚至三次检查的各种Singleton初始化方法吗? Go提供了一种标准方法。

f()一次。Do(f)发生-之前一次返回任意多个。Do(f)调用,而f()仅有一次调用。

var a string
var once sync.Once
func setup() {
    a = "hello, world"
}
func doprint() {
    once.Do(setup)
    print(a)
}
func twoprint() {
    go doprint()
    go doprint()
}

尽管上面的代码两次调用doprint(),但实际上安装程序只会执行一次,并发操作一次。Do(setup)将等待安装返回,然后继续。

参考链接

请启用JavaScript以查看由Disqus提供的评论。由博客提供的博客评论


本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shoujiruanjian/article-360712-1.html

    相关阅读
      发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

      热点图片
      拼命载入中...