Go与Java Go和Java微观对比

简介: 本文对比了Go语言与Java在线程实现上的差异。Go通过Goroutines实现并发,使用`go`关键字启动;而Java则通过`Thread`类开启线程。两者在通信机制上也有所不同:Java依赖共享内存和同步机制,如`synchronized`、`Lock`及并发工具类,而Go采用CSP模型,通过Channel进行线程间通信。此外,文章还介绍了Go中使用Channel和互斥锁解决并发安全问题的示例。

Go语言中线程的实现和Java语言中线程的实现

 go中的线程相关的概念是Goroutines(并发),是使用go关键字开启。

Java中的线程是通过Thread类开启的。  

在go语言中,一个线程就是一个Goroutines,主函数就是(主) main Goroutines。  

使用go语句来开启一个新的Goroutines

比如:  

普通方法执行

scss

体验AI代码助手

代码解读

复制代码

 myFunction()

开启一个Goroutines来执行方法

scss

体验AI代码助手

代码解读

复制代码

 go myFunction()

java中是

scss

体验AI代码助手

代码解读

复制代码

 new Thread(()->{

 

 //新线程逻辑代码

 

 }).start();

参考下面的代码示例:

 

go

体验AI代码助手

代码解读

复制代码

package main


import (
   "fmt"
)


//并发开启新线程goroutine测试


//我的方法
func myFunction() {
   fmt.Println("Hello!!!")
}
//并发执行方法
func goroutineTestFunc() {
   fmt.Println("Hello!!! Start Goroutine!!!")
}




func main() {
   /*
   myFunction()
   //go goroutineTestFunc()
   //此时因为主线程有时候结束的快,goroutineTestFunc方法得不到输出,由此可以看出是开启了新的线程。
   */
   //打开第二段执行
   /*
   go goroutineTestFunc()
   time.Sleep(10*time.Second)//睡一段时间  10秒
   myFunction()
    */
}

 

线程间的通信:

java线程间通信有很多种方式:

比如最原始的 wait/notify

到使用juc下高并发线程同步容器,同步队列

到CountDownLatch等一系列工具类

 ......

甚至是分布式系统不同机器之间的消息中间件,单机的disruptor等等。

Go语言不同,线程间主要的通信方式是Channel。

Channel是实现go语言多个线程(goroutines)之间通信的一个机制。

Channel是一个线程间传输数据的管道,创建Channel必须声明管道内的数据类型是什么  

下面我们创建一个传输int类型数据的Channel  

代码示例:  

go

体验AI代码助手

代码解读

复制代码

package main


import "fmt"


func main() {
   ch := make(chan int)
   fmt.Println(ch)
}

 

channel是引用类型,函数传参数时是引用传递而不是值拷贝的传递。

 channel的空值和别的应用类型一样是nil。

==可以比较两个Channel之间传输的数据类型是否相等。  

channel是一个管道,他可以收数据和发数据。

具体参照下面代码示例:

go

体验AI代码助手

代码解读

复制代码

package main


import (
   "fmt"
   "time"
)
//channel发送数据和接受数据用 <-表示,是发送还是接受取决于chan在  <-左边还是右边
//创建一个传输字符串数据类型的管道
var  chanStr  = make(chan string)
func main() {
   fmt.Println("main goroutine print Hello ")
   //默认channel是没有缓存的,阻塞的,也就是说,发送端发送后直到接受端接受到才会施放阻塞往下面走。
   //同样接收端如果先开启,直到接收到数据才会停止阻塞往下走
   //开启新线程发送数据
   go startNewGoroutineOne()
   //从管道中接收读取数据
   go startNewGoroutineTwo()
   //主线程等待,要不直接结束了
   time.Sleep(100*time.Second)
}


func startNewGoroutineOne() {
   fmt.Println("send channel print Hello ")
   //管道发送数据
   chanStr <- "Hello!!!"
}


func startNewGoroutineTwo(){
   fmt.Println("receive channel print Hello ")
   strVar := <-chanStr
   fmt.Println(strVar)
}

 

无缓存的channel可以起到一个多线程间线程数据同步锁安全的作用。

缓存的channel创建方式是

make(chan string,缓存个数)  

缓存个数是指直到多个数据没有消费或者接受后才进行阻塞。

类似于java中的synchronized和lock  

可以保证多线程并发下的数据一致性问题。

首先我们看一个线程不安全的代码示例:

 

scss

体验AI代码助手

代码解读

复制代码

package main


import (
   "fmt"
   "time"
)


//多线程并发下的不安全问题
//金额
var moneyA int =1000
//添加金额
func subtractMoney(subMoney int) {
   time.Sleep(3*time.Second)
   moneyA-=subMoney
}


//查询金额
func getMoney() int {
   return moneyA;
}




func main() {


   //添加查询金额
   go func() {
      if(getMoney()>200) {
         subtractMoney(200)
         fmt.Printf("200元扣款成功,剩下:%d元\n",getMoney())
      }
   }()


   //添加查询金额
   go func() {
      if(getMoney()>900) {
         subtractMoney(900)
         fmt.Printf("900元扣款成功,剩下:%d元\n",getMoney())
      }
   }()
   //正常逻辑,只够扣款一单,可以多线程环境下结果钱扣多了
   time.Sleep(5*time.Second)
   fmt.Println(getMoney())
}

 

缓存为1的channel可以作为锁使用:

 

示例代码如下:

scss

体验AI代码助手

代码解读

复制代码

package main


import (
   "fmt"
   "time"
)


//多线程并发下使用channel改造
//金额
var moneyA  = 1000
//减少金额管道
var synchLock = make(chan int,1)


//添加金额
func subtractMoney(subMoney int) {
   time.Sleep(3*time.Second)
   moneyA-=subMoney
}


//查询金额
func getMoney() int {
   return moneyA;
}




func main() {


   //添加查询金额
   go func() {
      synchLock<-10
      if(getMoney()>200) {
         subtractMoney(200)
         fmt.Printf("200元扣款成功,剩下:%d元\n",getMoney())
      }
      <-synchLock
   }()


   //添加查询金额
   go func() {
      synchLock<-10
      if(getMoney()>900) {
         subtractMoney(900)
         fmt.Printf("900元扣款成功,剩下:%d元\n",getMoney())
      }
      synchLock<-10
   }()
   //这样类似于java中的Lock锁,不会扣多
   time.Sleep(5*time.Second)
   fmt.Println(getMoney())
}

 

go也有互斥锁

类似于java中的Lock接口

参考如下示例代码:

scss

体验AI代码助手

代码解读

复制代码

package main


import (
   "fmt"
   "sync"
   "time"
)


//多线程并发下使用channel改造
//金额
var moneyA  = 1000


var lock sync.Mutex;
//添加金额
func subtractMoney(subMoney int) {
   lock.Lock()
   time.Sleep(3*time.Second)
   moneyA-=subMoney
   lock.Unlock()
}


//查询金额
func getMoney() int {
   lock.Lock()
   result := moneyA
   lock.Unlock()
   return result;
}




func main() {
   //添加查询金额
   go func() {
      if(getMoney()>200) {
         subtractMoney(200)
         fmt.Printf("200元扣款成功,剩下:%d元\n",getMoney())
      }else {
         fmt.Println("余额不足,无法扣款")
      }
   }()


   //添加查询金额
   go func() {
      if(getMoney()>900) {
         subtractMoney(900)
         fmt.Printf("900元扣款成功,剩下:%d元\n",getMoney())
      }else {
         fmt.Println("余额不足,无法扣款")
      }
   }()
   //正常
   time.Sleep(5*time.Second)
   fmt.Println(getMoney())
}

转载来源:https://juejin.cn/post/7122435320318001166

相关文章
|
Cloud Native 安全 Java
go和java的比较
go和java的比较
|
安全 编译器 Go
Go语言中的int和int32:同一个概念吗?
【2月更文挑战第24天】
1036 3
|
1月前
|
人工智能 缓存 JavaScript
SpringBoot 公共字段自动填充的6种方法
本文深入探讨了在Java开发中如何高效维护公共字段的多种解决方案。首先分析了手动设置公共字段带来的代码重复、维护成本高和易遗漏等问题,接着介绍了使用MyBatis-Plus自动填充、AOP统一处理等基础与进阶方案,实现字段自动赋值。文章还涵盖了多数据源适配、分布式ID生成、空指针防护、字段覆盖问题解决、性能优化以及操作日志追踪等生产环境中的最佳实践与避坑指南。最终通过方案组合使用,显著提升了开发效率与系统稳定性,为构建高质量企业级应用提供了有力支撑。
130 0
|
22天前
|
安全 Java 编译器
对比Java学习Go——基础理论篇
本章介绍了Java开发者学习Go语言的必要性。Go语言以简单、高效、并发为核心设计哲学,摒弃了传统的类继承和异常机制,采用组合、接口和多返回值错误处理,提升了代码清晰度与开发效率。Go直接编译为静态二进制文件,启动迅速、部署简便,其基于Goroutine和Channel的并发模型相较Java的线程与锁机制更轻量安全。此外,Go Modules简化了依赖管理,与Java的Maven/Gradle形成鲜明对比,提升了构建与部署效率。
|
1月前
|
云安全 机器学习/深度学习 人工智能
阿里云安全Black Hat技术开源大揭秘,AI安全检测的工程化实践
阿里云安全 LLMDYara框架开源核心思路,赋能云安全产品!
|
1月前
|
机器学习/深度学习 数据可视化 算法
深度学习模型结构复杂、参数众多,如何更直观地深入理解你的模型?
深度学习模型虽应用广泛,但其“黑箱”特性导致可解释性不足,尤其在金融、医疗等敏感领域,模型决策逻辑的透明性至关重要。本文聚焦深度学习可解释性中的可视化分析,介绍模型结构、特征、参数及输入激活的可视化方法,帮助理解模型行为、提升透明度,并推动其在关键领域的安全应用。
213 0
|
2月前
|
人工智能 数据挖掘 Linux
Centos安装Python3.7(亲测可用)
本指南详细介绍了在基于Linux(以CentOS系统为例,使用yum包管理器)的系统上安装Python 3.7版本的完整流程。Python是一种广泛使用的高级编程语言,在各种领域如软件开发、数据分析、人工智能和区块链开发等都有着重要的应用。
294 2
|
1月前
|
人工智能 算法 安全
干货I 算法备案,看这篇就够了!
本文全面解析算法备案的定义、背景及申请流程,涵盖相关法律法规、深度合成类应用上架要求、备案难点与经验分享,并介绍AIGC行业所需其他资质,助力企业合规发展。
初识go变量,使用var和:=来声明变量,声明变量的三种方式
这篇文章介绍了Go语言中使用`var`和`:=`声明变量的三种不同方式,包括声明单个或多个变量、通过值确定数据类型以及在函数体内使用`:=`声明局部变量。
初识go变量,使用var和:=来声明变量,声明变量的三种方式
|
10月前
|
Java Linux Docker
什么是 Docker?如何将 Spring Boot 应用程序部署到 Docker?
什么是 Docker?如何将 Spring Boot 应用程序部署到 Docker?
337 3