0

I have a tableview that contains a list of objects, which pull metadata from files on the harddrive.

The list can easily be 5000 items long, and has to be updated any time the hard-drive data changes. It can also be updated if the user provides an additional directory to build the dataset from. In short, the data on the tableview is liable to updated regularly.

I currently have a single thread that handles all updates. It uses a combination of a few different techniques (persistence between sessions, a FileWalker to add new data, and WatchService to watch for updates during a session). Unfortunately, this thread is not the JavaFX thread, so when I update the ObservableList backing the TableView, I can occasionally get a ConcurrentModificationException.

The basic shape of the background daemon thread a while ( true ) loop, because it polls the various places updates can come from, and reacts accordingly.

Platform.runLater() does not work seem to work for this large dataset. I tried having the background thread keep a list of objects to add and remove, and then requesting that the javafx thread update the ObservableList by reading the addMe and removeMe lists, but that didn't work -- the runLater() threads stopped being called during a large read of data, and so the tableview wouldn't update for 6+ minutes until all data was done being read by the background thread. Only after that would the runLater() finally be called. I avoided calling unncessary runLater()'s by checking to see if one update request was already pending, but pruning the number of requests didn't have any impact on the result.

Is there any way to have a constantly running thread like this in the background, and have it update the dataset for a JavaFX UI? I feel like there must be some way to do this, but I can't find anything that works.

Grumblesaurus
  • 3,021
  • 3
  • 31
  • 61
  • Your description really just implies that you are managing your threads incorrectly. It's not really going to be possible to help without an actual code example - a [MCVE] - no-one can diagnose a description of code. – James_D May 23 '17 at 17:47
  • https://stackoverflow.com/questions/44099841/javafx-observablelist-adding-items-causes-concurrentmodificationexception – Grumblesaurus May 23 '17 at 17:48
  • That didn't seem to garner any responses, so I tried generalizing the question to make it easier to read and understand. – Grumblesaurus May 23 '17 at 17:48
  • I ignored that question because you didn't show the important part of the code: the thread and how often you were trying to submit something to `Platform.runLater()`. (Plus, you made everything static, which tells me you're probably not at the point in your Java career to be tackling multithreading...). If you want help, create a new project that *only* tries to change data from a background thread and see if you can get that to work. If you can, you are most of the way to solving the problem. If you can't, post *that* complete project (only a few dozen lines, but complete). – James_D May 23 '17 at 17:54
  • Basically, there could be any number of things wrong, e.g. you're not really running on a background thread and you're blocking the JavaFX Application Thread, or you're flooding the JavaFX Application Thread with more `Platform.runLater(...)` calls than it can handle, or you're accidentally submitting background work to `Platform.runLater(...)`, or etc etc. 5000 items is not that many: I have some proof of concept code where `TableView` happily manages ~1,000,000 items. So the size of the lists is not the problem. – James_D May 23 '17 at 17:59
  • "(Plus, you made everything static, which tells me you're probably not at the point in your Java career to be tackling multithreading...)" -- I understand that concern. It's unfounded in this case. If we talked for 10 minutes on that topic alone, I think you'd be convinced that my design structure here is fine. In either case, please put aside that bias, it's not founded in fact. – Grumblesaurus May 23 '17 at 18:04
  • "or you're flooding the JavaFX Application Thread with more Platform.runLater(...) calls than it can handle" -- This is almost certainly the case. Any suggestion on how to fix that? – Grumblesaurus May 23 '17 at 18:06
  • 1
    OK, fair enough. But the main point still stands, for both questions: there's no way to answer either one as they stand. The problem likely arises in some part of the implementation of the threading, which isn't included in either case. You've adequately described the solution (background thread loops and polls for changes, submits updates to the FX Application Thread via `Platform.runLater()` as they occur).\ – James_D May 23 '17 at 18:06
  • I will submit another question with my exact runLater() code. I had it setup so only one runLater() was queued at a time, and it still stopped executing. I'll post that exact code, though, and perhaps that will help find the mistake I'm making. – Grumblesaurus May 23 '17 at 18:08
  • 1
    Flooding the FX Application thread: there are a few questions around on this. Try https://stackoverflow.com/questions/24116858/most-efficient-way-to-log-messages-to-javafx-textarea-via-threads-with-simple-cu There maybe others, but the basic idea is to put changes in a queue and consume the queue from time to time. – James_D May 23 '17 at 18:09
  • And I still think "I'll post my exact code" is not really a useful approach here. Divide and conquer: create a small application whose *only* purpose is to try to make a lot of updates to a (very simple) table from a background thread. That will be much more likely to get responses. – James_D May 23 '17 at 18:25
  • Maybe the approach posted in my answer here helps? https://stackoverflow.com/a/40678129/2991525 – fabian May 23 '17 at 18:51
  • Why not using a `Task` which is very recommended to run complex-long calculations in the background, also it has a useful methods like `setOnSucceeded()` and `setOnFailed`. – Yahya May 23 '17 at 22:37
  • @James_D: Your suggestion that I was flooding the FX application thread was the right diagnosis. Last night I tried ensuring that there was only one runLater() pending at a time (but that there was almost always exactly one). This, apparently, was too much. Today I setup another thread that ensures there's a minimum delay of 1 second between the runLater()s, and it works perfectly. I'm going to fine tune that 1 second down a bit, but thank you. If you type up a real answer, I'd be glad to accept. – Grumblesaurus May 24 '17 at 01:28
  • Maybe try an `AnimationTimer`, so you can perform exactly one update per frame rendering...? – James_D May 24 '17 at 01:29

0 Answers0