IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 【Go并发基础】 -> 正文阅读

[Java知识库]【Go并发基础】

什么是并发

? 代码按照顺序执行,即执行完一句才会执行下一句,这样的代码逻辑简单,符合人类的阅读习惯。但是这样是不够的,因为计算机非常强大,比如一款音乐软件,在听音乐的时候想下载歌曲,同一时刻做两件事,在编程中,这就是并发,并发可以让编写的程序在同一时刻做几件事。

进程和线程

? 讲并发就绕不开线程,不过在介绍线程前,先介绍什么是进程。

? 进程

? 在操作系统中,进程是一个非常重要的概念。当启动一个软件时,操作系统会为这个软件创建一个进程,这个进程就是软件的工作空间,它包含了软件运行所需的所有资源,比如内存空间、文件句柄,还有线程。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fXt1v6VA-1639406335171)(/Users/zhangshihao/Library/Application Support/typora-user-images/image-20211213214435023.png)]

? 线程

? 线程是进程对的执行空间,一个进程可以有多个线程,线程被操作系统调度执行,比如下载一个文件,发送一个消息等。这种多个线程被调度系统同时调度执行的情况,就是多线程并发

? 一个程序启动,就会有对应的进程被创建,同时进程也会启动一个线程,这个线程就叫主线程。如果主线程结束,那么整个流程就退出了。有了主线程,就可以从主线程里启动其他线程,也就有了多线程并发。

协程(Goroutine)

? Go语言中没有线程的概念,只有协程,也称为goroutine。相比线程来说,协程更加轻量,一个程序可以随意启动成千上万个goroutine。

? goroutine被Go Runtime调度,这一点和线程不一样。也即是说,Go语言的并发是由Go自己所调度的,自己决定同事执行多少个goroutine,什么时候执行哪几个。这对于开发者来说是完全透明,只需要在编码的时候告诉Go语言要启动几个goroutine,至于如何调度执行,不用关心。

? 要启动一个goroutine非常简单,Go语言提供了 go 关键字,相比其他编程语言简化了很多

func main() {
    go fmt.Println("奔跑的蜗牛")
    fm.Println("独臂阿童木")
    time.Sleep(time.Second)
}


// 输出
独臂阿童木
奔跑的蜗牛

// 程序是并发的,go关键字启动的goroutine并不阻塞main  goroutine的执行,所以才会打印出上述结果。示例中main goroutine等待一秒,避免main goroutine执行完就退出了,看不到启动的新goroutine打印结果

? 这样就启动了一个goroutine,用来调度fmt.Println()函数。这段代码里有两个goroutine,一个是main函数启动的main goroutine,一个是自己通过go关键字启动的goroutine。

? go 关键字 紧跟一个方法或者函数,就可以启动一个goroutine,让方法在这启动的goroutine中运行。

Channel

? 如果启动了多个goroutine,他们之间如何通信呢?这就是Go语言提供的 Channel(通道) 要解决的问题。

? 声明一个 channel

? 在Go语言中,声明一个channel非常简单,使用内置make函数即可

ch := make(chan string)

// chan 是关键字,表示是chan类型,后面string表示channel里面的数据是string类型。通过channel申明可以看到,chan是一个集合类型

? 定义好chan后就可以使用了,一个chan操作只有两种:发送和接收。

  • 接收: 获取chan中的值,操作符为 <-chan
  • 发送:向chan发送值,把值放在chan中,操作符为 chan <-

? 小技巧:注意发送和接收的操作符都是**<-**,只不过位置不同。接收的操作符在chan的左侧,发送的操作符在chan的右侧。

func main() {
    ch := make(chan string)
    
    go func() {
        fmt.Println("奔跑的蜗牛")
        ch <- "goroutine完成"
    }()
    
    fmt.Println("独臂阿童木")
    v := <-ch
    fmt.Println("接收到的chan中的值:", v)
}

// 程序并没有直接退出,可以看到打印结果,达到了time.Sleep函数的效果
// 因为通过maike创建的chan中没有值,而main goroutine想从chan中获得值,获取不到就一直等待,等到另一个goroutine向chan中发送值为止

// 出书
独臂阿童木
奔跑的蜗牛
接收到的chan中的值: goroutine完成
上述示例中,在新启动的goroutine中向chan类型的变量ch发送值;在main goroutine中,从变量ch接收值,如果ch中没有值,则阻塞等待ch中有值可以接收为止。

? channel有点像在两个goroutine之间架起管道,一个goroutine可以往这个管道发送数据,另一个可以从这个管道接收数据,有点类似队列。

? 无缓冲channel

? 上述示例中make创建的就是一个无缓冲channel,它的容量是0,不能存储任何数据。所以无缓冲channel只起传输数据的作用,数据并不会在channel中停留。这也意味着,无缓冲channel的发送和接收操作是痛失进行的,它也被称为同步channel

? 有缓冲channel

? 有缓冲channel类似一个阻塞对垒,内部的元素先进先出。通过make函数的第二个参数可以指定channel容量的大小,进而创建一个有缓冲channel

cacheCh := make(chan string, 3)

// 创建一个容量为5的channel,内部元素类型是string,也就是说这个channel内部最多可以存放5个类型为string的元素

? 一个有缓冲channel具备一下特点:

1. 有缓冲channel的内部有一个缓冲队列
2. 发送操作是向对垒的尾部插入元素,如果队列已满,则阻塞等待,直到另一个goroutine执行,接收操作释放队列空间
3. 接收操作是从队列的头部获取元素并把它从队列中删除,如果队列为空,则阻塞等待,知道另一个goroutine执行,发送数据插入新的元素

? 因为有缓冲channel类似一个队列,可以获取它的容量和里面元素的个数。

cacheCh := make(chan int, 5)
cachech <- 2
cachech <- 4
fmt.Println("cacheCh 容量为:", cap(cacheCh), "元素个数为:"len(cacheCh))

// 通过内置函数cap可以获取channel的容量,也就是最大能存放多少元素,通过内置函数len可以获取channel中元素的个数

? 小提示:无缓冲channel其实就是一个容量大小为0的channel。比如 make(chan int, 0)

? 关闭 channel

? channel 可以使用内置函数 close 关闭。

close(cacheCh)

? 如果一个channel被关闭了,就不能向里面发送数据了,如果发送的话,会引起panic异常。但是还可以接收channel里的数据,如果channel里面没有数据的话,接收的数据是元素类型的零值。

? 单向channel

? 有时候,因为特殊的业务需求,比如限制一个channel只可以接收但不能发送,或者限制一个channel只能发送但不能接接收,这种channel被称为单向channel

? 单向channel的声明也很简单,只需要在声明的时候带上<- 操作符即可。

onlySend := make(chan <- int)
onlyReceive := make(<-chan int)

// 注意: 声明单向channel <- 操作符的位置和上面发送接收操作是一样的。

? 在函数后者方法的参数中,使用单向channel比较多,这样可以防止一些操作影响了channel.

func counter(out chan <- int) {
    // 函数内容使用变量out, 只能进行发送操作
}

? select + channel示例

? 假如我们要下载一个文件,启动了3个goroutine进行下载,并发结果发送到3个channel中。哪个先下载好,就先用哪个channel的结果。

? 在Go语言中,通难过select语句实现多路复用操作,语句格式如下:

select {
case i1 <- c1 :
    // todo
case i2 <- c2 :
    // todo
default :
    // todo
}

? 整体结构和switch很像,都有case和default,只不过select和case是一个个可以操作的channel。

? 小提示:多路复用可以简单的理解为,N个channel中,任意一个channel有数据产生,select都可以监听到, 然后执行对应的分支,接收数据并处理。

// 多路下载示例

func main() {
    // 声明3个存放结果的channel
    firstCh := make(chan string)
    secondCh := make(chan string)
    threeCh := make(chan string)
    
    // 同时开启3个goroutine下载
    go func() {
        firstCh <- downloadFile("firstCh")  
    }()
     go func() {
        secondCh <- downloadFile("secondCh")  
    }()
     go func() {
        threeCh <- downloadFile("threeCh")  
    }()
    
    // 开启多路复用,哪个channel能获取到值,就先用哪个
    select {
        case filePath := <- firstCh :
        fmt.Println(filePath)
        case filePath := <- secoudCh :
        fmt.Println(filePath)
        case filePath := <- threeCh :
        fmt.Println(filePath)
    }
    
    func downloadFile(chanName string) string {
        // 模拟下载文件,可以随机time.Sleep时间
        time.Sleep(time.Second)
        return chanName + "filePath"
    }
}

? 如果这些case中有一个可以执行,select语句会选择该case语句执行,如果同时又多个case可以被执行,则随机选择一个,这样每个case都有平等的被执行的机会。如果一个select没有任何case,那么就会一直等下去。

小结:channel是Go语言并发的基础,在Go语言中,提倡通过通信来共享内存,而不是通过共享内存来通信,其实就是提倡通过channel发送接收消息的方式进行数据通信,而不是通过修改同一个变量。所以在数据流动、传递的场景中要优先使用channel,它是并发安全的,性能也不错。


  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-12-14 15:48:10  更:2021-12-14 15:49:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:00:43-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码