3

I have this Thread inside my Project which runs continously accepting new symbols

public class StreamThread extends Thread {
    private Set<String> allSymbolSet = new HashSet<String>(Arrays.asList("USBC", "TCSD", "PCLJ"));
    private PriorityBlockingQueue<String> priorityBlocking = new PriorityBlockingQueue<String>();

    public void addSymbols(String str) {
        if (str != null) {
            priorityBlocking.add(str);
        }
    }

    public void run() {
        while (true) {
            try {
                while (priorityBlocking.peek() != null) {
                    String symbol = priorityBlocking.poll();
                    allSymbolSet.add(symbol);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

my question is , i want to access the variable allSymbolSet from another class

what will be the best approach to get access to this variable named allSymbolSet from anoter class , for this i have two choices

  1. modify the access specifier of allSymbolSet from private to default .

  2. Write a get Method which is supoused to return the Set

Please suggest me , what will be the good approach in this case ??

  • 1
    More likely 2, but *why* does the other class try to access `allSymbolSet`, what does it want to do with it? – Joni Aug 02 '13 at 10:40

4 Answers4

2

Best approach would be the getter method AND synchronize the access to the object allSymbolSet, something like this:

public Set<String> getAllSymbolSet() {
    synchronized(allSymbolSet) {
        return allSymbolSet;
    }
}

and also synchronize the access to allSymbolSet inside your thread.

morgano
  • 17,210
  • 10
  • 45
  • 56
  • Thanks a lot , but why do i need to synchronize that ?? Is synchrnozation necessary in this case ?? –  Aug 02 '13 at 10:41
  • 1
    Yes, because you'll have two threads accesing the same HashSet, HashSet is not thread-safe – morgano Aug 02 '13 at 10:43
  • Do you really need synchronization if you only want to *read* the field? –  Aug 02 '13 at 10:44
  • @AtishDipongkor it is better because in the first case you have no control on what uses your field, with a getter you have better control on how share your object. In this case it is necessary to synchronize the access – morgano Aug 02 '13 at 10:46
  • @NishantShreshth Yes you need synchonization just to read. Or consider a thread safe(er) implementation http://stackoverflow.com/questions/6992608/why-there-is-no-concurrenthashset-against-concurrenthashmap – zapl Aug 02 '13 at 10:47
  • If you want to synchronize a collection, including a hash set, just use `Collections.synchronizedSet()`. – tbodt Aug 02 '13 at 10:48
  • @zapl What I think is synchronization will come into play only when you are going to modify the instance returned from the above method. –  Aug 02 '13 at 10:51
  • @tbodt I used to think the same, but the javadoc for Collections.synchronizedSet() says that anyway you have to synchronize on the set if you are going to iterate over it, so not much help using it – morgano Aug 02 '13 at 10:51
  • Reading a reference variable like this does not require synchronization. Even more, since nothing else uses `allSymbolSet` as a lock, the synchronized block achieves no effect. – Joni Aug 02 '13 at 10:54
  • @Joni that's why I specified that `allSymbolSet` should also be synchronized on the thread. Also, if you have an object accessed by more than one thread and the internal status is modified even by only one thread, then you need to synchronize it – morgano Aug 02 '13 at 10:58
  • @morgano i am creating the allSymbolSet this way , public Set allSymbolSet= Collections.synchronizedSet(new HashSet , do i still need to synchronize the getter method ?? –  Aug 02 '13 at 11:41
  • If the external thread that access `allSymbolSet` iterates over it (which is highly probably) then `Collections.synchronizedSet()` doesn't help much ( see [javadoc](http://docs.oracle.com/javase/7/docs/api/java/util/Collections.html) ) I think it is better for you to do a getter as @assylas recomends (returning a copy) – morgano Aug 02 '13 at 11:46
1

A few comments:

  • If you make the set non-private, some code could modify it (by mistake or on purpose) which could result in inconsistent behaviour in your StreamThread class. Don't do that.
  • Providing a simple getter does not solve the issue above. Prefer returning a copy of your set.
  • Make your variables final whenever you can when in a multi-threading environment - it solves many thread safety issues.
  • Prefer implementing Runnable than extending Thread
  • You will need to synchronize ALL accesses to your set (read and write), for example by using a synchronizedSet or even better by wrapping a ConcurrentHashMap which generally provides better performance.
  • instead of peek+poll you can simply take from your queue

So your final class could look like:

public class StreamTask implements Runnable {

    private final Set<String> allSymbolSet;
    private final PriorityBlockingQueue<String> priorityBlocking = new PriorityBlockingQueue<String>();

    public StreamTask() {
         String[] symbols = {"USBC", "TCSD", "PCLJ"};
         //use a thread safe set, for example based on ConcurrentHashMap
         allSymbolSet = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean> ());
         Collections.addAll(allSymbolSet, symbols);
    }

    public void addSymbols(String str) {
        if (str != null) {
            priorityBlocking.add(str);
        }
    }

    public Set<String> getSymbols() {
        return new HashSet<> (allSymbolSet); //return a copy
    }

    public void run() {
        while (true) {
            try {
                allSymbolSet.add(priorityBlocking.take());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

Finally, I might be missing something, but that class looks equivalent to the much simpler:

public class StreamTask {

    private final Set<String> allSymbolSet;

    public StreamTask() {
         String[] symbols = {"USBC", "TCSD", "PCLJ"};
         //use a thread safe set, for example based on ConcurrentHashMap
         allSymbolSet = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean> ());
         Collections.addAll(allSymbolSet, symbols);
    }

    public void addSymbols(String str) {
        if (str != null) {
            allSymbolSet.add(str);
        }
    }

    public Set<String> getSymbols() {
        return new HashSet<> (allSymbolSet); //return a copy
    }
}
assylias
  • 321,522
  • 82
  • 660
  • 783
  • +1 for also creating the copy when returning the Set. Nice answer. – Narendra Pathai Aug 02 '13 at 10:55
  • if i use the final keyword , will it allow it to modify the Set and PriorotyBlockingQueue ?? –  Aug 02 '13 at 11:29
  • 1
    @Kiran You can add to or remove from a final set, but you can't reassign it. So you can't write: `allSymbolSet = new HashSet<>()` after construction. – assylias Aug 02 '13 at 11:30
  • thanks a lot , one more question please , i am creating the allSymbolSet this way , public Set allSymbolSet= Collections.synchronizedSet(new HashSet , do i still need to synchronize the getter method ?? –  Aug 02 '13 at 11:39
  • @Kiran If you return a copy like I proposed then yes you need synchronization because the copy creation involves an iteration which needs to be synchronized manually. That's one of the reasons why I suggested using a wrapper around a ConcurrentHashMap instead, which has a thread safe iterator. – assylias Aug 02 '13 at 11:41
0

Better way is method 2. Writing a getter method. If you want to allow set the values then use a setter later. Then your data will be encapsulated .

Sanjaya Liyanage
  • 4,706
  • 9
  • 36
  • 50
0

Write a get Method which is supposed to return the Set. by using this your private remains private and you also access it from outside using Object of the same class.

Narendra Pathai
  • 41,187
  • 18
  • 82
  • 120