5

Say I have a server with multiple threads that share a reference to a Data instance. Quick eg,

edit1: Updated for readability

public void main() {
 Data data = new Data();
 ReentrantLock rl = new ReentrantLock(true);
 ReadThread t1 = new ReadThread(data, rl);
 UploadThread t2 = new UploadThread(data, rl);
 t1.start(); t2.start();
}

class ReadThread extends Thread {
private Data data;
private ReentrantLock lock;

ReadThread(Data d, ReentrantLock l){ data = d; lock = l; }

     void run(){
        lock.lock();
        try{
        data.put(aString)
        } finally { lock.unlock(); }
     }
}

class UploadThread extends Thread {
private Data data;
private ReentrantLock lock;

UploadThread(Data d, ReentrantLock l){ data = d; lock = l; }

     void run(){
        lock.lock();
        try{
        data.put(aString)
        } finally { lock.unlock(); }
     }
}

Is it better to use locks like above, or synchronize the put method like below,

class Data {
 private LinkedList<String> data = new LinkedList<String>();
 synchronized void put(String x){ data.add(x); }
}

This is pretty rough,

I'm mostly just concerned about the concurrency.

With the synchronized method am I correct in assuming the synchronization would occur on the class' "Data" instance/object? So one UploadThread could call the put procedure/method and one ReadThread could do the same in parallel. However using the ReentrantLock example, only one thread would be able to execute a put call at any one time?

What would happen if in the "Data" class I made the LinkedList static and I made the put method synchronized and static? Which is the best approach? Do I lose mut ex if I make things static?

I2obiN
  • 181
  • 3
  • 18
  • 5
    You are using `Lock` wrong. Given that, use `synchronized`. Or read more. Much more. – Boris the Spider Nov 29 '16 at 19:19
  • 2
    Also, don't `extends Thread`. – Boris the Spider Nov 29 '16 at 19:20
  • 1
    Also, format your code. Please. This is nigh on illegible. – Boris the Spider Nov 29 '16 at 19:20
  • I understand you have to surround it in a try-catch. This is rough pseudo code. I'm basically asking whether synchronized locks on the object reference that's passed in the constructor or on the actual object itself. – I2obiN Nov 29 '16 at 19:22
  • 4
    Use `synchronized`, unless you have a specific problem that you know `synchronized` won't solve. – President James K. Polk Nov 29 '16 at 19:22
  • `synchronized` locks on whatever you tell it to. Your question doesn't really make much sense. `Lock` should be used in a `try ... finally`; `catch` has very little to do with anything here. Start reading [here](https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html) and please, please don't give up before you reach the end of the trail. – Boris the Spider Nov 29 '16 at 19:23
  • "First, it is not possible for two invocations of synchronized methods on the same object to interleave". So if that object is a reference variable, they will lock based on that object, NOT the reference object correct? Thus ReadThread and UploadThread will access put in parallel, versus the lock which will only allow either UploadThread or ReadThread to execute a put call. Is this a correct assumption? – I2obiN Nov 29 '16 at 19:31
  • 1
    And, the `lock()` call should be _outside_ the try block, not inside. – Brian Goetz Nov 29 '16 at 19:31
  • Sorry my bad, will update – I2obiN Nov 29 '16 at 19:32
  • 6
    `ReentrantLock` is an "expert" feature, for when you have a situation that `synchronized` does not support (timed wait, polled wait, multiple condition queues, non block-structured locking.) None of those apply. Use `synchronized`. – Brian Goetz Nov 29 '16 at 19:34
  • "When a thread invokes a synchronized method, it automatically acquires the intrinsic lock for that method's object." Okay I believe this is what I was looking for, thank you for the info guys. – I2obiN Nov 29 '16 at 19:59

1 Answers1

9

In Java synchronized sections are reentrant. This means that a single thread can enter the synchronized section as many times as required, but a new thread can only enter when no other threads are present. A thread currently within these sections has acquired a lock and will only return the lock upon leaving all synchronized sections. Apart from declaring synchronized through the method signature, synchronized can also be called on objects directly. For example; these two methods will have the same effect:

synchronized public void foo() {

}

public void foo() {
    synchronized(this) {

    }
}

The ReentrantLock is very similar to synchronized in that only one thread can acquire the lock at one time. If a thread reaches a lock.lock() statement it will wait until the lock is unlocked by another thread. If the thread already has the lock it will continue. This can be useful in more complicated situations where a single synchronized code block isn't enough.

What would happen if ... I made the put method synchronized and static?

If the method is static synchronized that means you are locking the class itself and not the instance of the class. It is locked independently of an instance synchronized method.


For your code:

The easiest thing to do here would be to make the Data object into a thread safe object. If you are not able to edit the code for this class, then one valid strategy would be to wrap the object in a thread safe wrapper.

interface Foo {
    void bar();
} 
class UnsafeFoo implements Foo {
    @Override bar() { ... }
}
class ThreadSafeFoo implements Foo {
    Foo foo;
    ThreadSafeFoo(Foo foo) { this.foo = foo; } 
    @Override synchronized bar() { foo.bar(); }
}

Foo unsafe = new UnsafeFoo();
Foo safe = new ThreadSafeFoo(unsafe);
flakes
  • 21,558
  • 8
  • 41
  • 88
  • 1
    Thank you for your answer regarding `static synchronized`, for what I'm doing I can edit the class so that should be fine. Appreciate it :) – I2obiN Nov 29 '16 at 20:13