使用线程时,我有时将它们想象为在空间上下文中将对象之间的 3 维或更多维互连编织在一起。这不是一般的用例场景,但对于我所做的事情来说,这是一种有用的思考方式。
4 回答
您是否使用任何 API 来帮助线程化?
你是说 appart from java.util.concurrent
? FunctionalJava有一些有助于并发编程的结构,如从此处开始的多部分教程中所述。
您是否以不将线程概念化为进程的方式使用线程?
是的,就线程根本没有概念化的程度而言。以异步任务运行器为例。它在封面下使用线程,但我看不到它们,我也不关心它们。它们完全由任务运行器管理。
在幕后,这一切都只是线程,但是当我们不再关心单个线程而只是将它们视为许多插槽时,您可以以某种方式将代码放入其中并让它运行一段时间,那么就是当我们开始达到更高层次的抽象。
Agents/Actors 是执行此操作的常用方法。一个 Actor 就像一个拥有大量状态的线程,然后您可以向它发送一些代码并说“当您有时间时对您的状态执行此操作”或类似的内容。
首先
通常的免责声明:任何语言、使用任何抽象级别的并发编程都是困难和复杂的,并且有很多风险。考虑到:
- 并发编程使任何应用程序复杂化
- 单元测试关键部分很难,有时甚至是不可能的
- 重现源自并发代码的错误非常困难,并且很大程度上取决于架构、操作系统风格、版本等......
Java 并发 API
Java 在使开发人员尽可能容易地进行并发编程方面已经走了很长一段路。在大多数情况下,您会看到它java.util.concurrent
具有您需要的大部分抽象:
Runnable
Thread
可以扩展的接口和对象。只需输入您的代码,您就有一个准备好运行的线程- 一组不错的
Executors
:常量池、动态池、调度或其他。只需向它扔一个Runnable
,它就会完成其余的工作。 Semaphore
s和各种锁使您无需实现常见的锁定技术。wait()
适用于所有对象的内置notify()
API。
用途
作为软件工程师,剩下的唯一事情就是确保您编写正确的代码。这意味着您应该意识到自己可能面临的危险情况:
- 死锁- 两个或多个线程正在等待无序资源的情况,呈现无限等待循环。
- 活锁- 两个或多个线程在共享资源上礼貌地尝试让位于另一个但最终不接受它(考虑走廊上的两个人走到彼此并不断地从一侧移动到另一侧)
- 饥饿- 单个线程占用大部分或全部单个共享资源,从而剥夺其他线程对其的访问。
要点(或,何时使用)
仅当并发性将直接改善您的应用程序行为时才使用线程。
如果您正在等待 IO/网络/硬件绑定资源,请在其上生成一个线程,以便您可以继续做其他事情。
如果您只是想优雅地拆分 CPU 密集型计算,请不要使用线程。你最终可能会恶化你的表现。
如果您确实使用线程,请确保您已彻底考虑过风险,并三重检查您没有错过任何异常情况。
有用的(在线)资源
最快的方法是学习Sun 并发教程。除此之外,买一本好书。
祝你好运 :)
并发是一个深刻而复杂的话题。Java Concurrency in Practice等书籍可能会有所帮助。
有关线程的 API,请参阅并发实用程序概述。例如, BlockingQueue<E>可能很有用。
一个队列,它还支持在检索元素时等待队列变为非空,并在存储元素时等待队列中的空间变为可用的操作。
一种同步辅助,允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。
和CyclicBarrier用于一些有趣的行为。
一种同步辅助工具,它允许一组线程相互等待以达到共同的障碍点。
编辑:我现在正在阅读 Java Concurrency in Practice。这很好。
使用线程时,我有时将它们想象为在空间上下文中将对象之间的 3 维或更多维互连编织在一起。
听起来确实很复杂,例如,您将如何概念化 600 个线程?为什么不将它们视为显然同时运行的多个执行线程。
您是否使用任何 API 来帮助线程化?
我建议最简单和最直接的方法是你会找到的第一个匹配项。http://www.google.co.uk/search?q=java+threads
您是否以不将线程概念化为进程的方式使用线程?
由于线程不是进程,我不能说我曾经将线程视为进程。(旧版本的 Linux 除外)默认情况下,进程不共享内存/对象,它们完全独立运行(通常是不同的程序,可能用不同的语言编写)它们也使用不同的 API 以不同的方式启动。
有一种观点认为多线程很复杂。其实我想说的恰恰相反。多线程编程要求您使您的代码简单、易于理解和直接推理。虽然这需要经验,但您的目标是简单。