贫民窟冷知识(贫民窟问题)(贫民窟的解决方法)
7182023-09-09
各位老铁们好,相信很多人对Java如何解决可见性和有序性的问题都不是特别的了解,因此呢,今天就来为大家分享下关于Java如何解决可见性和有序性的问题以及为何java不建议使用sun包的问题知识,还望可以帮助大家,解决大家的一些困惑,下面一起来看看吧!
本文目录
含金量较高。因为Sun证书是由计算机技术公司SunMicrosystems所颁发的,该公司是全球计算机领域一流的厂商之一。其证书考试要求严格,涉及的知识面广,以及考试难度高。所以,拥有该证书的人在IT行业中所拥有的职业竞争力和认可度较高。此外,持有Sun证书的人在就业市场上也比其他人更加有竞争力。他们在获得工作面试机会时具有明显的优势,因为这种证书证明了他们在计算机科学和工程领域具备了更高水平的专业知识和技能。因此,持Sun证书的人可以增强自己的职业发展和就业前景。
不要使用Vue等框架意味着在编写代码或解决问题时,不使用Vue.js或其他类似的JavaScript框架。
因为Java是一种编程语言和计算平台,由SunMicrosystems在1995年首次发布。所以它从微末起步,逐渐发展为当今数字世界中很大一部分资产所依赖的基础,是用于构建许多服务和应用程序的可靠平台。
首先需要了解,为什么会有「可见性」和「时序性」问题,然后我们来看Java是如何解决这两个问题的。
「可见性」和「时序性」问题导致「可见性」和「时序性」问题的原因有如下几个:
抢占式任务执行:现代CPU执行多任务方式是「抢占式」,它的总控制权在操作系统手中,操作系统会轮流给需要CPU执行的任务分配执行时间片,超过时间后,操作系统会剥夺当前任务的CPU使用权,把它排在队列的最后,最后分配时间片……
存储速度差异:各存储执行速度的不同,离CPU越近,存储速度越快,相对的容量就越小。执行程序所需要的数据不可能一次性全部都加载到寄存器中,所以有load与store的过程,影响了所谓的「可见性」
指令重排:大多数现代微处理器都会采用将指令乱序执行(out-of-orderexecution,简称OoOE或OOE)的方法,在条件允许的情况下,直接运行当前有能力立即执行的后续指令,避开获取下一条指令所需数据时造成的等待。通过乱序执行的技术,处理器可以大大提高执行效率。除了处理器,常见的Java运行时环境的JIT编译器也会做指令重排序操作,即生成的机器指令与字节码指令顺序不一致。
解决方法解决思路很简单,就是把多线程强制单线程执行。
解决方法无非两种:
内存屏障
锁
先看下JVM的内存模型,我们基于这个模型来简单说明下
内存屏障内存屏障在Java中通过volatile关键字体现。volatile会在适当的地方添加下面四种内存屏障。
LoadLoad屏障:对于这样的语句Load1;LoadLoad;Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。
StoreStore屏障:对于这样的语句Store1;StoreStore;Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。
LoadStore屏障:对于这样的语句Load1;LoadStore;Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。
StoreLoad屏障:对于这样的语句Store1;StoreLoad;Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能。
内存屏障只保证可见性,不保证时序性。也就是说内存屏障只是解决了线程A修改的内容能立刻被线程B读到。
锁Java中锁按性质分可以分悲观锁和乐观锁。悲观锁基于锁指令实现,乐观锁基于CAS实现。
通过monitorenter和monitorexit两个指令实现悲观锁,这两个指令之间的指令不得重排,且独占。假设线程A和线程B同时执行一段代码,线程A先通过monitorenter获取到了锁,那么在线程A执行monitorexit之前,线程B都只能等待。
CAS即CompareAndSet,Java通过自旋以及CPU层级的指令实现。具体可参考JUC实现。假设有一个变量c,初始值为3。线程A和线程B同时修改这个变量,A,B都同时获取到了变量c的值,A首先进行修改,将值改成了4。B尝试修改,但是发现c的值现在是4而不是3,所以进行自旋等待,然后重新执行修改操作,将4改成了5。
ThreadLocal最后说下ThreadLocal。ThreadLocal即本地线程变量,也就是将公共的变量直接拿到线程内使用,其中的修改对外不影响。谈不上解决了「可见性」和「时序性」。只是保证了当前线程内的修改不影响其它线程,其它线程的修改也不影响当前线程。
Java的发展真的越来越快了,在Java9正式发布之前,我们可以使用它的SNAPSHOT版本,先来体验一下Java9有哪些新的特性,下面的清单来自于官方文档,看着似乎很多,但是真正具有颠覆意义的其实就是ModuleSystem,其余很多主要是一些新的feature增加,还有一些功能的加强,在本篇文章中,我们将介绍一下主要的几个,不会一一去说,资料也不多,所以我想说也没的说,另外Java8是我认为迄今为止Java最大的一次变化,不光是特性的增加,更多的是编程风格的转变,
102:ProcessAPIUpdates
110:HTTP2Client
143:ImproveContendedLocking
158:UnifiedJVMLogging
165:CompilerControl
193:VariableHandles
197:SegmentedCodeCache
199:SmartJavaCompilation,PhaseTwo
200:TheModularJDK
201:ModularSourceCode
211:ElideDeprecationWarningsonImportStatements
212:ResolveLintandDoclintWarnings
213:MillingProjectCoin
214:RemoveGCCombinationsDeprecatedinJDK
8215:TieredAttributionforjavac
216:ProcessImportStatementsCorrectly
217:AnnotationsPipeline2.0
219:DatagramTransportLayerSecurity(DTLS)
220:ModularRun-TimeImages
221:SimplifiedDocletAPI
222:jshell:TheJavaShell(Read-Eval-PrintLoop)
223:NewVersion-StringScheme
224:HTML5Javadoc
225:JavadocSearch
226:UTF-8PropertyFiles
227:Unicode7.0
228:AddMoreDiagnosticCommands
229:CreatePKCS12KeystoresbyDefault
231:RemoveLaunch-TimeJREVersionSelection
232:ImproveSecureApplicationPerformance
233:GenerateRun-TimeCompilerTestsAutomatically
235:TestClass-FileAttributesGeneratedbyjavac
236:ParserAPIforNashorn
237:Linux/AArch64Port
238:Multi-ReleaseJARFiles
240:RemovetheJVMTIhprofAgent
241:RemovethejhatTool
243:Java-LevelJVMCompilerInterface
244:TLSApplication-LayerProtocolNegotiationExtension
245:ValidateJVMCommand-LineFlagArguments
246:LeverageCPUInstructionsforGHASHandRSA
247:CompileforOlderPlatformVersions2
48:MakeG1theDefaultGarbageCollector
249:OCSPStaplingforTLS250:StoreInternedStringsinCDSArchives
251:Multi-ResolutionImages
252:UseCLDRLocaleDatabyDefault
253:PrepareJavaFXUIControls&CSSAPIsforModularization
254:CompactStrings
255:MergeSelectedXerces2.11.0UpdatesintoJAXP
256:BeanInfoAnnotations
257:UpdateJavaFX/MediatoNewerVersionofGStreamer
258:HarfBuzzFont-LayoutEngine
259:Stack-WalkingAPI260:EncapsulateMostInternalAPIs
261:ModuleSystem
262:TIFFImageI/O
263:HiDPIGraphicsonWindowsandLinux
264:PlatformLoggingAPIandService
265:MarlinGraphicsRenderer
266:MoreConcurrencyUpdates
267:Unicode8.0268:XMLCatalogs
269:ConvenienceFactoryMethodsforCollections
270:ReservedStackAreasforCriticalSections
271:UnifiedGCLogging
272:Platform-SpecificDesktopFeatures
273:DRBG-BasedSecureRandomImplementations
274:EnhancedMethodHandles
275:ModularJavaApplicationPackaging
276:DynamicLinkingofLanguage-DefinedObjectModels
277:EnhancedDeprecation
278:AdditionalTestsforHumongousObjectsinG
1279:ImproveTest-FailureTroubleshooting
280:IndifyStringConcatenation
281:HotSpotC++Unit-TestFramework
282:jlink:TheJavaLinker
283:EnableGTK3onLinux
284:NewHotSpotBuildSystem
285:Spin-WaitHints287:SHA-3HashAlgorithms
288:DisableSHA-1Certificates
289:DeprecatetheAppletAPI
290:FilterIncomingSerializationData
292:ImplementSelectedECMAScript6FeaturesinNashorn
294:Linux/s
390xPort295:Ahead-of-TimeCompilation
1.ModularSystem–JigsawProject
该特性是Java9最大的一个特性,Java9起初的代号就叫Jigsaw,最近被更改为Modularity,Modularity提供了类似于OSGI框架的功能,模块之间存在相互的依赖关系,可以导出一个公共的API,并且隐藏实现的细节,Java提供该功能的主要的动机在于,减少内存的开销,我们大家都知道,在JVM启动的时候,至少会有30~60MB的内存加载,主要原因是JVM需要加载rt.jar,不管其中的类是否被classloader加载,第一步整个jar都会被JVM加载到内存当中去,模块化可以根据模块的需要加载程序运行需要的class,那么JVM是如何知道需要加载那些class的呢?这就是在Java9中引入的一个新的文件module.java我们大致来看一下一个例子(module-info.java)
[java]viewplaincopyprint?modulecom.baeldung.java9.modules.car{requirescom.baeldung.java9.modules.engines;exportscom.baeldung.java9.modules.car.handling;}关于更多Java9模块编程的内容请参考一本书:《Java9Modularity》里面讲的比较详细,介绍了当前Java对jar之间以来的管理是多么的混乱,引入modularity之后的改变会是很明显的差别。
2.ANewHttpClient
就目前而言,JDK提供的Http访问功能,几乎都需要依赖于HttpURLConnection,但是这个类大家在写代码的时候很少使用,我们一般都会选择Apache的HttpClient,此次在Java9的版本中引入了一个新的package:java.net.http,里面提供了对Http访问很好的支持,不仅支持Http1.1而且还支持HTTP2,以及WebSocket,据说性能可以超过ApacheHttpClient,Netty,Jetty,简单的来看一个代码片段
[java]viewplaincopyprint?URIhttpURI=newURI("http://www.94jiankang.com");HttpRequestrequest=HttpRequest.create(httpURI).GET();HttpResponseresponse=request.response();StringresponseBody=response.body(HttpResponse.asString());3.ProcessAPIEnhance
在Java很早的版本中,提供了Process这样的API可以获得进程的一些信息,包括runtime,甚至是用它来执行当前主机的一些命令,但是请大家思考一个问题,你如何获得你当前Java运行程序的PID?很显然通过Process是无法获得的,需要借助于JMX才能得到,但是在这一次的增强中,你将会很轻松的得到这样的信息,我们来看一个简单的例子
[java]viewplaincopyprint?ProcessHandleself=ProcessHandle.current();longPID=self.getPid();ProcessHandle.InfoprocInfo=self.info();Optional<String[]>args=procInfo.arguments();Optional<String>cmd=procInfo.commandLine();Optional<Instant>startTime=procInfo.startInstant();Optional<Duration>cpuUsage=procInfo.totalCpuDuration();上面有大量的Optional,这是Java8中的API,同样在Java9中对其进行了增强,本人在Java8实战视频中对OptionalAPI进行了源码级别的剖析,感兴趣的一定要去看看。
已经获取到了JVM的进程,我们该如何将该进程优雅的停掉呢?下面的代码给出了答案
[java]viewplaincopyprint?childProc=ProcessHandle.current().children();childProc.forEach(procHandle->{assertTrue("Couldnotkillprocess"+procHandle.getPid(),procHandle.destroy());});通过上面的一小段代码,我们也发现了Java9对断言机制同样增加了一些增强,多说一些题外话,我们目前的系统中运行一个严重依赖于HivebeelineServer的程序,beelineserver不是很稳定,经常出现卡顿,甚至假死,假死后也不回复的问题,这样就导致我们的程序也会出现卡顿,如果运维人员不对其进行清理,系统运行几个月之后会发现很多僵尸进程,于是增加一个获取当前JVMPID的功能,然后判断到超过给定的时间对其进行主动杀死,完全是程序内部的行为,但是获取PID就必须借助于JMX的动作,另外杀死它也必须借助于操作系统的命令,诸如kill这样的命令,显得非常的麻烦,但是Java9的方式明显要优雅方便许多。
4.Try-With-Resources的改变
我们都知道,Try-With-Resources是从JDK7中引入的一项重要特征,只要接口继承了Closable就可以使用Try-With-Resources,减少finally语句块的编写,在Java9中会更加的方便这一特征
[java]viewplaincopyprint?MyAutoCloseablemac=newMyAutoCloseable();try(mac){//dosomestuffwithmac}try(newMyAutoCloseable(){}.finalWrapper.finalCloseable){//dosomestuffwithfinalCloseable}catch(Exceptionex){}我们的Closeable完全不用写在try()中。
5.DiamondOperatorExtension
[java]viewplaincopyprint?FooClass<Integer>fc=newFooClass<>(1){//anonymousinnerclass};FooClass<?extendsInteger>fc0=newFooClass<>(1){//anonymousinnerclass};FooClass<?>fc1=newFooClass<>(1){//anonymousinnerclass};6.InterfacePrivateMethod
[java]viewplaincopyprint?interfaceInterfaceWithPrivateMethods{privatestaticStringstaticPrivate(){return"staticprivate";}privateStringinstancePrivate(){return"instanceprivate";}defaultvoidcheck(){Stringresult=staticPrivate();InterfaceWithPrivateMethodspvt=newInterfaceWithPrivateMethods(){//anonymousclass};result=pvt.instancePrivate();}}}该特性完全是为了Java8中default方法和static方法服务的。
7.JShellCommandLineTool
在Java8出来的时候,很多人都喊着,这是要抢夺Scala等基于JVM动态语言的市场啊,其中有人给出了一个Java做不到的方向,那就是Scala可以当作脚本语言,Java可以么?很明显在此之前Java不行,ta也不具备动态性,但是此次Java9却让Java也可以像脚本语言一样来运行了,主要得益于JShell,我们来看一下这个演示
[java]viewplaincopyprint?jdk-9\bin>jshell.exe|WelcometoJShell--Version9|Foranintroductiontype:/helpintrojshell>"Thisismylongstring.Iwantapartofit".substring(8,19);$5==>"mylongstring"这是我们在Jshell这个控制台下运行,我们如何运行脚本文件呢?
[java]viewplaincopyprint?jshell>/savec:\develop\JShell_hello_world.txtjshell>/openc:\develop\JShell_hello_world.txtHelloJShell!8.JCMDSub-Commands
记得在Java8中,放弃了Jhat这个命令,但是很快在Java9中增加了一些新的命令,比如我们要介绍到的jcmd,借助它你可以很好的看到类之间的依赖关系
[java]viewplaincopyprint?jdk-9\bin>jcmd14056VM.class_hierarchy-i-sjava.net.Socket14056:java.lang.Object/null|--java.net.Socket/null|implementsjava.io.Closeable/null(declaredintf)|implementsjava.lang.AutoCloseable/null(inheritedintf)||--org.eclipse.ecf.internal.provider.filetransfer.httpclient4.CloseMonitoringSocket||implementsjava.lang.AutoCloseable/null(inheritedintf)||implementsjava.io.Closeable/null(inheritedintf)||--javax.net.ssl.SSLSocket/null||implementsjava.lang.AutoCloseable/null(inheritedintf)||implementsjava.io.Closeable/null(inheritedintf)9.Мulti-ResolutionImageAPI
接口java.awt.image.MultiResolutionImage封装了一系列的不同分辨率图像到一个单独对象的API,我么可以根据给定的DPI矩阵获取resolution-specific,看一下下面的代码片段
[java]viewplaincopyprint?BufferedImage[]resolutionVariants=....MultiResolutionImagebmrImage=newBaseMultiResolutionImage(baseIndex,resolutionVariants);ImagetestRVImage=bmrImage.getResolutionVariant(16,16);assertSame("Imagesshouldbethesame",testRVImage,resolutionVariants[3]);关于AWT的东西,本人几乎不怎么接触,如果有用到的朋友,等JDK9出来之后,自己体会使用一下吧。
10.VariableHandles
很早之前就传言Java会将unsafe这一个类屏蔽掉,不给大家使用,这次看他的官方文档,貌似所有已sun开头的包都将不能在application中使用,但是java9提供了新的API供大家使用。
在JDK9中提供了一个新的包,叫做java.lang.invoke里面有一系列很重要的类比如VarHandler和MethodHandles,提供了类似于原子操作以及Unsafe操作的功能。
11.Publish-SubscribeFramework
在新版的JDK9中提供了消息发布订阅的框架,该框架主要是由Flow这个类提供的,他同样会在java.util.concurrent中出现,并且提供了Reactive编程模式。
12.UnifiedJVMLogging
该特性为JVM的所有组件引入了一个通用的日志系统,提供了JVM日志的基础设施,你可以不用专门为了打印某些日志而添加一些专门的标签,只需要使用统一的log指令即可,比如:
[java]viewplaincopyprint?java-Xlog:gc=debug:file=gc.txt:none...jcmd9615VM.logoutput=gc_logswhat=gc13.ImmutableSet
其实在Java的早期版本中就已经有这样的功能了,比如Collections.xxx就可以将某个collection封装成不可变,但是此次的Java9版本将其加到了对应的Set和List中,并且有一个专门的新包用来存放这些具体的实现java.util.ImmutableCollections,这一个特性和Scala真的如出一辙。
[java]viewplaincopyprint?Set<String>strKeySet=Set.of("key1","key2","key3");14.OptionalToStream
对Option提供了stream功能,关于Optional的用法,我在我的教程中讲的非常详细,如果你还没有掌握,抓紧啊
[java]viewplaincopyprint?List<String>filteredList=listOfOptionals.stream().flatMap(Optional::stream).collect(Collectors.toList());关于本次Java如何解决可见性和有序性的问题和为何java不建议使用sun包的问题分享到这里就结束了,如果解决了您的问题,我们非常高兴。