I have following code for a chat server application in Java -
public synchronized List<ChatMessage> getMessages(int messageNumber) {
return messages.subList(messageNumber + 1, messages.size());
}
public synchronized int addMessage(ChatMessage c) {
messages.add(c);
return messages.size()-1;
}
I have following test code -
public static void main(String[] args) {
final ChatRoom c = new ChatRoom();
Thread user1 = new Thread(new Runnable() {
public void run() {
for(int i=0;i<1000;i++) {
c.addMessage(new ChatMessage());
c.getMessages(0);
}
}
});
Thread user2 = new Thread(new Runnable() {
public void run() {
for(int i=0;i<1000;i++) {
c.addMessage(new ChatMessage());
c.getMessages(0).size();
}
}
});
user1.start();
user2.start();
}
I am getting a ConcurrentModificationException.
How is this possible?
How is this possible?
Your getMessages method just returns a view on the original list. It doesn't create a copy of the list. So one thread is using a view on the list while another modifies the list - at that point, you get the exception.
From the docs for List.subList:
The semantics of the list returned by this method become undefined if the backing list (i.e., this list) is structurally modified in any way other than via the returned list. (Structural modifications are those that change the size of this list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.)
It's not clear what you're really trying to achieve here, but fundamentally you can't use subList to magically create a thread-safe list :)
The simplest thing to do is to create a combined method
public synchronized int addMessageAndGetCount(ChatMessage c) {
messages.add(c);
return messages.size();
}
public static void main(String... args) {
final ChatRoom c = new ChatRoom();
final Runnable runner = new Runnable() {
public void run() {
for(int i = 0; i < 1000; i++) {
c.addMessageAndGetCount(new ChatMessage());
}
}
};
new Thread(runner).start();
new Thread(runner).start();
}
You cannot safely return a list or a subList from a synchronized block. You can return a copy but all you need is the size.
Related
The following code triggers a ConcurrentModificationException very, very quickly:
import java.util.*;
public class SynchFail {
static List<Integer> LIST = new ArrayList<Integer>();
public static void main(String[] args) {
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
LIST.add(1);
}
}}).start();
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
List<Integer> syncList = Collections.synchronizedList(LIST);
synchronized(syncList) {
for (Integer thisInt : syncList) {
}
}
}
}}).start();
}
}
... whereas the following behaves as it should:
import java.util.*;
public class SynchSucceed {
static List<Integer> LIST = new ArrayList<Integer>();
public static void main(String[] args) {
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
synchronized(LIST) {
LIST.add(1);
}
}
}}).start();
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
synchronized(LIST) {
for (Integer thisInt : LIST) {
}
}
}
}}).start();
}
}
... my understanding was that synchronized collections were to prevent ConcurrentModificationExceptions in situations like this (but clearly they do not).
Given this: where should I make use of these ?
In the first code snippet, you have not followed the instructions in the documentation of synchronizedList:
In order to guarantee serial access, it is critical that all access to the backing list is accomplished through the returned list.
In the other thread, you are adding to the list via the original LIST, not the "returned list". LIST is just a normal ArrayList and calling add on it won't acquire any locks or anything like that, so add could still be successfully called while the iteration is in progress.
If you did:
final static List<Integer> LIST = Collections.synchronizedList(new ArrayList<>());
public static void main(String[] args) {
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
LIST.add(1);
}
}}).start();
new Thread(new Runnable() {
#Override
public void run() {
while (true) {
synchronized(LIST) {
for (Integer thisInt : LIST) {
}
}
}
}}).start();
}
Then it wouldn't throw a CME. When you call add on a synchronised list, it tries to acquire the intrinsic lock on LIST. If iteration is in progress, the lock would have been already held by the other thread (since you did synchronized (LIST) { ... } there), so it will wait until the iteration is over. Compare this with the second code snippet, and notice how this saves you from writing an extra synchronized (LIST) {} block around the add call.
Couple of things:
If you need sychronized access to an ArrayList you should use Vector instead. It does the same thing but its methods are syncrhonized.
in your case, the 2nd snippet works because you are syncing over the same object LIST in both threads
This is a pseudocode version of my current working code:
public class DataTransformer {
private final boolean async = true;
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
public void modifyAsync(Data data) {
if (async) {
executorService.submit(new Runnable() {
#Override
public void run() {
modify(data);
}
});
} else {
modify(data);
}
}
// This should actually be a variable inside modify(byte[] data)
// But I reuse it to avoid reallocation
// This is no problem in this case
// Because whether or not async is true, only one thread is used
private final byte[] temp = new byte[1024];
private void modify(Data data) {
// Do work using temp
data.setReady(true); // Sets a volatile flag
}
}
Please read the comments. But now I want to use Executors.newFixedThreadPool(10) instead of Executors.newSingleThreadExecutor(). This is easily possible in my case by moving the field temp inside modify(Data data), such that each execution has it's own temp array. But that's not what I want to do because i want to reuse the array if possible. Instead I want for each of the 10 threads a temp array. What's the best way to achieve this?
As static variable is shared between all Threads, so you could declare as static. But if you want to use different values then either use Threadlocal or use different object.
With ThreadLocal you could do :
ThreadLocal<byte[]> value = ThreadLocal.withInitial(() -> new byte[1024]);
You could also use object like this:
public class Test {
public static void main(String[] args) {
try {
Test test = new Test();
test.test();
} catch (Exception e) {
e.printStackTrace();
}
}
class Control {
public volatile byte[] temp = "Hello World".getBytes();
}
final Control control = new Control();
class T1 implements Runnable {
#Override
public void run() {
String a = Arrays.toString(control.temp);
System.out.println(a);
}
}
class T2 implements Runnable {
#Override
public void run() {
String a = Arrays.toString(control.temp);
System.out.println(a);
}
}
private void test() {
T1 t1 = new T1();
T2 t2 = new T2();
new Thread(t1).start();
new Thread(t2).start();
}
}
I am struggling to find how to remove an Element of a CopyOnWriteAccess without getting a thread exception.
Exception in thread "Thread-3649" java.lang.ArrayIndexOutOfBoundsException: Index 4 out of bounds for length 4
at java.base/java.util.concurrent.CopyOnWriteArrayList.elementAt(CopyOnWriteArrayList.java:386)
at java.base/java.util.concurrent.CopyOnWriteArrayList.remove(CopyOnWriteArrayList.java:479)
at com.mycompany.playmatehunter.Downloader.init(Downloader.java:28)
at com.mycompany.playmatehunter.Downloader.run(Downloader.java:19)
at java.base/java.lang.Thread.run(Thread.java:834)
I have the following Downloader class
public class Downloader extends Thread{
private CopyOnWriteArrayList<String> url_list;
private String chemin;
public Downloader( CopyOnWriteArrayList<String> l, String chemin)
{
this.url_list = l;
this.chemin = chemin;
}
public void run()
{
init();
}
public synchronized void init() // the list is already synchronized
{
if (!url_list.isEmpty())
{
int alea = (int)(Math.random()*url_list.size());
System.out.println(this.getName()+"removes the link nbr "+url_list.get(alea));
url_list.remove(alea);
}
}
}
And inside the main :
CopyOnWriteArrayList<String> index = new CopyOnWriteArrayList( FileToList("/index/index.txt") );
while( index.size() != 0)
{
List<Thread> tg = new ArrayList<Thread>();
for(int i=0;i<7;i++)
{
Thread t=new Thread(new Downloader(index, ""));
t.start();
tg.add(t);
}
for(Thread t: tg)
t.join();
}
Do you know how to get rid of ThreadException?
Thank you
Your access to the list is not synchronized. You have multiple threads, and even though the init() method is synchronized, the synchronization is on the thread instance, not on a common object, so it is useless. If you need to ensure mutual exclusion among threads, you have to synchronize on a common object:
public void init() // No synchronization here
{
synchronized(url_list) { // synchronize on a common object
if (!url_list.isEmpty())
{
int alea = (int)(Math.random()*url_list.size());
System.out.println(this.getName()+"removes the link nbr "+url_list.get(alea));
url_list.remove(alea);
}
}
}
I have to create a method to calculate the sum of all elements in an array. The caveat is that the array is divided into a number of parts for many threads to calculate these parts simultaneously, and then combine to calculate the sum
All of these are restricted to inside the method code. The problem is when I write:
Thread t = new Thread(()->{
int sum=0;
//do some calculations
//time to pass this result back to the main method
});
The local anonymous class can only access final or effectively final local variable of the main method, which means I can't create a local variable and then change it to update the result. I can't think of a way to pass a thread's result back to combine with the results from the other threads.
Is there any way to solve this?
You can divide up the work in your main thread and do something like this:
public class Foo implements Runnable {
private volatile CustomArray<Integer> arr;
private volatile Integer sum;
public Foo(CustomArray<Integer> arr) {
this.arr = arr;
}
#Override
public void run() {
synchronized(this.arr) {
sum = arr.getSum();
}
}
public Integer getValue() {
synchronized(this.arr) {
return sum;
}
}
}
And call from another thread like so:
CustomArray<Integer> completeArray = new CustomArray<>(data);
ArrayList<CustomArray<Integer>> dividedArrays = completeArray.divideWork();
for(CustomArray<Integer> each : dividedArrays) {
Foo foo = new Foo(each);
new Thread(foo).start();
// ... join through some method
Integer value = foo.getValue();
}
Or, you can use an Executor and a Callable:
public void test() throws InterruptedException, ExecutionException
{
ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<Integer> callable = new Callable<Integer>() {
#Override
public Integer call() {
return 2;
}
};
Future<Integer> future = executor.submit(callable);
// returns 2 or raises an exception if the thread dies
Integer output = future.get();
executor.shutdown();
}
Thread t = new Thread(new Runnable() { public void run() {} });
I'd like to create a thread this way. How can I pass parameters to the run method if possible at all?
Edit: To make my problem specific, consider the following code segment:
for (int i=0; i< threads.length; i++) {
threads[i] = new Thread(new Runnable() {public void run() {//Can I use the value of i in the method?}});
}
Based on Jon's answer it won't work, since i is not declared as final.
No, the run method never has any parameters. You'll need to put the initial state into the Runnable. If you're using an anonymous inner class, you can do that via a final local variable:
final int foo = 10; // Or whatever
Thread t = new Thread(new Runnable() {
public void run() {
System.out.println(foo); // Prints 10
}
});
If you're writing a named class, add a field to the class and populate it in the constructor.
Alternatively, you may find the classes in java.util.concurrent help you more (ExecutorService etc) - it depends on what you're trying to do.
EDIT: To put the above into your context, you just need a final variable within the loop:
for (int i=0; i< threads.length; i++) {
final int foo = i;
threads[i] = new Thread(new Runnable() {
public void run() {
// Use foo here
}
});
}
You may create a custom thread object that accepts your parameter, for example :
public class IndexedThread implements Runnable {
private final int index;
public IndexedThread(int index) {
this.index = index;
}
public void run() {
// ...
}
}
Which could be used like this :
IndexedThread threads[] = new IndexedThread[N];
for (int i=0; i<threads.length; i++) {
threads[i] = new IndexedThread(i);
}