Почему синхронизированная блокировка не работает при конкатенации строк - PullRequest
0 голосов
/ 11 июля 2020

Я использую следующий код. Я не могу добиться синхронизации. По моему мнению, здесь должна была сработать концепция пула строк.

Я хочу знать причину проблемы, а не ее альтернативы.

import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class SynchronizationBug {
    private static Map<String,Date> concurrentMap=new ConcurrentHashMap();
    
    public static void start(String processId) throws InterruptedException{
        final String lock="log"+processId;
        synchronized (lock) {
            if(concurrentMap.containsKey(processId)) {
                System.out.println("Process is already working and started at "+concurrentMap.get(processId));
                return;
            }       
            concurrentMap.put(processId,new Date());
        }
    }
    
    
    public static void main(String[] args) throws InterruptedException {
//      System.out.println(isPrime(10));
        final String pId="p1";
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    start(pId);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        });
        
        Thread t2=new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    start(pId);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        });
        
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        
        for (String s:concurrentMap.keySet()) {
            System.out.println(s+" - "+concurrentMap.get(s));
        }
    }
    
}

Если я заблокирую процесс ID, который передается как параметр метода, тогда все работает должным образом. В чем причина такого поведения Java. Я тестировал это на Java8 Eclipse IDE.

EDIT: Если концепция пула строк не имеет значения, то как работает следующий код?

import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class SynchronizationBug {
    private static Map<String,Date> concurrentMap=new ConcurrentHashMap();
    
    public static void start(String processId) throws InterruptedException{
        final String lock="log"+processId;
        synchronized (processId) {
            if(concurrentMap.containsKey(processId)) {
                System.out.println("Process is already working and started at "+concurrentMap.get(processId));
                return;
            }       
            concurrentMap.put(processId,new Date());
        }
    }
    
    
    public static void main(String[] args) throws InterruptedException {
//      System.out.println(isPrime(10));
        final String pId1="p1";
        final String pId2="p1";
        Thread t1=new Thread(new Runnable() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    start(pId1);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        });
        
        Thread t2=new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                try {
                    start(pId2);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        });
        
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        
        for (String s:concurrentMap.keySet()) {
            System.out.println(s+" - "+concurrentMap.get(s));
        }
    }
    
}

Здесь я Я передаю два разных строковых объекта с одинаковым значением. Из-за их одинаковой ссылки в пуле блокировка работает нормально. Поправьте меня, пожалуйста ....

1 Ответ

4 голосов
/ 11 июля 2020

Причина такого поведения в том, что конкатенация String, если оба операнда не являются выражениями констант времени компиляции, приводит к созданию нового экземпляра String. См. здесь на языке spe c:

Объект String создается заново (§12.5), если выражение не является константным выражением (§15.29).

Параметры метода не являются выражениями констант времени компиляции, так как метод может быть вызван с любым значением параметра.

И, поскольку текущий поток является единственным потоком, который имеет доступ к этому только что созданная строка, синхронизация с ней не имеет никакого эффекта.

Поскольку вы говорите, что не хотите знать альтернативы, я остановлюсь на этом.

Вы, кажется, не убеждены, несмотря на то, что я указал на соответствующую часть SPE c, что струны, на которых вы синхронизируете, разные. Попробуйте добавить эту строку прямо перед synchronized:

    System.out.println(System.identityHashCode(lock));  // Add this
    synchronized (lock) {
      // ...

. Это распечатает «идентификатор ha sh код» блокировки, который не совпадает с lock.hashCode(), который основывается на значении строки. Это покажет, что synchronized синхронизируется с разными значениями (если только вы исключительно повезло / не повезло, поскольку ha sh коллизии маловероятны, но возможны).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...