2013-07-23 2 views
3

Мне интересно, использую ли я правильный метод для вызова чего-то в другом потоке. Я делаю это на Android, но думаю, что это общий вопрос Java.Заключительный параметр int, используемый в анонимном Runnable

У меня есть метод с некоторыми параметрами. Допустим, что они int.

class Main1 { 
    public static void mainOnlyWork(int x, int y) { 
    // not important here. 
    } 
} 

class Test { 
    Handler mHandler; 

    // called from main thread, stored as reference. 
    public Test() { 
    mHandler = new Handler(); 
    } 

    public static void callInMainThread(final int x, final int y) { 
    mHandler.post(new Runnable() { 
     public void run() { 
     Main1.mainOnlyWork(x, y); 
     } 
    }); 
    } 

Теперь мой вопрос, является ли безопасно использовать конечный Интс для создания анонимного работоспособным без каких-либо членов класса? Если я опускаю конечные ключевые слова для параметров x и y, Eclipse будет жаловаться. Мне кажется, что в таких случаях предполагается использовать только постоянные числа. Если я пройду не постоянную, это нормально? «Является ли Java« постоянным », переходя к этой функции?

Но я хочу вызвать Test.callInMainThread из native, используя JNI. Java не может определить, являются ли эти числа константами или нет, на мой взгляд. Могу ли я доверять Java, чтобы сделать какую-то магию? Будет ли всегда так работать?

Я думаю, что, может быть, я должен создать прокси-класс, как:

private abstract RunnableXY implements Runnable { 
    public RunnableXY(int x, int y) { 
    this.x = x; 
    this.y = y; 
    } 

    public int x; 
    public int y; 

    public abstract void run(); 
} 

и способ вызова использовать:

public static void callInMainThread(final int x, final int y) { 
    mHandler.post(new RunnableXY(x,y) { 
     public void run() { 
     Main1.mainOnlyWork(this.x, this.y); 
     } 
    }); 
    } 

Таким образом, я защитить ценности от мусора сбора до работоспособной не используется и ушел. Должен ли я создавать обертки или отмечать окончательный x в параметрах метода безопасным? Когда я пытаюсь, последнее ключевое слово работает просто отлично. Однако в потоках я не думаю, что если он работает, это причина, по которой он будет работать всегда. Всегда ли это работает с финалом? Как это делается, если это так? Будет ли разница, если параметр не является примитивным типом, а Object?

Обновление: Я уже понимаю, какие конечные средства на Java. Это не вопрос. Вопрос заключается в том, где область переменных, используемых при создании Runnable. Они являются локальными, то есть после завершения функции их ценность не может быть указана. Где хранятся эти значения, когда объект Runnable передается в другой поток и ждет выполнения?

+0

Я думаю, что я нашел ответ сам. Эти параметры должны быть окончательными, поскольку они затем используются при генерации метода run() анонимного Runnable. Если я запустил callInMainThread (x = 3, y = 5), public void run() будет сгенерировать тело как Main1.mainOnlyWork (3, 5), не ссылаясь ни на какую переменную, локальную или глобальную. Это то, что я искал. Он хранится в коде Runnable.run(), который создается в этой точке.И код не изменится при передаче между потоками. – Pihhan

ответ

2

Начиная с основ: Java составляет копий при передаче параметров. Полученные вами x и y не являются исходными переменными, которые передаются, они являются копиями исходных значений. Когда вы объявляете метод как этого значения, которые вы проходите в не должны быть постоянными, но копии, получаемые методом являются :

callInMainThread(final int x, final int y) { ..... } 

Во-вторых: Нет, вы не должны сделайте обертки. Когда вы обращаетесь к переменным, которые являются локальными для внешней области, компилятор Java автоматически создает поля для их хранения, как и созданные вами обертки. Это прозрачно для вас.

Одна из причин, по которой вы не можете опустить final, состоит в том, что Java не реализует никакого механизма передачи изменений в значение переменной между локальной переменной метода и сгенерированным полем в анонимном классе. Кроме того, анонимный класс может жить дольше, чем вызов метода. Что произойдет, если анонимный класс читает или записывает переменную после возврата метода? Если переменная не является окончательной, вы больше не можете ее прочитать или записать в нее.


На самом деле final переменные не являются постоянными. Вы можете присвоить им разные значения, но только один раз. Значение метода конечного метода присваивается при вызове метода, поэтому они практически постоянны в течение всего метода.

+0

А, спасибо! Я не понимал, что именно подразумевается под третьим абзацем, пока я сам не понял это разными словами. – Pihhan

1

Типы примитивов всегда передаются по значению. Даже если вы изменяете их внутри метода, их исходные значения (вне метода) никогда не изменяются. Так что да, это безопасно, потому что эти значения являются локальными для метода (будь то final или нет).

О ключевом слове final в параметрах, это фактически только мешает вам переустанавливать значение, у него нет других целей. Это для простой безопасности кода. Параметры всегда, переданные значением в Java (ссылки на объекты также передаются по значению), поэтому независимо от того, какое-либо перераспределение будет локальным для метода. Как только метод завершится, все повторное присвоение, которое вы сделали в методе, исчезнет. Например, нет никакой разницы между

public void test(String a) { 
    a = "Hello"; 
} 

и

public void test(final String a) { 
    a = "Hello"; 
} 

за исключением того, что компилятор выдаст ошибку во втором случае. В первом случае, когда метод test() заканчивается a, будет восстановлено исходное значение (оно не будет «Привет»). Таким образом, окончательный результат в параметрах делает параметр «постоянным» (обратите внимание, что для объектов: вы все равно можете изменить состояние объекта, но не ссылку на объект).


final ключевое слово требуется в анонимных классах, потому что вы ссылки на переменную в другой области видимости класса (в вашем случае, ваш анонимный Runnable экземпляр ссылается Main1 переменные экземпляра). Поэтому, если это выполняется в другом потоке, и они не являются окончательными, вы можете перезаписать исходную ссылку для продолжительности метода. Это заставит каждую нить ссылаться на разные объекты с тем же именем переменной, что запутывает, и поэтому оно было заблокировано в дизайне языка.


Вам не нужно создавать какие-либо обертки или дополнительные ссылки. Параметры уже указаны в течение всего метода и не будут собираться с мусором.

+0

Точно факт, что они местные для работы, меня беспокоит. Если я назову его три раза с разными параметрами, быстро в строке. Перед тем как основной поток начнет выполнение первой функции, параметры метода были изменены 3 раза. Где хранятся эти локальные значения? Если в стеке локального потока должно быть возможно, оно будет перезаписано. Основной поток не должен иметь доступ к стеку рабочих потоков, правильно? Являются ли эти ints в памяти кучи, которые разделены между потоками? – Pihhan

+0

Локальные переменные хранятся в стеке, поскольку они являются временным хранилищем. Каждый поток имеет свой собственный стек, он не используется. Это локальный контекст потока. Они не могут быть перезаписаны в стекх локального потока, кроме текущего контекста потока, независимо от того, как быстро они вызываются или откуда - на самом деле я не могу понять, что именно вы подразумеваете под этим. Кроме того, опять же, эти значения ** копируются **, поэтому независимо от того, изменяются ли они в локальной области, вы меняете только эту локальную копию, а не другую, а не оригинальную. – m0skit0

+0

Тогда мой вопрос: как они копируются в новый Runnable() {}. Они сделаны как поля этого анонимного объекта? Я хочу знать, как эти переменные отправляются в другой поток. Я понимаю, что означает локальная переменная. Но эти переменные не используются при вызове этого метода. Они передаются методу, который будет называться в будущем. Где они находятся между собой? – Pihhan

3

Внутренне в потоке берется копия параметров и локальных переменных (находящихся в стеке вызовов метода). Это связано с тем, что после завершения вызова метода поток все еще сохраняется.

И переменные должны быть окончательными, чтобы запретить переписывание исходных переменных в методе, что приведет к тому, что в потоке будет иметь другую версию. Это просто вводит в заблуждение. Поэтому вопрос о том, чтобы обе версии с одним и тем же именем означали одно и то же.

Тщательный дизайн языка.

(Упрощенные несколько, не называя симметрична вспять случаи, когда же самые.)

Смежные вопросы