2010-12-06 4 views
1

자바에서 서로 이야기하는 노드를 구현하려고합니다. 나는 서버와 통신하기를 원하는 모든 노드에 대해 새로운 쓰레드를 생성함으로써이를 수행한다.자바 : 쓰레드, 어떻게해야 할 것인가?

주어진 수의 노드, 즉 많은 스레드가 생성되었을 때 서버에 연결되면 각 스레드가 "sharedCounter"에 추가 한 후 다음 비트의 코드를 실행하기를 원합니다.

공유 변수에 'locks'를 사용하고 signalAll() 또는 notifyAll()과 같은 함수를 사용해야한다고 생각합니다.하지만 모든 스레드가 제대로 작동하는지는 명확하게 알 수 없습니다. 또는 구현할 수 있습니다.


이 자바 개념을 설명하는 어떤 도움을 크게 감상 할 수있다 : D 아래


대략 내 코드의 구조 :

import java.net.*; 
import java.io.*; 

public class Node { 

    public static void main(String[] args) { 
    ... 
    // Chooses server or client launchers depend on parameters. 
    ... 
    } 
} 

class sharedResource { 
    private int sharedCounter; 

    public sharedResource(int i) { 
     sharedCounter = i; 
    } 

    public synchronized void incSharedCounter() { 
     sharedCounter--; 
     if (sharedCounter == 0) 
      // Get all threads to do something 
    } 
} 

class Server { 
    ... 
     for (int i = 0; i < numberOfThreads; i++) { 
      new serverThread(serverSocket.accept()).start(); 
     } 
    ... 
     sharedResource threadCount = new sharedResource(numberOfThreads); 
    ... 
} 

class serverThread extends Thread { 
... 
//some code 
Server.threadCount.incSharedCounter(); 
// Some more code to run when sharedCounte == 0 
... 
} 

class Client { 
... 
} 
+0

멀티 스레딩을, 그것을 많은 프로그래밍 언어에 존재합니다. Java는 내 의견으로는 아마도 가장 쉬운 방법 중 하나 일 것입니다. (스레드) –

답변

3

// 모든 스레드 뭔가를 할 얻기

스레드 (또는 확장 스레드가 아닌 구현해야 오히려보다 Runnable는) 그들이 실행할 것으로 예상되는 코드를 포함하는 실행 방법을

.

일단 Thread # start (Runnable # run을 호출 함)를 호출하면 스레드는 정확하게 그 작업을 시작합니다.

Java에서 멀티 스레딩에 익숙하지 않은 것 같기 때문에 동시 작업을보다 쉽게 ​​구현할 수 있도록 Java5에서 소개 된 동시성 유틸리티 패키지에 대한 소개를 읽는 것이 좋습니다.

특히 당신이 찾고있는 것처럼 보이는 것은 조건이 충족 될 때까지 작업을 "일시 중지"하는 것입니다 (귀하의 경우 카운터가 0에 도달 했음). 이를 위해서는 CountDownLatch을 봐야합니다.

+0

좋은 생각이지만 [[ExecutorService # invokeAll()'] (http://download.oracle.com/javase/6/docs/api/java/util/concurrent/ExecutorService.html#invokeAll%28java.util .Collection, % 20long, % 20java.util.concurrent.TimeUnit % 29)이 필요한 것보다 많을 수 있습니다. Btw : 동시성 튜토리얼은 [here] (http://download.oracle.com/javase/tutorial/essential/concurrency/index.html)에서 볼 수 있습니다. – BalusC

+0

'Thread'를 확장하는 대신'Runnable'을 구현하면 어떤 이점이 있습니까? – Serplat

+0

@Serplat : Runnable과 Runner를 분리하여 유지. 또한 : http://stackoverflow.com/questions/541487/java-implements-runnable-vs-extends-thread – Thilo

0

이 광범위한 주제입니다. 자바에서 동시성 (threading, more or less)에 대해서는 official guides을 통해 읽을 수 있습니다. 이것은 잘라 버리고 말린 해결책이있는 것이 아닙니다. 당신은 무언가를 디자인해야합니다.

+3

java.util.concurrent 패키지는 몇 가지 잘 정리 된 솔루션을 제공합니다. 그 중 하나를 사용할 수 있다면, 당신은 쓰레드로 작업 할 필요가없고 대기/통보하고 직접 동기화 할 필요가 없습니다. – Thilo

+0

특정 문제에 대해서는 예. 디자인의 일부는 문제를 식별하고 이미 알려진 솔루션의 문제로 표현하려고합니다. :) 절대 바퀴를 재발 명하지 마십시오. –

1

실제로 주제는 광범위하지만 기본 사항을 설명하려고합니다. 자세한 내용은 다양한 blogs 및 기사에서 읽을 수 있습니다. 그 중 하나는 Java trail입니다.

경주에서 서로 나란히 실행되는 주자 (실제 인물) 인 것으로 각 스레드를 보는 것이 가장 좋습니다. 각 러너는 실행 중에 모든 작업을 수행 할 수 있습니다. 예를 들어 경주에서 주어진 순간에 테이블에서 물 한 컵을 가져옵니다. 육체적으로, 그들은 한꺼번에 같은 컵에서 마실 수는 없지만 가상 세계에서는 가능합니다 (라인이 그려지는 곳입니다).

예를 들어 두 명의 주자를 다시 가져옵니다. 그것들 각각은 트랙을 앞뒤로 움직여야하고, 각 끝에서 1'000'000 번 동안 버튼을 누르십시오 (주자가 공유 함). 버튼은 매번 카운터를 하나씩 증가시킵니다. 그들이 뛰기를 마쳤을 때 카운터의 가치는 무엇입니까? 육체적 인 세계에서 주자가 동시에 버튼을 밀 수 없기 때문에 2 천 파운드가 될 것입니다. 첫번째 선수가 떠나기를 기다릴 것입니다. 이것은 정확히 두 스레드가 할 것입니다.이 코드를 고려 :

public class ThreadTest extends Thread { 
    static public final int TOTAL_INC = 1000000; 

    static public int counter = 0; 

    @Override 
    public void run() { 
     for (int i=0; i<TOTAL_INC; i++) { 
      counter++; 
     } 
     System.out.println("Thread stopped incrementing counter " + TOTAL_INC + " times"); 
    } 

    public static void main(String[] args) throws InterruptedException { 
     Thread t1 = new ThreadTest(); 
     Thread t2 = new ThreadTest(); 

     t1.start(); 
     t2.start(); 

     t1.join(); // wait for each thread to stop on their own... 
     t2.join(); // 

     System.out.println("Final counter is : " + counter + " which should be equal to " + TOTAL_INC * 2); 
    } 
} 

출력이 할 수있는 일 일단

Thread stopped incrementing counter 1000000 times 
Thread stopped incrementing counter 1000000 times 
Final counter is : 1143470 which should be equal to 2000000 

동안의

등을 단지 두 번 같은 값을 증가 할 두 개의 스레드; 이것을 경쟁 조건이라고합니다.

동기화는 run 메서드가 작동하지 않으므로 잠금 메커니즘을 사용하여 이러한 일이 발생하지 않도록해야합니다. run 방법을 다음과 같이 변경을 고려

static private Object lock = new Object(); 
@Override 
public void run() { 
    for (int i=0; i<TOTAL_INC; i++) { 
     synchronized(lock) { 
      counter++; 
     } 
    } 
    System.out.println("Thread stopped incrementing counter " + TOTAL_INC + " times"); 
} 

지금 예상 출력이 우리가 공유 객체와 카운터를 동기화 한

... 
Final counter is : 2000000 which should be equal to 2000000 

입니다. 이는 단 한 명의 주자가 한 번에 버튼에 액세스 할 수 있기 전에 대기 행렬을 배치하는 것과 같습니다.

참고 :이 잠금 메커니즘은 mutex이라고합니다. 한 번에 n 스레드로 리소스에 액세스 할 수있는 경우 semaphore을 사용하는 것이 좋습니다.

멀티 스레딩은 교착 상태와 관련이 있습니다. A deadlock은 두 스레드가 동기화 된 자원을 계속 사용 가능하게하기 위해 다른 스레드가 상호 대기 할 때입니다. 예를 들어

  • 스레드 1 시작
  • 스레드 2 시작
  • 스레드 1 개 취득 동기화 오브젝트 1
  • 스레드 2 취득 동기화 object2
  • 스레드 나사 (1)에 의해 로크 (계속하는 object2 획득 2 개 필요)
  • 스레드 1이 스레드 2에 의해 잠기기 위해 계속하려면 object1을 획득해야합니다 (스레드 2에 의해 잠김)
  • 프로그램이 교착 상태가됩니다.

이 문제가 발생하지 않도록하는 방법은 다양합니다 (스레드가 수행하는 작업과 구현 방법에 따라 달라질 수 있습니다 ...) 특히 그것에 대해 읽어야합니다.

참고 : 객체가 동기화 될 때 wait, notifynotifyAll 만 호출 할 수있는 방법. 두 스레드가 동기화 블록 내에서 for...loop 블록을 실행하는

static public final int TOTAL_INC = 10; 
static private int counter = 0; 
static private Object lock = new Object(); 

static class Thread1 extends Thread { 
    @Override 
    public void run() { 
     synchronized (lock) { 
      for (int i=0; i<TOTAL_INC; i++) { 
       try { 
        lock.wait(); 
        counter++; 
        lock.notify(); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 
} 

static class Thread2 extends Thread { 
    @Override 
    public void run() { 
     synchronized (lock) { 
      for (int i=0; i<TOTAL_INC; i++) { 
       try { 
        lock.notify(); 
        counter--; 
        lock.wait(); 
       } catch (InterruptedException e) { 
        /* ignored */ 
       } 
      } 
     } 
    } 
} 

공지 예를 ​​들면 다음과 같습니다. 두 스레드가 모두 끝나면 counter == 0의 결과입니다.이 작업은 리소스의 waitnotify 메서드를 통해 동기화 된 리소스에 "서로 허용"하기 때문에 수행 할 수 있습니다. 이 두 가지 방법을 사용하지 않으면 두 스레드가 순차적으로 동시에 실행되지 않고 (더 정확하게는 교대로) 실행됩니다.

이 내용이 스레드에 대해 (Java에서) 밝혀지기를 바랍니다.여기 **

** UPDATEThilo 이전에 의해 제안 된 CountDownLatch 클래스 사용하여 위에서 설명한 모든 것의 개념의 작은 증거입니다 : 자바 개념이 아니다

static class Server { 
    static public final int NODE_COUNT = 5; 

    private List<RunnableNode> nodes; 
    private CountDownLatch startSignal; 
    private Object lock = new Object(); 


    public Server() { 
     nodes = Collections.synchronizedList(new ArrayList<RunnableNode>()); 
     startSignal = new CountDownLatch(Server.NODE_COUNT); 
    } 

    public Object getLock() { 
     return lock; 
    } 

    public synchronized void connect(RunnableNode node) { 
     if (startSignal.getCount() > 0) { 
      startSignal.countDown(); 
      nodes.add(node); 
      System.out.println("Received connection from node " + node.getId() + " (" + startSignal.getCount() + " remaining...)"); 
     } else { 
      System.out.println("Client overflow! Refusing connection from node " + node.getId()); 
      throw new IllegalStateException("Too many nodes connected"); 
     } 
    } 

    public void shutdown() { 
     for (RunnableNode node : nodes) { 
      node.shutdown(); 
     } 
    } 

    public void awaitAllConnections() { 
     try { 
      startSignal.await(); 
      synchronized (lock) { 
       lock.notifyAll(); // awake all nodes 
      } 
     } catch (InterruptedException e) { 
      /* ignore */ 
      shutdown(); // properly close any connected node now 
     } 
    } 
} 


static class RunnableNode implements Runnable { 
    private Server server; 
    private int id; 
    private boolean working; 

    public RunnableNode(int id, Server server) { 
     this.id = id; 
     this.server = server; 
     this.working = true; 
    } 
    public int getId() { 
     return id; 
    } 
    public void run() { 
     try { 
      Thread.sleep((long) (Math.random() * 5) * 1000); // just wait randomly from 0 to 5 seconds.... 

      synchronized (server.getLock()) { 
       server.connect(this); 
       server.getLock().wait(); 
      } 

      if (!Thread.currentThread().isAlive()) { 
       throw new InterruptedException(); 
      } else { 
       System.out.println("Node " + id + " started successfully!"); 

       while (working) { 
        Thread.yield(); 
       } 

      } 
     } catch (InterruptedException e1) { 
      System.out.print("Ooop! ..."); 
     } catch (IllegalStateException e2) { 
      System.out.print("Awwww! Too late! ..."); 
     } 

     System.out.println("Node " + id + " is shutting down"); 
    } 
    public void shutdown() { 
     working = false; // shutdown node here... 
    } 
} 


static public void main(String...args) throws InterruptedException { 
    Server server = new Server(); 

    for (int i=0; i<Server.NODE_COUNT + 4; i++) { // create 4 more nodes than needed... 
     new Thread(new RunnableNode(i, server)).start(); 
    } 
    server.awaitAllConnections(); 

    System.out.println("All connection received! Server started!"); 

    Thread.sleep(6000); 
    server.shutdown(); 
} 
+0

답변 해 주셔서 대단히 감사합니다. 스레드가 어떻게 작동하는지 명확하게 설명하면 이해와 사용에 많은 도움이됩니다. (내가 가진 특정 문제를 해결했기 때문에 다른 답을 옳게 선택했지만, 답은 쓰레드에 대한 내 작업에 매우 잘 맞습니다.) –

+0

나는 당신이 단지 일반적인 도움을 원한다고 생각했다 : 여기에 나는 당신의 필요를 위해'CountDownLatch' 클래스를 가진 가능한 예제를 추가했다. –

관련 문제