2013-01-16 3 views
3

내 응용 프로그램에서 상황은 다음과 같습니다. 병렬로 실행되는 2 개의 스레드가 있습니다. 스레드 중 하나의 목적은 스크린 샷을 캡처하는 것이고 두 번째 스레드의 목적은 특정 폴더에 저장된 스크린 샷의 이름을 바꾸는 것입니다. 첫 번째 스레드 - 다음과 같이 응용 프로그램의 코드는 - :java를 사용하여 폴더에 대한 액세스를 동기화하는 방법은 무엇입니까?

CapturingAndRenamingSimultaneously.java에게

/** 
* Created with IntelliJ IDEA. 
* User: AnkitSablok 
* Date: 15/1/13 
* Time: 1:03 PM 
* To change this template use File | Settings | File Templates. 
*/ 

package com.tian.screenshotcapture; 

import java.io.File; 
import java.io.IOException; 
import java.util.concurrent.BlockingQueue; 
import java.util.concurrent.LinkedBlockingQueue; 

public class CapturingAndRenamingSimultaneously { 
    public static void main(String[] args) { 

     // we use the linked blocking queue here to resolve the concurrency issues 
     final BlockingQueue<File> queue = new LinkedBlockingQueue<File>(1024); 

     new Thread(new Runnable() { 
      @Override 
      public synchronized void run() { 
       try { 
        System.out.println("In the capture thread now"); 
        CaptureScreenshots.captureScreenshots(queue); 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
      } 
     }).start(); 

     new Thread(new Runnable() { 
      @Override 
      public synchronized void run() { 
       try { 
        while (true) { 
         System.out.println("In the rename thread now"); 
         RenameScreenShots.renameScreenshots(queue); 
         Thread.sleep(5000); 
        } 

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

CaptureScreenshots.java

/** 
* Created with IntelliJ IDEA. 
* User: AnkitSablok 
* Date: 15/1/13 
* Time: 12:35 PM 
* To change this template use File | Settings | File Templates. 
*/ 

// this code is used to capture the screenshots 

package com.tian.screenshotcapture; 

import java.awt.Dimension; 
import java.awt.Rectangle; 
import java.awt.Robot; 
import java.awt.Toolkit; 
import java.awt.image.BufferedImage; 
import javax.imageio.ImageIO; 
import java.io.File; 
import java.util.concurrent.BlockingQueue; 
import java.util.concurrent.LinkedBlockingQueue; 

public class CaptureScreenshots { 

    // this code is used to capture the screen shots 
    public static void captureScreenshots(BlockingQueue<File> queue) throws Exception { 

     String fileName = "C:\\Users\\ankitsablok\\Desktop\\Screenshots"; 
     int index = 0; 

     for (; ;) { 
      ++index; 
      Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); 
      Rectangle screenRectangle = new Rectangle(screenSize); 
      Robot robot = new Robot(); 
      BufferedImage image = robot.createScreenCapture(screenRectangle); 
      ImageIO.write(image, "jpg", new File(fileName + "\\i" + index + ".jpg")); 
      queue.put(new File(fileName + "\\i" + index + ".jpg")); 

      Thread.sleep(1000); 
     } 
    } 

} 

RenameScreenShots.java

/** 
* Created with IntelliJ IDEA. 
* User: AnkitSablok 
* Date: 15/1/13 
* Time: 12:49 PM 
* To change this template use File | Settings | File Templates. 
*/ 

package com.tian.screenshotcapture; 

import java.io.*; 
import java.util.concurrent.BlockingQueue; 

public class RenameScreenShots { 
    public static void renameScreenshots(BlockingQueue<File> queue) throws IOException, InterruptedException { 

     for (int i = 0; i < queue.size(); ++i) { 

      File sourceFile = queue.take(); 
      System.out.println("The filename is : " + sourceFile.getName()); 

      if (sourceFile.getName().contains("sent")) { 
      } else { 
       System.out.println("The modified name of the source file is :" + sourceFile.getName().substring(0, 
         sourceFile.getName().indexOf('.')) 
         + "sent" + ".jpg"); 

       File newFile = new File(sourceFile.getParent() + "/" + sourceFile.getName().substring(0, 
         sourceFile.getName().indexOf('.')) 
         + "sent" + ".jpg"); 

       byte[] buffer = new byte[1024]; 

       FileInputStream fis = new FileInputStream(sourceFile); 
       FileOutputStream fos = new FileOutputStream(newFile); 

       int length; 

       while ((length = fis.read(buffer)) > 0) { 
        fos.write(buffer, 0, length); 
       } 

       System.out.println("The file was deleted successfully : " + sourceFile.delete()); 

       fis.close(); 
       fos.close(); 
      } 
     } 
    } 

} 

나는 폴더에 액세스하려는 그 하나 개의 프로세스가 다른 프로세스를해야한다고 한 후 폴더에 이미지를 기록 할 때입니다 동기화하기 폴더의 이미지 이름을 바꿀 수 있습니다. 그러나 스크린 샷을 쓰고 읽는 폴더에 대한 액세스를 동기화 할 수는 없습니다. 위의 코드를 사용하여 어떤 시점에서 파일을 사용중인 다른 프로세스에서 FileNotFoundException 오류가 발생합니다. 이 문제를 어떻게 해결할 수 있습니까?

미리 감사드립니다.

답변

2

두 스레드간에 공유 작성 LinkedBlockingQueue.

스레드에서 CaptureScreenshots이 큐에 넣어 새로 만든 File 개체입니다.

스레드에서 RenameScreenShots이 큐에서 순차적으로 읽으면 File 개체를 준비하고 처리합니다.

UPD :

  1. 이 들어있는 폴더의 하위 폴더를 생성

    : 당신은 이미지 파일의 수십억 그들의 File 기술자로 메모리를 많이 먹을 것을 두려워하는 경우
    , 당신은 향상 등의 알고리즘을 적용 할 수 있습니다 이미지 파일을 넣고 이미지 파일을이 하위 폴더에 넣습니다.

  2. 이 하위 폴더와 정수 이름의 이름을 1, 2, 3, ..., 89으로 지정합니다.

  3. 인위적으로 각 하위 폴더의 파일 수를 제한하십시오. 파일 수가 제한에 도달하면 하위 폴더의 이름 - 수를 늘리십시오. 새로 작성한 후 계속하십시오.

  4. LinkedBlockingQueueFile 설명자를 넣는 대신 Integer 개의 개체를 넣으십시오. 각 개체는 같은 이름으로 채워진 하위 폴더에 해당합니다.

  5. 인사이드 LinkedBlockingQueue에서 새 요소를 가져오고이 요소를 하위 폴더 이름으로 간주하고이 하위 폴더 내의 모든 파일을 조용히 처리합니다.

UPD-2 내 UPD-1에 도입 계획을보다 쉽게 ​​처리 하위 폴더의 마지막 번호에 해당하는 몇 가지 정수 값의 공유 synchornized 게터를 사용하여 구현 될 수.

+0

LinkedBlockingQueue가 메모리 공간을 차지하면 모든 파일이 주 메모리 공간을 차지하므로 좋은 해결책이되지 않을 것입니다. 수십억 개의 이미지가 있다면 확실하게 이해할 수 없습니다. – AnkitSablok

+0

'File' 개체가 있습니다. 간단히 말해서 일부 파일의 간단한 설명자 (간단히 말해서 디스크의 이름과 경로 만 포함하는 래퍼와 같은 것으로 간주합니다). 그것은 많은 기억을 먹지 않을 것입니다. – Andremoniy

+0

글쎄, 링크 된 블로킹 큐는 반드시 파일 디스크립터를 참조로 저장합니다. 10 억 개의 레퍼런스를 사용하고 있다면 무슨 일을했는지 ​​여전히 기억 문제가 될 것입니다. 또한 하위 폴더를 만드는 것에 대한 해결 방법은 내 일을 복잡하게 만들지 않을 것입니다. 아웃. – AnkitSablok

0

작업이 동기화되면 연속적으로 실행되므로 실제로 두 개의 별도 스레드가 없어야합니다.

동일한 스레드에서 순차적으로 쓰기 및 이름 바꾸기를 수행하고 동기화 할 필요가 없습니다.

+0

폴더에 개별적으로 액세스하려면 2 개의 스레드가 필요하므로 개별적으로 멀티 스레드가 필요합니다. – AnkitSablok

+0

이유는 무엇입니까? 당신에게는 두 가지 작업이 있습니다. 하나는 다른 작업 후에 발생해야합니다. 병렬로 수행 할 수있는 작업이 없기 때문에 별도의 스레드를 사용할 필요가 없습니다. 멀티 스레딩에 여전히 설정되어 있다면 (사용하지는 않지만) java.util.concurrent.BlockingQueue와 같은 것을 사용하십시오. 스레드 1은 수행해야 할 작업을 수행하고 파일 이름을 대기열로 푸시합니다. 스레드 2는 대기열에서 가져 와서 파일을 처리합니다. 수동 동기화는 항상 최후의 수단이어야합니다. – pap

0

당신은 왜 Mutex이 아니고 Mutex controll에 기반하여 폴더에 액세스하는 스레드의 동작을 기반으로합니까 ??

+0

예제를 가르키거나 자바에서 사용되는 방법과이 상황에서 어떻게 사용될 수 있는지에 대한 예제를 제공 할 수 있습니까? – AnkitSablok

+0

아이디어는 간단합니다. 모니터가 사용 가능한 경우 모니터 상태를 찾은 다음 스레드가 잠길 것입니다 모니터 및 폴더 (액세스 폴더)의 과정을 완료하고 작업이 완료되면 모니터를 놓습니다. 하지만, 이것은 또한 다른 스레드가 대기를 야기합니다. – TheWhiteRabbit

+0

정확히 당신이 언급 한 요점 – AnkitSablok

2

어쩌면 파일 잠금을 시도 할 수 있습니다. 각 파일은 파일 잠금이 될 수 있습니다. 당신은 그런 A.shot 같은 파일에 screanshot을 쓸 때

RandomAccessFile file = new RandomAccessFile(some_file, "rw"); 
FileChannel fc = file.getChannel(); 
FileLock lock = fc.tryLock(); 
.... 
lock.release() 

, 당신은 파일 A.shot을 만들고, A.shot의 파일 잠금, 다음에 데이터를 기록 보유. 파일이 끝나면 파일 잠금을 해제하십시오.

이름 바꾸기 프로세스가 파일 잠금을 먼저 가져와 성공한 경우 작업 이름을 바꾸어야합니다. 파일 잠금을 얻을 수없는 경우 (쓰기 스레드가 잠금을 해제하지 않았으므로) 대기하십시오.

희망은 유용합니다. :-)

관련 문제