Is it safe in Java to read (not modify) objects wh

2019-08-13 04:47发布

there was already a question whether threads can simultaneously safely read/iterate LinkeList. It seems the answer is yes as far as no-one structurally changes it (add/delete) from the linked list.

Although one answer was warning about "unflushed cache" and advicing to know "java memory model". So I'm asking to elaborate those "evil" caches. I'm a newbie and so far I still naively believe that following code is ok (at least from my tests)

public static class workerThread implements Runnable {
    LinkedList<Integer> ll_only_for_read;
    PrintWriter writer;
    public workerThread(LinkedList<Integer> ll,int id2) throws Exception {
        ll_only_for_read = ll;
        writer = new PrintWriter("file."+id2, "UTF-8");
    }
    @Override
    public void run() {
        for(Integer i : ll_only_for_read) writer.println(" ll:"+i);
        writer.close();
    }
}

public static void main(String args[]) throws Exception{
    LinkedList<Integer> ll = new LinkedList<Integer>();
    for(int i=0;i<1e3;i++) ll.add(i);
    // do I need to call something special here? (in order to say:
    // "hey LinkeList flush all your data from local cache
    // you will be now a good boy and share those data among
    // whole lot of interesting threads. Don't worry though they will only read
    // you, no thread would dare to change you"
    new Thread(new workerThread(ll,1)).start();
    new Thread(new workerThread(ll,2)).start();
}

8条回答
ゆ 、 Hurt°
2楼-- · 2019-08-13 05:05

Although one answer was warning about "unflushed cache" and advicing to know "java memory model".

I think you are referring to my Answer to this Question: Can Java LinkedList be read in multiple-threads safely?.

So I'm asking to elaborate those "evil" caches.

They are not evil. They are just a fact of life ... and they affect the correctness (thread-safety) reasoning for multi-threaded applications.

The Java Memory Model is Java's answer to this fact of life. The memory model specifies with mathematical precision a bunch of rules that need to be obeyed to ensure that all possible executions of your application are "well-formed". (In simple terms: that your application is thread-safe.)

The Java Memory Model is ... difficult.

Someone recommended "Java Concurrency in Practice" by Brian Goetz et al. I concur. It is the best textbook on the topic of writing "classic" Java multi-threaded applications, and it has a good explanation of the Java Memory Model.

More importantly, Goetz et al gives you a simpler set of rules that are sufficient to give you thread-safety. These rules are still too detailed to condense into StackOverflow answer ... but

  • one of the concepts is "safe publication", and
  • one of the principles is to use / re-use existing concurrency constructs rather than to roll your own concurrency mechanisms based on the Memory Model.

I'm a newbie and so far I still naively believe that following code is ok.

It >>is<< correct. However ...

(at least from my tests)

... testing is NOT a guarantee of anything. The problem with non-thread-safe programs is that the faults are frequently not revealed by testing because they manifest randomly, with low probability, and often differently on different platforms.

You cannot rely on testing to tell you that your code is thread-safe. You need to reason1 about the behaviour ... or follow a set of well-founded rules.


1 - And I mean real, well-founded reasoning ... not seat-of-the-pants intuitive stuff.

查看更多
干净又极端
3楼-- · 2019-08-13 05:15

If your code created and populated the list with a single thread and only in a second moment you create other threads that concurrently access the list there is no problem.

Only when a thread can modify a value while other threads try to read the same value can happens problems.

It can be a problem if you change the object you retrieve (also if you don't change the list itself).

查看更多
爱情/是我丢掉的垃圾
4楼-- · 2019-08-13 05:18

Yes, in your specific example code it's okay, since the act of creating the new thread should define a happens-before relationship between populating the list and reading it from another thread." There are plenty of ways that a seemingly-similar set up could be unsafe, however.

I highly recommend reading "Java Concurrency in Practice" by Brian Goetz et al for more details.

查看更多
男人必须洒脱
5楼-- · 2019-08-13 05:20

You need to guarantee happens-before relationship between reads and writes in your LinkedList because they are done in separate threads.

Result of ll.add(i) will be visible for new workerThread because Thread.start forms happens-before relationship. So your example is thread safe. See more about happens-before conditions.

However be aware of more complex situation, when LinkedList is read during iteration in worker threads and at the same time it is modified by the main thread. Like this:

for(int i=0;i<1e3;i++) {
    ll.add(i);
    new Thread(new workerThread(ll,1)).start();
    new Thread(new workerThread(ll,2)).start();
}

This way ConcurrentModificationException is possible.

There are several options:

  1. Clone your LinkedList inside of workerThread and iterate the copy instead.
  2. Use synchronization both for list modification and for list iteration (but it will lead to poor concurrency).
  3. Instead of LinkedList use CopyOnWriteArrayList.
查看更多
Evening l夕情丶
6楼-- · 2019-08-13 05:21

The way you're using it is fine, but only by coincidence.

Programs are rarely that trivial:

  • If the List contains references to other (mutable) data, then you'll get race conditions.
  • If someone modifies your 'reader' threads later in the code's lifecycle, then you'll get races.

Immutable data (and data structures) are by definition thread-safe. However, this is a mutable List, even though you're making the agreement with yourself that you won't modify it.

I'd recommend wrapping the List<> instance like this so the code fails immediately if someone tries to use any mutators on the List:

List<Integer> immutableList = Collections.unmodifiableList(ll); //...pass 'immutableList' to threads.

Link to unmodifiableList

查看更多
smile是对你的礼貌
7楼-- · 2019-08-13 05:24

If you only access to the list is by 'read' methods (including iterations) then you are fine. Like in your code.

查看更多
登录 后发表回答