前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >并发编程-到处都是线程!

并发编程-到处都是线程!

作者头像
ImportSource
发布2018-04-03 16:36:11
7840
发布2018-04-03 16:36:11
举报
文章被收录于专栏:ImportSource

即使是你在自己的program中从来没有显式的创建线程,框架也依然会代表你偷偷的创建一些线程。所以呢,被这些线程调用的代码也必须是线程安全的。这样的设计和实现给开发人员带来了一定的负担,因为开发线程安全的代码必须要更加的细心以及更多的分析,相对于非线程安全的代码来说。

每个Java的应用程序都会用到线程。当JVM启动后,JVM就会创建一些线程用于JVM的那些housekeeping任务(比如garbage collection以及finalization)以及创建一个主线程(main thread)用于运行main方法。AWT和Swing这些图形界面框架会创建一些线程用于管理用户界面事件(user interface events)。Timer会创建线程用于执行延迟的任务。还有一些组件框架,比如servlets以及RMI会创建线程池并在这些线程中调用组件里边的方法。

如果你使用这些工具,你就不得不去了解和熟悉并发和线程安全这些概念。因为这些框架创建线程并在他们的线程中调用你的组件。如果把并发看作是“可选的”或者是“高级”的语言功能自然是极好的,但现实告诉我们几乎所有的Java应用程序都是多线程的,而且这些框架并没有把你从适当的协同访问应用程序状态的需求中隔离出来。什么意思呢?就是说需要你对访问应用程序的状态进行协同。

当“并发”(concurrency)被框架引入到应用程序(application)的时候,你就不能仅仅把并发局限于框架代码,因为框架总会去回调应用程序的组件,而这些代码又会访问应用程序的状态。类似的,对于线程安全性的需求也不能仅仅局限于被框架调用的那些应用程序组件,而是要延伸到所有的那些被组件访问的程序的状态的代码路径。这样的话,线程的安全性需求是被传染的。就是链式蔓延的那个意思。

Timer。Timer提供了一个方便的机制,就是它调度任务们在稍后的某个时刻来运行,运行一次或者周期的运行。引入了Timer以后将会让串行的程序变得复杂,因为Timer Task们是被Timer放在另外一个单独的线程中来管理的,而不是应用程序来管理。如果一个TimerTask访问了被其它线程也访问的数据,那么不仅仅TimerTask要以线程安全的方式来访问数据,而且其它的任何访问这个数据的类都要采取线程安全的方式。通常解决这个问题的最简单的方法就是确保被TimerTask访问的对象本身是线程安全的就可以了,就是把线程安全这个能力封装在共享对象的内部就可以了。

Servlets and JavaServer Pages (JSPs)。Servlets 框架设计被用来处理deploy一个web应用程序以及分发来自远程HTTP客户端的request。一个到达server的request被分发的过程,也许要通过一连串的filter,最后到达对应的servlet或者JSP。每个servlet表示一个应用程序逻辑的组件(component),而且如果是高吞吐量的网站,多个client会一次请求同一个servlet的service。在Servlets的规范中,要求一个servlet可以同时被多个线程调用。也就是说,servlet们也必须是线程安全的。

当你build一个web应用程序,即使你能够确保一个servlet一次只被一个线程调用,你依然需要注意线程安全的问题。Servlet们通常需要访问和其它servlet共享的状态信息,比如应用程序域对象(application scoped objects,被存储在ServletContext中)或者会话域对象(session scoped objects,被存储在每个客户端的HttpSession中)。当一个servlet访问被多个servlets或多个request共享的对象时,那么它就必须要适当的协同的访问这些对象,因为多个request也许是分别来自不同线程,不同的线程的request同时访问这些对象。 Servlets 和 JSPs,以及servlet filter和在scoped containers中的(比如ServletContext和HttpSession)的对象,都必须是线程安全的。

Remote Method Invocation。RMI让你可以调用运行在其它JVM中运行的对象的方法。当你使用RMI调用一个远程方法,方法的参数会被打包到一个byte stream中,然后通过网络被运送到远程到JVM中,然后再把包拆开,把参数拿出来传给远程的方法。

当RMI的代码调用了你的远程对象,那么这个调用是在哪个线程中发生的呢?你是不知道的,但肯定不是在我们创建的线程中。而是在一个由RMI专门管理的一个线程中被调用的。那么这会儿你会想,RMI究竟创建了多少个线程呢?会不会出现同一个远程对象的同一个远程方法同时被多个RMI线程调用呢?

远程对象必须要警惕两个“线程安全性”问题:恰当的协同访问那些和其它对象共享的状态,以及恰当的协同访问远程对象自己的状态(因为相同的对象也许会被多个线程同时调用)。比如像servlets,RMI对象都应该为多次同时调用做好准备,并且必须提供他们自己的线程安全。

Swing and AWT。图形用户界面(GUI)应用程序天生就是异步的。用户会在任何时刻选择一个菜单选项或者按下一个按钮,而且他们希望应用程序马上就响应,即使这个时候应用程序正在投入的做一件其它事情,也要快速响应。Swing 和 AWT很好的解决了这个问题,他们通过创建一个单独的线程来专门负责处理用户触发的事件并更新图形界面呈现给用户。

Swing的组件中,比如JTable,并不是线程安全的。这个时候要想保证线程安全,替代方案就是,Swing的program会把所有对GUI组件的访问限制在事件线程中。如果一个应用程序想要在事件线程外面来操作GUI,那么它就必须把操控GUI的代码放在事件线程中来运行。

当用户进行一个UI的操作时,一个事件的handler就会在事件线程中被调用来处理用户请求的任何的操作。如果这个handler需要访问应用程序的状态(state),而且这个状态也被其它线程所访问(比如一个正在编辑的文档),那么这个事件handler,以及访问这个状态的任何其它的代码,都必须用一种线程安全的方式来访问这个状态。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2016-07-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 ImportSource 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档