I am rewriting software that serves as a bridge between two services. Communication is done via sockets. The environment it has high data competition, especially a list with active sockets. The software has some problems because this competition has not been handled correctly, causing some information to be shown to be wrong, or not delivered at some point in the communication.
As I am rewriting, I have some questions about the best and safest scenario.
Imagine the following: Multiple threads accessing a single list, being able to add, remove and iterate several times in the same second.
To solve this problem, I have created the following structure:
ServerService (Where would be the list that would be accessed by all threads)
@Component
public class ServerService {
private CopyOnWriteArrayList<String> lista = new CopyOnWriteArrayList(new ArrayList<String>());
public void add(String string, String myThreadName){
System.out.println(myThreadName+". ADICIONANDO VALOR: "+string);
lista.add(string);
}
public void remove(String string, String myThreadName){
System.out.println(myThreadName+". REMOVENDO VALOR: "+string);
lista.remove(string);
}
void printAll(String myThreadName){
synchronized (lista){
lista.forEach(s -> System.out.println(myThreadName+". ITERANDO VALUE: "+s));
}
}
List<String> getAll(){
return lista;
}
}
MyRunnable (Where would be the control part of the sockets that would add, remove or 'iterate' the list)
public class MyRunnable implements Runnable{
ServerService serverService;
String myThreadName;
@Autowired
public MyRunnable(ServerService serverService, String myThreadName) {
this.serverService = serverService;
this.myThreadName = myThreadName;
}
@Override
public void run() {
while(true){
String value = Double.toString(ThreadLocalRandom.current().nextInt(0, 10));
serverService.add(value, myThreadName);
serverService.printAll(myThreadName);
serverService.remove(value, myThreadName);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
DemoApplication (Only one main to start)
@SpringBootApplication
public class DemoApplication implements ApplicationRunner {
@Autowired
ServerService serverService;
@Autowired
TaskExecutor taskExecutor;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(ApplicationArguments args) throws Exception {
taskExecutor.execute(new MyRunnable(serverService, "THREAD 1"));
taskExecutor.execute(new MyRunnable(serverService, "THREAD 2"));
}
}
So, will I have the correct bidding behavior on the list?
Another question, how would I 'iterate' the list inside the thread, in a synchronized way, so that another thread can not add to the list when another one is iterating? Would that be the right way?
@Override
public void run() {
while(true){
String value = Double.toString(ThreadLocalRandom.current().nextInt(0, 10));
serverService.add(value, myThreadName);
synchronized (serverService.getAll()){ --ALTERAÇÃO AQUI
serverService.getAll().stream().forEach(System.out::println);
}
serverService.remove(value, myThreadName);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
If necessary, I'll put more details.