Download 类是否不适合不变性?
我有一个用来下载的课程。该类是可观察的,我也将其用作 swing UI 模型对象。然而,它太慢了,无论是启动(从http握手开始,我想我对此无能为力)还是同步访问。 该类的接口是:
public File getDownloadedFile()
public String getMimeType()
public synchronized BigInteger getExpectedSize()
public URL getURL()
public synchronized boolean cancel()
public synchronized boolean retry()
public synchronized boolean isDone()
public synchronized boolean isCancelled()
public synchronized BigInteger getProgress()
public String getName()
public synchronized BigInteger getBytesRead()
public synchronized BigDecimal getBytesPerSecond()
public synchronized BigDecimal getTimeRemaining()
public synchronized void start()
我想消除那里的同步,但我知道要做到这一点,我必须找到一种方法来使该类不可变。我尝试将其建模为状态机(内部隐藏),但是中间(运行)状态有一个 InputStream + 一个线程。将其变成不可变包装器的正确方法是什么? 1)将类变成状态机+不可变状态的包装器。让 start 启动第一个机器状态,然后用不可变的副本通知观察者每个状态变化。 (然而,客户对单一类的期望 - 对于前的 swing ui 模型来说,是有问题的。2
)放弃使用包装器作为 swing 模型。只需将我的控制器注册为观察者,启动并添加第一个(不可变)状态作为模型,并使用不可变状态标识符(例如下载 url)来识别由 notificationObservers() 发出的下载状态机状态属于某个下载,替换每个连续状态+更新用户界面。但是,我认为在没有通知的情况下使用它时,这不是很有用。至少有一种等待下载完成的方法会很好,不是吗?
I have a class that i use to do downloads. The class is observable and i use it as a swing UI model object too. However it is too slow, both to start (from the http handshaking i presume so not much i can do about it probably) and from synchronized access.
The interface of the class is:
public File getDownloadedFile()
public String getMimeType()
public synchronized BigInteger getExpectedSize()
public URL getURL()
public synchronized boolean cancel()
public synchronized boolean retry()
public synchronized boolean isDone()
public synchronized boolean isCancelled()
public synchronized BigInteger getProgress()
public String getName()
public synchronized BigInteger getBytesRead()
public synchronized BigDecimal getBytesPerSecond()
public synchronized BigDecimal getTimeRemaining()
public synchronized void start()
I want to eliminate the synchronized(s) there, but i know that to do so, i have to make a way to turn this class immutable. I tried to model this as a state machine (hidden internally), however the middle (running) state has a InputStream + a thread. What is the correct way to turn this into a immutable wrapper?
1) Make class into a wrapper for a state machine + the immutable state. Make start start the first machine state, then notify observers on each state change with a immutable copy. (however the client expectation of a monolitic class - for the model of the swing ui for ex, is problematic.
2) Give up on using the wrapper as swing model. just register my controller as a observer, start and add the first (immutable) state as a model and use a immutable state identifier (for example the download url) to recognize the download state machine state emitted by notifyObservers() as belonging to some download, to replace each successive state + update the ui. However i don't think this is very usable when this is used without interest in notification. At least a way to wait for the download to be complete would be nice no?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我认为你的第一个错误是试图将这个类变成 Swing 模型。查看方法列表,您执行此操作的唯一原因似乎是让某些 Swing 组件询问对象以报告进度。这违反了面向对象设计的“告诉,不要问”原则。
因此,创建一个单独的
DownloadProgress
对象,并让您的Download
对象定期更新它。由于该对象只包含数据字段,因此它的访问器可以同步。如果您希望它向 Swing 组件推送通知(再次强调,告诉不要询问),那么您的访问器使用 SwingUtilities.invokeLater()。这就剩下控制实际的下载了。如果您使用 信号量,那么您可以从一个线程(我假设是 Swing 事件调度线程)设置它并从另一个线程(实际执行读取的线程)读取它。您可能需要采取更严厉的措施,例如关闭读卡器下方的插座。然而,这是与同步完全不同的主题。
哦,是的,您正在后台线程中执行下载,对吗?不在 Swing 事件调度线程上?
I think your first mistake is trying to turn this class into a Swing model. Looking at the method list, it appears that the only reason you've done this is to let some Swing component interrogate the object to report progress. That violates the "tell, don't ask" principle of OO design.
So create a separate
DownloadProgress
object, and have yourDownload
object update it on a regular basis. Since this object just contains data fields, its accessors can be synchronized. If you want it to push out notifications to the Swing component (again, tell don't ask), then your accessors queue up a callback using SwingUtilities.invokeLater().That leaves controlling the actual download. If you use a Semaphore, then you can set it from one thread (I'll assume the Swing event dispatch thread) and read it from another (the thread that's actually doing the reading). You may have to take more drastic action, such as closing the socket underneath the reader. However, that's a completely different topic from synchronization.
Oh yeah, you are performing the download in a background thread, right? Not on the Swing event dispatch thread?
从我的角度来看,如果你要让这个类只能下载一个文件,那么它基本上是不可变的。是的,某些状态可能会作为其工作的副产品而发生变化,但所有变化都是内部的。严格来说,它不是不可变的,因为它具有会改变的状态,但从实践的角度来看,它是不可变的,因为它没有允许程序员改变这些状态的方法。
我想我不太明白你的总体目标。报告剩余下载时间的代码是否正在获取剩余时间的固定快照(例如),或者显示文件名的方法是否也无法取消下载(例如)?
From my perspective if you were to make it so that this class can only download one file it is basically immutable. Yes some of the state may change as a by product of doing its work, but all of the changes are internal. Strictly speaking it is not immutable since it has state that changes, but from a practical point of view it is immutable since it has no methods that allow a programmer to alter those states.
I think I do not quite understand your overall goal. Is it so that the code that is reporting the remaining download time is getting a fixed snapshot of the time remaining (for example) or is it so that the method displaying the file name cannot also cancel the download (for example)?
API 的构造方式意味着此类必须以某种方式封装可变状态。
然而,仅仅因为类是可变的,并不意味着您需要在每个方法上使用
synchronized
关键字。您应该能够限制同步代码内更小的块的需要,甚至可以通过使用一些
Atomic
类来完全消除它。我强烈建议阅读 Java 并发实践,我认为这是在 Java 程序中实现并发的权威著作。当然,它不会照亮最黑暗的角落,但它肯定会帮助您了解什么是安全的,什么是不安全的,以及在没有充分准备的情况下应该避免什么......
The way that API is structured means that this class has to encapsulate mutable state somehow.
However, just because the class is mutable, that doesn't mean you need to slap a
synchronized
keyword on every method.You should be able to limit your need to synchronize to much smaller blocks inside the code - or even eliminate it altogether by using some of the
Atomic
classes.I'd strongly recommend reading Java Concurrency in Practice, which I consider the definitive work on implementing concurrency in java programs. Sure, it doesn't shine a light into the darkest corners, but it will certainly help you understand what is safe, what is unsafe, and what you should avoid without significant preparation...
一个很好的答案。而且这个类被用作渲染器的查询,这是正确的。现在,我“解决”了通过将每个可变属性保存在不可变(状态机)易失性对象上(该对象在另一个线程上的(易失性)流的每次更新时更新)来使其不可变。因此,从流中获取此对象(一次),(ServiceLoader 实例化 - 因此一种 DI 形式)渲染器正在查询它以获取适当的字段 - 信息 - 以选择在我的 JDownloadList 组件上渲染的内容。尽管您的其他建议听起来很“告诉不要问”,但我不会遵循它,只是为了保持不同渲染器的灵活性,以不同的方式使用下载信息,而无需在渲染器上使用大量额外的设置器(即无论如何都有下载状态)。我只需要小心,在每次渲染时只请求一次对象,我应该是
能够删除同步。
正确的?正确的?
A good answer. And you're right on the money that this class is being used as a query for the renderer. Right now i "solved" making it immutable by holding each mutable property on a immutable (state-machine) volatile object (that gets updated on each update from the (volatile) stream on another thread). So getting this object from the stream (once) and the (ServiceLoader instantiated - so a form of DI) renderer is querying it for the appropriate fields - information - to choose what to render on my JDownloadList component. Even though your other advice is sound "tell don't ask" i'm not going to follow through on it, just to keep the flexibility of different renderers to use the download information differently without a lot of additional setter on the renderer (that has the download state anyway). I just have to be careful to only ask for the object once at each render and i should be
able to remove the synchronized.
Right? Right?