2

My main thread has a private LinkedList which contains task objects for the players in my game. I then have a separate thread that runs every hour that accesses and clears that LinkedList and runs my algorithm which randomly adds new uncompleted tasks to every players LinkedList. Right now I made a getter method that is synchronized so that I dont run into any concurrency issues. This works fine but the synchronized keyword has a lot of overhead especially since its accessed a ton from the main thread while only accessed hourly from my second thread.

I am wondering if there is a way to prioritize the main thread? For example on that 2nd thread I could loop through the players then make a new LinkedList then run my algorithm and add all the tasks to that LinkedList then quickly assign the old LinkedList equal to the new one. This would slightly increase memory usage on the stack while improving main thread speed.

Basically I am trying to avoid making my main thread use synchronization when it will only be used once an hour at most and I am willing to greatly degrade the performance of the 2nd thread to keep the main threads speed. Is there a way I can use the 2nd thread to notify the 1st that it will be locking a method instead of having the 1st thread physically have to go through all of the synchronization over head steps? I feel like this would be possible since if that 2nd thread shares a cache with the main thread and it could change a boolean denoting that the main thread has to wait till that variable is changed back. The main thread would have to check that boolean every time it tries run that method and if the 2nd thread is telling it to wait the main thread will then freeze till the boolean is changed.

Of course the 2nd thread would have to specify which object and method has the lock along with a binary 0 or 1 denoting if its locked or not. Then the main thread would just need to check its shared cache for the object and the binary boolean value once it reaches that method which seems way faster than normal synchronization. Anyways this would then result in them main thread running at normal speed while the 2nd thread handles a bunch of work behind the scenes without degrading main thread performance. Does this exist if so how can I do it and if it does not exist how hard would it actually be to implement?

  • Some example code would help this Question. In particular, I doubt your "getter method that is synchronized" is actually thread-safe, but I can only guess. – Basil Bourque Aug 27 '22 at 07:24
  • @BasilBourque Well I store a concurrent map filled with Data objects each of those Data objects contain a LinkedList with a synchronized getter that returns the LinkedList. I will always be accessing the same Object instance via that map and since it’s only one internal synchronized call and is the only way to reference that LinkedList it should be thread safe. But really my question above is about trying to find a new / better way do this to avoid synchronization overhead. – IDontWantToMakeAnotherAccount Aug 27 '22 at 11:52

1 Answers1

2

Premature optimization

It sounds like you are overly worried about the cost of synchronization. Doing a dozen, or a hundred, or even a thousand synchronizations once an hour is not going to impact the performance of your app by any significant amount.

If your concern has not yet been validated by careful study with a profiling tool, you’ve fallen into the common trap of premature optimization.

AtomicReference

Nevertheless, I can suggest an alternative approach.

You want to replace a list once an hour. If you do not mind letting any threads continue using the current list already accessed while you swap out for a new list, then use AtomicReference. An object of this class holds the reference to another object of a specified type.

I generally like the Atomic… classes for thread-safety work because they scream out to the reader that a concurrency problem is at hand.

AtomicReference < List < Task > > listRef = new AtomicReference<>( originalList ) ;

A different thread is able to replace that reference to the old list with a reference to the new list.

listRef.set( newList ) ;

Access by the other thread:

List< Task > list = listRef.get() ; 

Note that this approach does not make thread-safe the payload, the list itself. But you claim that only a single thread will ever be manipulating the content of the list. You claim a different thread will only replace the entire list. So this AtomicReference serves the purpose of replacing the list in a thread-safe manner while making the issue of concurrency quite obvious.

volatile

Using AtomicReference accomplishes the same goal as volatile. I’m wary of volatile because (a) its use may go unnoticed by the reader, and (b) I suspect many Java programmers do not understand volatile, especially since its meaning was redefined.


For more info about why plain reference assignment is not thread-safe, see this Question.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Anyways back on track to my question above would making my LinkedList volatile fix my issues? I think it should since its being written to by one thread and read to by another thread. But that being said what is the performance difference between making my LinkedList volatile and making my LinkedList's getters synchronized? I think volatile fields take 10x longer to access than non volatile variables (like 100ns vs 10ns) but I am not sure how much synchronization effects performance and how would that compare to AtomicReference which also seems better than synchronization. – IDontWantToMakeAnotherAccount Aug 28 '22 at 22:42
  • Also with AtomicReference do I just access my LinkedList from the main thread with .get() and from my second thread with .set() meaning I dont ever actually call my original LinkedList? Does making that LinkedList final effect anything? Do I first need to fill the LinkedList before I make a reference or can I reference is the original LinkedList just never touched? Also the main thread does load the objects when a player logs in but after that only the 2nd thread will load it. If these 2 events occur at the same time would that break this AtomicReference approach? – IDontWantToMakeAnotherAccount Aug 28 '22 at 22:48
  • The important point is that the thread that regenerates the list every hour needs to generate a _new_ list instance and then swap that in atomically. The main thread would re-read the reference each time. – Tim Moore Aug 29 '22 at 02:22
  • I think a `volatile` field would be just as suitable as an `AtomicReference` in your use case. See https://stackoverflow.com/q/281132/29470 – Tim Moore Aug 29 '22 at 02:30
  • 1
    As long as you only use `get` and `set`, the `AtomicReference` is not different to a `volatile` field. But in this scenario, even the costs of synchronization wouldn’t be higher than those of a `volatile` field. What sounds expensive here, is [the use of `LinkedList`](https://stackoverflow.com/q/322715/2711488)… – Holger Aug 30 '22 at 12:29