2013-07-23 1 views
3

올바른 메서드를 사용하여 다른 스레드에서 뭔가를 호출하는지 궁금합니다. Android에서이 작업을 수행하고 있지만 일반적인 Java 질문이라고 생각합니다.익명의 runnable에 사용 된 final int 매개 변수

일부 매개 변수가있는 메소드가 있습니다. 그들이 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); 
     } 
    }); 
    } 

이제 내 질문은 어떤 클래스 멤버없이 실행 가능한 익명 작성하는 최종의 int를 사용하는 것이 안전합니다, 무엇입니까? x와 y 매개 변수에 대한 최종 키워드를 생략하면 이클립스는 불평 할 것이다. 그것은 저에게 것, 저것은 일정한 수만 그런 경우에 사용하기 위하여 예정된다이다. 상수가 아니라면 괜찮습니까? Java는이 함수에 전달하여 상수를 '만들'나요?

하지만 JNI를 사용하여 native에서 Test.callInMainThread를 호출하려고합니다. 자바가 그 숫자가 상수인지 아니면 내 의견으로는 알 수있는 방법이 없습니다. 자바가 어떤 마법을 걸 것을 믿을 수 있습니까? 항상이 방법으로 작동할까요? 사용하는 것이

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가 아니면 차이가 있습니까?

업데이트 : 나는 자바에서 어떤 의미인지를 이미 알고 있습니다. 그건 문제가되지 않습니다. 요점은 Runnable 생성시 변수의 범위가 어디에 있는지입니다. 그들은 지역 주민들입니다. 즉, 기능이 끝난 후에는 그 가치를 참조 할 수 없습니다. Runnable 객체가 다른 쓰레드에 전달되어 실행될 때까지 기다렸다가 그 값을 어디에 저장 하는가?

+0

(동일하지가 보유 케이스, 반전 대칭 이름. 다소 단순화 된). 이러한 매개 변수는 익명의 Runnable의 run() 메서드를 생성 할 때 사용되기 때문에 final이어야합니다. callInMainThread (x = 3, y = 5)를 실행하면 public void run()이 Main1.mainOnlyWork (3, 5)로 본문을 생성하고 로컬 또는 전역 변수를 참조하지 않습니다. 이것이 제가 찾던 것입니다. 그 시점에서 작성된 Runnable.run()의 코드에 저장됩니다.그리고 쓰레드간에 전달할 때 코드는 변경되지 않습니다. – Pihhan

답변

2

기본 사항부터 : 매개 변수를 전달하면 복사본이됩니다. 메서드가받는 x와 y는 전달 된 원래 변수가 아니며 원래 값의 복사본입니다. 당신은 당신이 상수가 될 필요가 없습니다에 합격이 값 같은 방법을 선언하지만, 방법은받는 복사 1 경우 :

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

둘째 : 아니, 당신은 필요가 없습니다에 래퍼 만들기. 외부 범위에 대해 로컬 인 변수에 액세스하면 Java 컴파일러는 수동으로 만든 래퍼와 마찬가지로 변수를 저장하는 필드를 자동으로 생성합니다. 이것은 당신에게 투명합니다.

final을 생략 할 수없는 한 가지 이유는 Java가 메소드의 로컬 변수와 익명 클래스의 생성 된 필드 사이의 변수 값 변경 사항을 전송하는 메커니즘을 구현하지 않기 때문입니다. 또한 익명 클래스는 메서드 호출보다 오래 살 수 있습니다. 익명 클래스가 메서드를 반환 한 후에 변수를 읽거나 쓰면 어떻게됩니까? 변수가 final이 아니면 더 이상의 값을 읽거나 쓸 수 없습니다.


1 사실 final 변수 상수 아니다. 그것들에 다른 값을 할당 할 수는 있지만 한 번만 할 수 있습니다. 메서드가 호출 될 때 최종 메서드 매개 변수의 값이 할당되므로 메서드의 지속 기간 동안 거의 일정합니다.

+0

아, 고마워! 나는 다른 단어로 나 자신을 알아낼 때까지 정확히 세 번째 단락의 의미를 이해하지 못했습니다. – Pihhan

1

프리미티브 유형은 항상 값으로 전달됩니다. 메서드 내에서 메서드를 수정하더라도 메서드 외부의 원래 값은 변경되지 않습니다. 그래서 그렇습니다.이 값은 메서드의 로컬이므로 (final) 안전합니다.

매개 변수의 약 final 키워드는 실제로 값을 다시 할당하는 것을 방해하며 다른 용도는 없습니다. 이것은 단순한 코드 안전을위한 것입니다. 매개 변수는 이고 항상 Java에서 값으로 전달되는입니다 (객체 참조도 값으로 전달됩니다). 따라서 어떤 재 할당이든 메소드에 로컬이됩니다. 메소드가 끝나면 메소드에서 수행 한 모든 재 할당이 사라집니다. 예를 들어, 제 2 경우에 에러를 발생 것이다 컴파일러 제외

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

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

사이에는 차이가 없다. 첫 번째 경우 test() 메서드가 완료되면 a은 원래 값으로 복원됩니다 ("Hello"이 아님). 따라서 매개 변수의 최종 유효성은 매개 변수를 "상수"로 만듭니다 (객체의 경우 : 객체 상태는 수정할 수 있지만 객체의 참조는 수정할 수 없음).당신이 (당신의 익명 Runnable 인스턴스가 Main1 인스턴스 변수를 참조하는 귀하의 경우) 다른 클래스 범위의 변수를 참조하고 있기 때문에


final 키워드는 익명 클래스가 필요합니다. 따라서 다른 스레드에서이 스레드를 실행하고 최종 스레드가 아닌 경우 메서드의 지속 기간 동안 원래 참조를 덮어 쓸 수 있습니다. 이렇게하면 각 스레드가 동일한 변수 이름을 가진 다른 개체를 참조하게되어 혼란스럽고 언어 디자인에서 차단되었습니다.


래퍼 또는 추가 참조를 만들 필요가 없습니다. 매개 변수는 메소드의 지속 기간 동안 이미 참조되었으며 가비지 수집되지 않습니다.

+0

그들은 기능에 국부적으로다는 것을 정확한 사실 저를 걱정한다. 다른 매개 변수로 세 번 호출하면 빠른 행이됩니다. 메인 스레드가 첫 번째 기능 실행을 시작하기 전에 메소드 매개 변수가 3 번 변경되었습니다. 해당 로컬 값은 어디에 저장됩니까? 로컬 스레드 스택에있는 경우이를 덮어 쓸 수 있어야합니다. 주 스레드는 작업자 스레드 스택에 액세스하면 안됩니다. 스레드간에 공유되는 힙 메모리의 int입니까? – Pihhan

+0

로컬 변수는 임시 저장소이므로 스택에 저장됩니다. 각 스레드는 자체 스택을 가지고 있으며 공유되지 않습니다. 이것은 thread의 로컬 문맥입니다. 현재 스레드 컨텍스트를 사용하는 경우를 제외하고는 로컬 스레드 스택에서 덮어 쓸 수 없습니다. 실제로 호출되는 위치와 관계없이 실제로 정확히 어떤 의미인지 이해할 수 없습니다. 또한이 값은 ** 복사 **되므로 로컬 범위에서 변경 되더라도 로컬 복사본 만 배타적으로 변경하고 다른 복사본은 변경하지 않고 원래 값을 변경합니다. – m0skit0

+0

그럼 내 질문에 어떻게 그들이 새로운 Runnable() {}에 복사됩니다. 그들은 그 익명의 대상으로 만들어 졌습니까? 그 변수들이 다른 스레드로 어떻게 보내지는지 알고 싶습니다. 지역 변수 란 무엇을 의미하는지 이해합니다. 그러나 이러한 변수는 해당 메서드가 호출 될 때 사용되지 않습니다. 그것들은 장래에 호출 될 메서드에 전달됩니다. 그들 사이의 시간은 어디에 있습니까? – Pihhan

3

내부적으로 메서드 호출 스택에 상주하는 매개 변수 및 로컬 변수의 복사본이 스레드에서 가져옵니다. 이것은 메서드 호출이 끝난 후에도 스레드가 여전히 남아 있기 때문입니다.

변수는 메서드에서 원래 변수를 덮어 쓰지 않아야 스레드의 버전이 달라질 수 있습니다. 이는 오도 된 것입니다. 따라서 같은 이름의 두 버전 모두 같은 것을 의미하게하는 문제입니다.

신중한 언어 디자인.

내가 대답을 자신을 발견 생각