Why, despite the synchronization, one time - First Name drops out earlier, and another time - null. In one of the outcomes, null is first displayed, and then First Name. How, after deleting the name, can we take and display First Name? Explain to me how to read this code correctly? For example, how does "return remove (0)" work? Thank.

package synchronized_collections; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class Main extends Thread { public static void main(String[] args) throws InterruptedException { NameList nameList = new NameList(); nameList.addName("First Name"); class MyThread extends Thread { @Override public void run() { System.out.println(nameList.removeFirstName() + "\nРазмер после удаления - " + nameList.size()); } } new MyThread().start(); new MyThread().start(); } static class NameList extends Thread { private List<String> list = Collections.synchronizedList(new ArrayList<>()); public synchronized void addName(String name) { list.add(name); } public synchronized String removeFirstName() { if(list.size() > 0) { return list.remove(0); } System.out.println("----------------------------"); return null; } public synchronized int size() { return list.size(); } } } 

    1 answer 1

    You have synchronized methods inside the NameList class, but in the MyThread stream, even though there is one line of code, you perform several different operations:

     System.out.println(nameList.removeFirstName() + "\nРазмер после удаления - " + nameList.size()); 

    This is equivalent to the following record:

     String s1 = nameList.removeFirstName(); String s2 = nameList.size(); String s3 = s1 + "\nРазмер после удаления - " + nameList.size(); System.out.println(s3); 

    As you can see, this is not an atomic operation, and is not synchronized into one block, so that between calls of nameList.removeFirstName () and nameList.size () of one thread, a call from another thread may well occur.

    UPD. For sequential execution of threads, you need to synchronize on some object common to all threads, for example:

     public class Main extends Thread { private static Object obj = new Object(); public static void main(String[] args) throws InterruptedException { NameList nameList = new NameList(); nameList.addName("First Name"); class MyThread extends Thread { @Override public void run() { synchronized (obj) { System.out.println(nameList.removeFirstName() + "\nРазмер после удаления - " + nameList.size()); } } } new MyThread().start(); new MyThread().start(); } static class NameList extends Thread { private List<String> list = Collections.synchronizedList(new ArrayList<>()); public void addName(String name) { list.add(name); } public String removeFirstName() { if (list.size() > 0) { return list.remove(0); } System.out.println("----------------------------"); return null; } public int size() { return list.size(); } } } 
    • Can you list a couple of options for how the program can work? And how to make it work predictably. How the hell do I have to first output First name, and then null, to synchronize run () did not help, the block too, use StringBuffer too (Maybe I used the wrong one). Thank. - Albert Petrov
    • @Albert Petrov supplemented the answer. In addition to this example, you can use the class Lock. - Vladimir Yarovoy
    • That is, the first thread comes out of the synchronized method, maybe it even needs to go to another synchronized method, but at this moment the second one can intervene and enter it instead of the first one? And about unsynchronized methods in general, you can remain silent, right? - Albert Petrov
    • @ Albert Petrov exactly. after the first thread has left the synchronized method, there are no locks anywhere, and at that moment all threads are equal, to whom the processor time will fall, it will be executed next. - Vladimir Yarovoy pm