
Java面试宝典:MongoDB实战技巧 作者:忆遂愿
https://cloud.tencent.com/developer/article/2466159?shareByChannel=link
在 Java 驱动使用方面,作者详细介绍了如何在 Java 环境中使用 MongoDB 驱动程序,包括驱动的安装、配置以及如何通过 Java 代码与 MongoDB 进行交互。通过具体的代码示例和详细的步骤说明,读者可以轻松上手,将 MongoDB 集成到自己的 Java 应用程序中。
在 Go 语言中,使用
goroutine来实现并发。goroutine是一种轻量级的线程,可以通过go关键字来启动。
package main
import (
"fmt"
"time"
)
func hello() {
fmt.Println("hello goroutine")
}
func main() {
go hello()
fmt.Println("main goroutine")
time.Sleep(time.Second)
}
func main() {...}:这是 Go 程序的入口函数,程序从这里开始执行。go hello():使用 go 关键字启动一个新的 goroutine 来调用 hello 函数。goroutine 是 Go 语言中的轻量级线程,它允许并发执行代码。当执行到这一行时,会创建一个新的执行流,该执行流将调用 hello 函数,而不会阻塞当前的执行流程。fmt.Println("main goroutine"):在主 goroutine 中打印出 "main goroutine" 字符串。time.Sleep(time.Second):让主 goroutine 暂停执行一秒钟。这是为了确保程序不会立即退出,因为新启动的 goroutine 可能还没有机会执行完 hello 函数。如果没有这行代码,程序可能会在 hello 函数执行之前就结束,因为主 goroutine 会直接结束,导致整个程序终止。package main
import (
"fmt"
"time"
)
func hello(i int) {
fmt.Println("hello goroutine", i)
}
func main() {
for i := 0; i < 10; i++ {
go hello(i)
}
fmt.Println("main goroutine")
time.Sleep(time.Second)
}仔细观察一下不难看出,这两次输出的结果都不相同,原因如下:
在 Go 语言中,
goroutine的执行顺序是不确定的。当你使用go关键字启动多个goroutine时,它们会被调度到 Go 的运行时环境中并发执行。 以下是导致打印顺序不同的原因:
goroutine 的执行顺序。不同的 goroutine 可能在不同的系统线程上执行,并且它们的执行顺序取决于调度器的决策,而不是它们被创建的顺序。goroutine 是并发执行的,它们可能在不同的时间点开始和完成,这取决于系统的负载、CPU 核心的可用性等因素。go 关键字启动一个新的 Goroutine,例如 go func() {...}()。Goroutines 非常轻量,可以轻松创建大量的 Goroutines,并且它们由 Go 的运行时系统管理。package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
// 启动多个 goroutines
for i := 1; i <= 5; i++ {
wg.Add(1)
go worker(i, &wg)
}
// 等待所有 goroutines to complete
wg.Wait()
}
var wg sync.WaitGroup:创建一个 sync.WaitGroup 实例,用于等待一组 Goroutines 完成。wg.Add(1):在启动每个 Goroutine 之前,调用 wg.Add(1) 来增加等待组的计数,表示有一个新的 Goroutine 要等待。go worker(i, &wg):启动一个新的 Goroutine 来执行 worker 函数,并将 wg 的指针传递给它。defer wg.Done():在 worker 函数中,使用 defer wg.Done() 来通知 WaitGroup 该 Goroutine 已经完成。wg.Wait():在主函数中,调用 wg.Wait() 会阻塞,直到 WaitGroup 的计数为 0,即所有 Goroutines 都已完成。I/O 操作或阻塞时,M 可以切换到另一个 Goroutine 执行,提高并发性能。package main
import (
"fmt"
"runtime"
)
func main() {
// 启动一个匿名 goroutine,接收一个字符串参数 s
go func(s string) {
// 循环两次
for i := 0; i < 2; i++ {
// 打印传入的字符串 s
fmt.Println(s)
}
}("hello")
// 主 goroutine
for i := 0; i < 2; i++ {
// 让出时间片,让其他 goroutine 有机会执行
runtime.Gosched()
// 打印主 goroutine 信息
fmt.Println("main goroutine")
}
}
runtime.Gosched():这个函数调用会使当前 goroutine(在这种情况下是主 goroutine)让出处理器,允许其他 goroutine 运行。它放弃自己的时间片并重新进入可运行的 goroutine 队列,以便其他 goroutine 有机会执行。
runtime.Goexit() 是 Go 语言 runtime 包中的一个函数,它的主要作用是立即终止当前正在执行的 goroutine,而不会影响其他 goroutine 的执行。runtime.Goexit() 被调用时,它会执行当前 goroutine 中已注册的 defer 函数,然后终止该 goroutine,但不会执行 goroutine 中 runtime.Goexit() 之后的代码调用Goexit之前 runtime.GOMAXPROCS(n int) 是 Go 语言 runtime 包中的一个函数,它用于设置可以同时执行的最大 CPU 核心数,也就是可以同时运行的 goroutine 的最大数量。n 表示要使用的 CPU 核心数。如果 n 小于 1,则表示不限制 CPU 核心数,Go 运行时会根据系统的 CPU 核心数自动调整。可以看出当参数为1的时候执行的时间不同 ,那么原因是什么?
首先对代码部分进行简单分析
package main
import (
"fmt"
"runtime"
"time"
)
func a() {
for i := 0; i < 5; i++ {
fmt.Println("A: ", i)
}
fmt.Println(time.Now())
}
func b() {
for i := 0; i < 5; i++ {
fmt.Println("B: ", i)
}
fmt.Println(time.Now())
}
func main() {
runtime.GOMAXPROCS(1)
go a()
go b()
time.Sleep(time.Second)
}
runtime.GOMAXPROCS(1):将最大的可同时执行的 goroutine 数量设置为 1。这意味着在任何给定的时间,只有一个 goroutine 会在 CPU 上执行,其他 goroutine 会等待。go a() 和 go b():启动两个 goroutine,分别执行函数 a 和 b。time.Sleep(time.Second):让主 goroutine 暂停执行一秒钟,以等待 a 和 b 两个 goroutine 有机会执行。runtime.GOMAXPROCS(1) 限制了并发执行的 goroutine 数量为 1,但 goroutine 的调度顺序仍然是不确定的。a 和 b 两个 goroutine 被创建后,它们会被放入 goroutine 队列中等待调度。由于 goroutine 的调度是由 Go 的运行时系统决定的,所以 a 和 b 哪个先被执行是不确定的。a 先被调度,它可能会在执行过程中被暂停,然后 b 被调度,或者反之。这取决于 Go 运行时的调度决策,包括当前系统的负载、其他 goroutine 的状态等因素。runtime.GOMAXPROCS(2) 会允许最多 2 个 goroutine 同时运行,但这并不保证 a 和 b 函数的执行顺序会固定。goroutine 的状态等)来决定哪个 goroutine 先执行,以及它们的执行顺序。goroutine 的执行时间可能会受到系统资源分配、操作系统调度、其他进程的影响,因此即使有更多的 CPU 资源,也不能保证 a 和 b 的执行顺序和时间是固定的。可以通过管道来同步时间
package main
import (
"fmt"
"time"
)
func a(ch chan bool) {
for i := 0; i < 5; i++ {
fmt.Println("A: ", i)
}
fmt.Println(time.Now())
ch <- true
}
func b(ch chan bool) {
<-ch
for i := 0; i < 5; i++ {
fmt.Println("B: ", i)
}
fmt.Println(time.Now())
}
func main() {
ch := make(chan bool)
go a(ch)
go b(ch)
time.Sleep(time.Second)
}
ch := make(chan bool):创建一个布尔类型的通道。func a(ch chan bool):函数 a 执行完后会向通道 ch 发送一个 true 值。func b(ch chan bool):函数 b 会从通道 ch 接收一个值,这会阻塞 b 的执行,直到 a 发送一个值到通道。这样可以确保 b 在 a 完成后开始执行。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。