멀티 레벨 잠금으로 인해 멀티 스레드 프로그램에서 교착 상태가 발생할 수 있습니까?

StackOverflow https://stackoverflow.com/questions/19849079

문제

작가와 독자가있는 프로그램이 있으며 액세스 권한은 모니터에 의해 제어됩니다.

그래서 이것은 굶어 죽어야했지만 교착 상태를 얻었습니다. 나는 왜 그런지 궁금해하고 다른 잠금 장치를 넣었다는 것을 기억했다. 나는 내 글로벌 변수를 불일치로부터 보호하기 위해 독자 내부의 읽기 방법 내에서 불필요하다고 생각했다. 교착 상태가 발생하지 않고 스레드 10000 시간을 실행할 수 있었기 때문에 교착 상태가 발생하지 않을 것이라고 생각했지만 실험실 데모를해야 할 때 10010 번째 스레드에서 교착 상태가되었습니다. 그래도 왜 그렇게할지 이해가 안 돼요. 또한, 나는 그것이 굶어 죽을 것이라고 기대하지 않았지만 분명히 그럴 것으로 예상되었습니다.

내 질문은 다음과 같습니다. 그 다단계 잠금 장치는 교착 상태에 책임이 있습니까? 그렇지 않다면 무엇을 원인합니까?!

    import java.io.*;
    import java.io.IOException;
    import java.util.*;

    public class Writer extends Thread{

    private int number;

    public Writer(int number)
    {
        this.number = number;
    }

    public int getNumber()
    {
        return number;
    }

        public static void Write(String filename){

        try {

            String content = RandomString();


            File f = new File(filename);

            if (!f.exists())
            {
                f.createNewFile();
            }


            PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("Task1out.txt", true)));
            out.println(content);
            out.close();


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

    public static String RandomString(){

        String chars = new String("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
        int n = chars.length();

        String randomString = new String();
        Random r = new Random();

            for (int i=0; i<100; i++)
            {
                randomString = randomString + chars.charAt(r.nextInt(n));
            }

        System.out.println("RandomString() generated: " + randomString);

        return randomString;

    }



    public void run(){

        try{

        //FileControl fc = new FileControl();

            int number = this.getNumber();


            for(int i = 0; i <1000; i++) //CHANGE IT TO 1000
            {
                main.fc.WriterEntry(number);

                //write file random characters (must append)

                Write("Task1out.txt");

                main.fc.WriterExit(number);

            }
        } catch(InterruptedException e)
        {
            System.out.println("Interrupted Exception caught");
        }

    }


}

이것은 작가 수업입니다.

    import java.io.BufferedWriter;
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileWriter;
    import java.io.FileReader;
    import java.io.IOException;
    import java.util.*;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;



public class Reader extends Thread{


    private int number;

    public Reader(int number)
    {
        this.number = number;
    }


    public int getNumber()
    {
        return number;
    }

        public static synchronized void Read(String filename)throws InterruptedException{

        BufferedReader br = null;





            main.lock.lock(); //lock
        try{




        try {


            String line;
            char[] chars = new char[100];
            int readIndex2 = 0;
            int addToIndex = 0;



            br = new BufferedReader(new FileReader(filename));


            int initialReadIndex = main.getIndex();




            System.out.println("initial read index: " + initialReadIndex);

            while ((line = br.readLine()) != null && readIndex2 < initialReadIndex+100 && addToIndex < 100) {

                for(int i = 0; i< 100; i++)
                {
                    if (initialReadIndex == readIndex2 || initialReadIndex < readIndex2)
                    {

                        if(line.length() > addToIndex)
                        {




                        chars[i] = line.charAt(i);
                        addToIndex++;
                        }


                    }
                    else
                    {
                        readIndex2++;
                    }
                }
                System.out.println(chars);
            }

            if(line == null)
            {
                System.out.println("nothing to read");
            }



            main.incrementIndex(addToIndex);


            System.out.println("current read index: " + (initialReadIndex + addToIndex));





        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("buffered reader exception");
        } finally {


            try {


                if (br != null)
                    {

                    br.close();
                    }
            } catch (IOException ex) {
                ex.printStackTrace();
                System.out.println("exception during closing");
            }
        }
        }finally{
            main.lock.unlock(); //lock

        }

        }


    public void run(){

        try{


        //FileControl fc = new FileControl();


        int number = this.getNumber();


            for(int i = 0; i <1000; i++) //CHANGE IT TO 1000
            {
                main.fc.ReaderEntry(number);

                //read file

                Read("Task1out.txt");

                main.fc.ReaderExit(number);
            }
        } catch(InterruptedException e)
        {
            System.out.println("Interrupted Exception caught");
        }

    }



        }

이것은 독자 수업입니다.

 import java.io.BufferedWriter;
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileWriter;
    import java.io.FileReader;
    import java.io.IOException;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;

    public class main{

    public static FileControl fc = new FileControl();

    final static Lock lock = new ReentrantLock();

    public static int readIndex;

    public static void incrementIndex(int increment) {


                readIndex = readIndex + increment;

    }

    public static int getIndex()
    {
        return readIndex;
    }



    public static void main(String[] args) throws InterruptedException {



            Writer [] writer = new Writer[10];
            Reader [] reader = new Reader[10];

            for(int i = 0; i < 10; i++)
            {
                reader[i] = new Reader(i);
                writer[i] = new Writer(i);
                //creating readers and writers

            }

            for(int i = 0; i < 10; i++)
            {
                //anonymous threads
                //(new Thread(new Writer())).start();
                //(new Thread(new Reader())).start();

                reader[i].start();
                writer[i].start();

            }




            for(int i = 0; i < 10; i++)
            {
                try{
                    reader[i].join();
                    writer[i].join();
                } catch(InterruptedException e){
                    e.printStackTrace();
                }


            }






        }

}

이것은 주요 클래스입니다.

    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;


    public class FileControl {
    final Lock lock = new ReentrantLock();
    final Condition writers = lock.newCondition();
    final Condition readers = lock.newCondition();
    int activereaders = 0;
    int waitingwriters = 0;
    boolean writing = false;

    public void WriterEntry(int number)throws InterruptedException{
        lock.lock();
        try{
                if(writing == true || activereaders > 0){
                    waitingwriters++;
                    System.out.println("Writer thread " + number + " : waiting to write");
                    writers.await();
                    waitingwriters--;
                }
                System.out.println("Writer thread " + number + " : ready to write");

                writing = true;
           }
        finally{
            lock.unlock();
        }


    }



    public void WriterExit(int number)throws InterruptedException{
        lock.lock();
        try{
            System.out.println("Writer thread " + number + " : finished to write");

            System.out.println("writers " + waitingwriters + "readers " + activereaders); //test

            if(waitingwriters > 0)
                writers.signal();
            else{
                writing = false;
                readers.signal();
            }
        }
        finally{
            lock.unlock();
        }

    }


    public void ReaderEntry(int number)throws InterruptedException{
        lock.lock();
        try{

            if(writing == true || waitingwriters > 0){ //remove activereaders > 0
                System.out.println("Reader thread " + number + " : waiting to read");
                readers.await();
                activereaders++;
            }


            System.out.println("Reader thread " + number + " : ready to read");
        }
        finally{
            lock.unlock();
        }

    }

    public void ReaderExit(int number)throws InterruptedException{
        lock.lock();
        try{



        activereaders--;



        System.out.println("Reader thread " + number + " : finished to read");

        System.out.println("writers " + waitingwriters + "readers " + activereaders); //test

            if(activereaders == 0)
            {
                if(waitingwriters > 0)
                {
                    writers.signal();
                }
                else
                {
                    readers.signal();
                }
            }
        }
        finally{
            lock.unlock();
        }

    }


}

이것은 모니터입니다.

모니터의 유사 코드

도움이 되었습니까?

해결책

여러 자물쇠 A, B 및 C가있을 때마다 코드가 동일한 순서로 해당 잠금을 획득하려는 시도를 보장하지 않으면 교착 상태를 가질 수 있습니다.

final Lock A = new ReentrantLock();
final Lock B = new ReentrantLock();
final Lock C = new ReentrantLock();

A, B, C 또는 C, B, A 또는 A, C, B- 순서가 일관된 한 중요하지 않습니다.

A, B, C에 대한 코드 경로가 하나 있고 C, B, a에 대한 시도가 하나있을 때 문제가 발생합니다.

A와 C가 모두 개최되기 때문에 아마도 추측 할 수 있듯이,이 둘 중 하나는 B를 얻고 두 가지 모두 교착 상태가 될 것입니다. (일명 리소스 잠금 그래프에주기가 있습니다)

공식적으로 말하는 교착 상태가 발생할 수 있습니다 경우에만 다음 모든 조건은 다음과 같습니다.

  1. 선점 없음 :이 시스템은 할당 후 리소스를 자유롭게하지 않습니다. 그들은 보유 과정에서만 해제 할 수 있습니다.
  2. 원형 대기 : 위에서 논의 됨.
  3. 상호 배제 : 하나의 프로세스 만 주어진 시간에 리소스를 사용할 수 있습니다.
  4. 자원 보유 : 프로세스는 현재 하나 이상의 리소스를 보유하고 있으며 다른 프로세스에서 보유한 추가 리소스를 요청/기다리고 있습니다.

최상의 솔루션은 주문이 일관되거나 더 높은 (단일) 레벨에서 잠그는 것입니다. 또 다른 옵션은 잠금을 시도하는 동안 타임 아웃을하는 잠금 라이브러리를 사용하는 것입니다 (또는 조건을 사용하고이를 수행하는 래퍼를 작성하십시오). 그러나 그 접근법은 희미한 마음을위한 것이 아닙니다. 이것의 일부 구현은 임의의 시간을 기다리고 다시 시도하지만 자물쇠 횟수가 증가함에 따라 비효율적 일 수 있습니다.

자원:

추신 : 나는 당신의 코드의 많은 부분을 제대로 형식화하지 않았으며 최소한의 예제가 아니기 때문에 실제로 당신의 코드를 많이 읽지 않았습니다 (즉, 우리의 목적에 맞는 장황). 그러나이 조언은 이론적 인 관점에서 질문에 답해야합니다.

다른 팁

확실히 가능합니다. 런타임을 확인할 수도 있습니다!

첫 번째 단계는 스레드 덤프를 얻는 것입니다. 다음은 세 가지 방법입니다.

  • VisualVM에서 프로세스를 열고 "스레드"탭으로 이동하면 이런 종류의 교착 상태를 감지하는지 알려줍니다. 그런 다음 스레드 덤프 (바로 여기에 버튼이 있음)를 수행 할 수 있습니다. 각 스레드가 수행하는 작업과 소유 한 자물쇠 및 획득하려는 잠금 장치 (있는 경우)를 알려줍니다.
  • Linux 또는 Mac에서는 발행하여 스택을 얻을 수 있습니다. kill -3 <pid>, 어디 <pid> Java 프로세스 'ID입니다. 동일한 스레드 덤프를 stderr에 덤프합니다. 해당 스레드 덤프의 바닥에는 또한 감지 된 교착 상태 요약도 포함됩니다. Windows 에서이 작업을 수행하는 방법을 모르겠습니다.
  • 당신은 또한 호출 할 수 있습니다 jstack <pid>, 스레드 덤프를 stdout에 인쇄합니다 ( jstack'원래 Java 프로세스가 아닌'stdout ').

나는 교착 상태에 대한 샘플 프로그램을 작성하고 그것을 실행했습니다. 내 요점). 스레드 덤프의 관련 섹션은 다음과 같습니다.

Found one Java-level deadlock:
=============================
"Thread-2":
  waiting for ownable synchronizer 7f42b0f38, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  which is held by "Thread-1"
"Thread-1":
  waiting for ownable synchronizer 7f42ba170, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  which is held by "Thread-2"

그리고 관련 스레드 상태는 다음과 같습니다.

"Thread-2" prio=5 tid=7fc01c911000 nid=0x113d18000 waiting on condition [113d17000]
   java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <7f30c3528> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:811)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:842)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1178)
    at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:186)
    at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:262)
    at Locky$Boomer.run(Locky.java:22)
    at java.lang.Thread.run(Thread.java:680)

   Locked ownable synchronizers:
    - <7f30c3558> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)

"Thread-1" prio=5 tid=7fc01d06c800 nid=0x113c15000 waiting on condition [113c14000]
   java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <7f30c3558> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:811)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:842)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1178)
    at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:186)
    at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:262)
    at Locky$Boomer.run(Locky.java:22)
    at java.lang.Thread.run(Thread.java:680)

   Locked ownable synchronizers:
    - <7f30c3528> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)

이것은 작동하지 않습니다 모두 교착 상태. 예를 들어, 외부 자원을 기다리는 교착 상태는 잡히지 않습니다. 그러나 그것은 잡을 것입니다 Lock-기반 교착 상태뿐만 아니라 synchronized-기반.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top