Являются ли ссылки на методы потокобезопасными параметрами метода в Java

у меня следующий сценарий:

interface ValueBinding<T> {
    public void setValue(T input);
}

public enum FacesBinding {
    VALUE;
    public void bindString(ValueBinding<String> fcn, HttpServletRequest req, String param){
        try {
            String val = req.getParameter(param);
            if( val != null )
                fcn.setValue(val);
        } catch (Exception e) { }        
    }
    public void bindBoolean(ValueBinding<Boolean> fcn, HttpServletRequest req, String param){
        try {
            fcn.setValue(req.getParameter(param) != null);
        } catch (Exception e) { }        
    }
    public void bindInt(ValueBinding<Integer> fcn, HttpServletRequest req, String param){
        try {
            int val = Integer.parseInt(req.getParameter(param));
            fcn.setValue(val);
        } catch (Exception e) { }        
    }
    public void bindLong(ValueBinding<Long> fcn, HttpServletRequest req, String param){
        try {
            long val = Long.parseLong(req.getParameter(param));
            fcn.setValue(val);
        } catch (Exception e) { }        
    }
...
...
}

и я использую его в "многопоточной" среде следующим образом:

параллельные потоки вызывают этот метод

            @Override // concurrent Threads are calling this method
            public Category initData(FacesContext facesContext) throws Exception {
                Category entity = new Category();
                HttpServletRequest req = facesContext.getRequest();
                FacesBinding.VALUE.bindLong(entity::setId, req, Table.Category.Field.ID.name());
                FacesBinding.VALUE.bindString(entity::setName, req, Table.Category.Field.NAME.name());

                FacesBinding.VALUE.bindInt(entity::setPosition, req, Table.Category.Field.POSITION.name());
                FacesBinding.VALUE.bindBoolean(entity::setLocalized, req, Table.Category.Field.LOCALIZED.name());           
                return entity;
            }

is

FacesBinding.VALUE.bindLong(entity::setId, req, Table.Category.Field.ID.name());

100% потокобезопасность, когда я передаю ссылку на метод (интерфейс) entity::setId в качестве параметра метода в объекте enum (Singleton)?

ПРИМЕЧАНИЕ:

entity::setId

entity::setName

entity::setPosition

...так далее. ВСЕ эти методы являются стандартными методами установки Java.

public void setId(long id) {
        this.id = id;
    }
public void setName(String name) {
        this.name = name;
    }
....

ОБНОВЛЕНИЕ:

чтобы быть конкретным: ARE

Category entity = new Category();
entity.setId(5);//thread safe for sure

100% равно

FacesBinding.VALUE.bindLong(entity::setId, ...);

делает ли тот факт, что FacesBinding является синглтоном, а ссылка на метод в bindLong(entity::setId, ...) небезопасна для потоков??


person Rami.Q    schedule 27.01.2017    source источник
comment
Я не вижу, что может сделать этот поток безопасным. Но это заслуживает некоторого исследования   -  person AxelH    schedule 27.01.2017
comment
@AxelH, разве тот факт, что метод bindLong(entity::setId, ...) принимает метод нового экземпляра объекта (в моем случае new Category()), не делает его потокобезопасным?   -  person Rami.Q    schedule 27.01.2017


Ответы (1)


Ваш вызов ссылки на метод будет потокобезопасным, если ваш метод setId является потокобезопасным, ни больше, ни меньше.

Ссылка на метод — это причудливое сокращение для создания ваших ValueBinding объектов. Когда они скомпилированы, будет своего рода частный внутренний класс, который реализует ваш функциональный интерфейс и вызывает указанный вами метод. Поскольку вы указали метод, принадлежащий объекту, этот внутренний класс также будет эквивалентен конструктору, который принимает ваш объект Category, и частному полю для его хранения (я сказал, что это эквивалентно, потому что по умолчанию реализации не ограничиваются этим поведением, если они могут обосновать и выбрать любое другое).

person M. Prokhorov    schedule 27.01.2017
comment
Метод setId является обычным методом установки Java, поскольку он принадлежит конкретному экземпляру объекта категории и является потокобезопасным. означает ли это, что моя реализация выше 100% потокобезопасной, несмотря на то, что я использую ее как ссылку на метод в объекте Singleton (перечисление)? - person Rami.Q; 27.01.2017
comment
Я бы сказал, что использование его в качестве простого установщика скорее сделает его не потокобезопасным, потому что под безопасностью потоков вы обычно подразумеваете упорядоченную запись, и поскольку нет синхронизации ни в ValueBinding, ни в setId, и с дополнительным фактом, что любой из ваших объектов ValueBinding может совместно использоваться несколькими параллельными потоками по усмотрению его сайта использования. - person M. Prokhorov; 27.01.2017
comment
в моем случае параллельные потоки вызывают метод initData(..). как вы видите внутри этого метода, я создаю новый экземпляр моего объекта категории. Ни один поток не имеет прямого доступа к методам установки. каждый вызов initData(..) становится новым экземпляром. вот почему я сказал, что это потокобезопасно. Но я не уверен, что это так, используя ссылки на методы в объекте перечисления. вот мои заботы. - person Rami.Q; 27.01.2017
comment
В вашей конкретной ситуации вы действительно можете считать свой метод initData(FC) потокобезопасным, поскольку он (насколько я могу судить) не использует никаких значений, которые также может использовать другой поток. С точки зрения функций ваш initData можно считать чистым, поскольку он зависит только от его ввода, а не от какого-то внешнего изменяемого состояния. Тем не менее, я по-прежнему остаюсь при своем первоначальном суждении: entity::getId сам по себе не является потокобезопасным. Он просто используется потокобезопасным способом (т. е. нет случаев, когда несколько потоков могут получить доступ к одному и тому же экземпляру entity::getId). - person M. Prokhorov; 27.01.2017
comment
Спасибо большое Михаил, на самом деле я согласен со всем, что вы сказали. Но я подожду, чтобы увидеть, что другие говорят об этом. - person Rami.Q; 27.01.2017
comment
Просто примечание о ссылках на методы в Java 8, они используются как функциональный интерфейс, но они не компилируются в частный внутренний класс. Они компилируются в опкод, который был добавлен в Java 7 и называется invokedynamic, который первоначально загружается и создает динамическую связь между вызовом и кодом, который должен быть выполнен. После этого начального вызова код операции считывается как invokestatic, поэтому он чрезвычайно эффективен. - person CraigR8806; 27.01.2017
comment
@CraigR8806 CraigR8806, они действительно компилируются в invokestatic? Насколько я помню, ссылка на метод компилируется в эквивалент как частного статического метода, так и метода invokedynamic, который указывает на этот метод и имеет закодированную информацию об ожидаемом типе. Эти типы invokedynamic затем подхватываются преобразователем и превращаются в анонимный класс, который только после этого имеет код операции invokestatic, указывающий на закрытый метод. Я не знал, что этот метод можно напрямую связать, не могли бы вы указать, откуда вы его узнали? - person M. Prokhorov; 27.01.2017
comment
@CraigR8806 CraigR8806, если подумать, может быть, invokestatic связан для случая, когда ссылка на метод не захватывается (т.е. метод экземпляра без параметров). Это оно? - person M. Prokhorov; 27.01.2017
comment
@Михаил Прохоров: на самом деле все наоборот. Если ссылка на метод не захватывает (т. е. имеет вид TypeName::methodName), инструкция invokedynamic будет связана с псевдометодом, который всегда возвращает один и тот же экземпляр функции. При захвате (форма expression::methodName) он будет связан с методом фабрики static, который будет создавать экземпляр нового функционального объекта при каждом вызове. Это не целевой метод ссылки (methodName), который в любом случае обычно не является static. Вместо этого они могут ссылаться на конструктор; это незначительная деталь реализации. - person Holger; 30.01.2017
comment
Но, похоже, в этих методах есть небольшая путаница. Лямбда-выражения компилируются в private методы, всегда static, если они не захватывают this, но все же могут быть static, если они захватывают это (и получают this в качестве обычного параметра), это зависит от компилятора. Класс, синтезированный во время выполнения, будет вызывать этот метод, независимо от того, static или нет. Точно так же он может напрямую вызывать целевой метод ссылки на метод или даже непосредственно конструктор. Только для некоторых редких случаев, например. ArrayType::new или varargs, ссылка на метод компилируется как лямбда-выражение. - person Holger; 30.01.2017