2016-10-30 7 views
5

그래서이 그들 중 하나가 사용자로부터 정보를 얻을하도록되어 다음과 같이 다른 스레드는 사용자가 제공 한 정보와 함께 작동하도록 생각되는 경우 실행되는 두 개의 스레드 :두 스레드간에 ArrayList를 공유하고 있습니까?

public class UserRequest implements Runnable { 

@Override 
public void run() { 
    // TODO Auto-generated method stub 
    String request; 
    Scanner input = new Scanner(System.in); 
    while(true) 
    { 
     System.out.println("Please enter request:"); 
     request = input.nextLine(); 
     try 
     { 
      //do something 
     } 
     catch(IOException e) 
     { 
      e.printStackTrace(); 
     } 
    } 

} 

그리고 두 번째 스레드 :

public class Poller implements Runnable { 

ArrayList<String> colors = new ArrayList<String>(); 

public void poll() 
{ 
    for(String color : colors) 
    { 
     if(color == "") 
     { 
      //do work 
     } 
     else 
     { 
      //do work 
     } 
    } 
} 

@Override 
public void run() { 

    colors.add("Violet"); 
    colors.add("Green"); 
    colors.add("Yellow"); 
    colors.add("Orange"); 

    while(true) 
     poll();  
} 
} 

사용자가 입력 한 내용을 UserRequest 개체에 입력하고 개체를 Poller 개체에 넣으면 새로운 값으로도 작업 할 수 있습니다. 내가 BlockingQueue 같은 몇 가지를 쳐다 보았다하지만 그들은 데이터의 공유 이외에 수행해야 할 다른 작업을 가지고 있기 때문에 스레드가 다른 기다리고 싶지 않아요. 어떻게이 일을 할 수 있습니까?

+0

ArrayList는 스레드로부터 안전하지 않습니다. – SLaks

+0

thread-safe 대안은 무엇일까? thread간에 그것을 공유하는 방법은 무엇입니까? –

+0

많은 사람들이 지적했듯이, 대기열은 아마도 당신이 찾고있는 것일 것입니다. ConcurrentLinkedQueue를 과도한 잠금없이 스레드로부터 안전한 것으로 사용할 수 있습니다. – KennethJ

답변

5

'푸시'및 '설문'동사를 사용 했으므로 Queue이 아닌 List을 찾고있는 것으로 보입니다.

그러므로 here이라고 기록 된 ConcurrentLinkedQueue을 찾고 있다고 생각합니다.

개체를 먹이면 UserRequest 개체를 먹이고 개체를 먹는 경우 Poller 개체를 사용할 수 있습니다.

은 당신의 Poller 객체가 있기 때문에 오픈 while의 꽤 높은 CPU 소비를해야합니다 보이지만 있지 않는 어떤 wait :

이 코드를 실행하는 데 약간의 변화를 필요로하지만 거의 모든을 포함
public class Poller implements Runnable { 
    Queue<String> colors = new ConcurrentLinkedQueue<String>(); 

    public void poll() { 
    while(this.colors.isEmpty()){ 
     Thread.currentThread().wait(); 
    } 

    String color = this.colors.poll(); 

    while(color != null) { 
     if(color == "") { 
     //do work 

     } else { 
     //do work 
     } 

     color = this.colors.poll(); 
    } 
    } 

    @Override 
    public void run() { 
    colors.offer("Violet"); 
    colors.offer("Green"); 
    colors.offer("Yellow"); 
    colors.offer("Orange"); 

    while(true) { 

     this.poll(); 
    } 
    } 
} 

필요한 것. 이것은 매우 간단합니다. 요소가 남아 있지 않을 때까지 계속 폴링을 유지합니다. 그런 일이 발생하면 Poller 개체는 Queue에 요소가 없으면 실행할 수 없기 때문에 현재 Thread의 현재 상태를 묻습니다. 당신이 발견하는 경우

public class UserRequest implements Runnable { 

    @Override 
    public void run() { 
    String request; 
    Scanner input = new Scanner(System.in); 

    while(true) { 
     System.out.println("Please enter request:"); 
     request = input.nextLine(); 

     try { 
     //do something 

     } catch(IOException e) { 
     e.printStackTrace(); 

     } finally { 
     this.notifyAll(); // Notifies all sleeping threads to wake up 
     } 
    } 
    } 

, 나는 만 UserRequest 클래스에 notifyAll 전화를 추가했습니다. 왜? 매우 간단합니다 : notifyAll 깨우기 모두 wait ing Thread 이것은 정확히 Poller의 요소가없는 것입니다.

Poller이 호출되면 깨우면 색상 Queue에 요소가 있는지 확인하고 문제가 없는지 확인합니다. Queue에 요소가 없으면 UserRequest이 다시 깨어날 때까지 다시 잠자기됩니다.

0

당신이 다음 폴러 개체에서 사용자가 입력 한 새 값에 액세스하려면 다음

객체 대신 폴러 클래스의 ArrayList의 새로운 인스턴스을 만드는, 힙에 저장되기 때문에
  • 그냥 보낼 수 있습니다 UserRequest.So에서 목록 객체의 참조를 변경하면 userRequest의 arrayList에 새 값을 추가하면 폴러에서 사용중인 arrayList에 반영됩니다.

    public class UserRequest implements Runnable { 
    
    private ArrayList<String> arrayList = new ArrayList<String>(); 
    
    @Override 
    public void run() { 
        // TODO Auto-generated method stub 
        String request; 
        Scanner input = new Scanner(System.in); 
        while(true) 
        { 
         System.out.println("Please enter request:"); 
         request = input.nextLine(); 
         try 
         { 
    
         Poller poller = new Poller(arrayList); 
         Thread t = new Thread(poller); 
         t.start(); 
    
         } 
         catch(IOException e) 
         { 
          e.printStackTrace(); 
         } 
        } 
    
    } 
    

    당신은이처럼 폴러 클래스를 변경할 수 있습니다 :

    public class Poller implements Runnable { 
        private ArrayList arrayList = null;  
    
        Poller(ArrayList<String> arrayList){ 
        this.arrayList = arrayList; 
        } 
    
    public void poll() 
    { 
        for(String color : arrayList) 
        { 
         if(color == "") 
         { 
          //do work 
         } 
         else 
         { 
          //do work 
         } 
        } 
    } 
    
    @Override 
    public void run() { 
    
         while(true){ 
         poll(); 
        }  
    } 
    

    하지만 그 대신 당신이해야 무한 루프에 풀을 호출

예를 들어, 당신은이 방법을 수행 할 수 있습니다 ArrayList에 리스너를 추가하여 List에 새 값이 추가 된 경우에만 poll()을 호출하십시오.

당신은 ArrayList에 리스너를 추가하는 방법에 대한 자세한 내용을 알고이 링크를 확인하실 수 있습니다 : https://stackoverflow.com/a/16529462/7083385

+0

멀티 스레드 환경에서'ArrayList'를 사용하면 쓰기 및 읽기 순서를 보장 할 수 없으므로'ConcurrentModificationException'이 던져 질 수 있습니다. – Zeh

+0

그렇지만 @Jenna는 ArrayList를 사용하여이를 수행하려고합니다 –

+0

OP는 병행 성을 수정하지만 사용자 입력을 차단하는 'Pollinger'가 소비 할 때까지 'BlockingQueue'옵션을 제공합니다. – Zeh

0

당신은 큐를 사용할 수 있습니다. 대기열에는 자체 폴링 메소드가 있습니다. 당신은 그것을 정적으로 만들 수 있지만 나는 그것이 최선의 접근 방법이라고 생각하지 않습니다. 일반적으로 나는 래퍼 클래스의 일종으로 큐를 인스턴스화하기 위해 봄을 사용하지만, 당신이 그 경로를 취하는 것처럼 보이지 않는다.

+0

'Queue' static을 만드는 것이 최선의 방법이 아니라는 것에 동의합니다. OP는 '봄 (spring)'에 대해서도 아무 말도하지 않았고, 그렇다고해도 주입하는 것이 문제를 해결하지 못합니다. 왜냐하면 당신이 주사하는 것을 아직도 알아야하기 때문입니다. – Zeh

1

"하지만이 스레드는 데이터 공유 외에 다른 작업을 수행해야하기 때문에 스레드가 다른 스레드를 기다리는 것을 원하지 않습니다."

이것을 수행 할 방법이 없습니다. 클래스의 적절한 스레딩은 항상은 다른 스레드가 수행하는 동안 한 스레드가 대기해야 할 필요가 있다는 문제점을 안고 있습니다. 요점은 그것을 최소화하려는 것입니다. 스레드가 매우 간단하게 멈추게하고 드물게 스레드를 중지시키지 않는 경우에만 스레드가 오류를 야기 할 수 있습니다. 동기화 된 데이터 구조 중 하나를 사용하거나 직접 약간의 동기화 코드를 작성할 수 있습니다.

문제의 유일한 객체는 arraylist이며 스레드의 절대 최소 실속을 원합니다. 따라서 arraylist 자체의 객체를 기반으로 동기화하려고합니다. 따라서 arraylist 객체에 액세스하는 지점 주위에 약간의 동기화 블록을 작성하십시오.

public class Poller implements Runnable { 

    ArrayList<String> colors; 

    public Poller(ArrayList<String> colors) { 
     this.colors = colors; 
     //pass in colors object, if modified from the scanner side it must synchronize the block around the colors object too. 
    } 

    public void doWork(String color) { 
     //do work 
    } 

    public void addColor(String color) { 
     synchronized (colors) { 
      colors.add(color); 
     } 
    } 

    @Override 
    public void run() { 
     while (!Thread.interrupted()) 
      if (!colors.isEmpty()) { 
       String color; 
       synchronized (colors) { 
        color = colors.remove(0); 
       } 
       doWork(color); //work done outside synch 
      } 
     try { 
      Thread.sleep(100); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

요점은 결코 목록에 물건을 동시에 제거하거나 추가하지 않는 것입니다. 루프 내에서 작업이 완료되면 문제가 발생하고 배열의 크기가 변경되어 비트 수가 얼마나되는지 모르기 때문에 목록 전체를 반복 할 수 없습니다. 그러나 ArrayList를 사용하여 데이터 구조를 변경하고 해당 동기화 된 블록에서 문자열을 가져온 다음 해당 블록에서 작업하는 코드 블록을 동기화하기 만하면됩니다. 이 방법은 만 빠져 나가는 간단한 인스턴트 한 스레드가 읽고 쓰고 다른 하나는해야합니다. 둘 다 매우 빠른 작업입니다.

3
이 문제를 해결하는 방법은 두 가지가 있습니다

:

1) List interface를 구현하는 클래스를 사용하려면 그것은 생산자 - 소비자, 작업이 소모 또는 등과 로직 ConccurentLinkedQueue처럼 thread safe collection을 사용하고 (그리고 같은 결과적으로 평범한 ArrayList과 같은 방법을 사용할 수 있습니다.) CopyOnWriteArrayList의 측면을 봐야하지만이 클래스는 블로킹 동기화를 사용합니다.

2) 또 다른 방법은 내장 사용 자바 동기화 도구, 예

자세한 내용은 사양을 읽어야합니다. 물론

private final Semaphore semaphore = new Semaphore(2, true); 

    public void appendToList() throws InterruptedException { 
    available.acquire(); 
    arrayList.add(.....); //put here what u need 
    } 

    public void putItem(Object x) { 
    if (someLogicHere(x)) //semaphore releases counter in this place 
     available.release(); 
    } 

, 당신은 결합 할 수 있습니다 사용 그들 모두, 예를 들어 :의가 사용하는 세마포어의 예를 살펴 보자 당신은 동시에 약간의 semaphores을 사용하거나 diff 도구를 사용할 수 있습니다.

관련 문제