ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 멀티쓰레드 프로그래밍
    자바 스터디 2021. 2. 2. 22:13

     

    백기선님의 Java 스터디를 진행하며 찾아본 내용입니다.

     

    목표

    자바의 멀티쓰레드 프로그래밍에 대해 학습하세요.

     

    학습할 것

     

    • Thread 클래스와 Runnable 인터페이스
    • 쓰레드의 상태
    • 쓰레드의 우선순위
    • Main 쓰레드
    • 동기화
    • 데드락

     


    Thread 클래스와 Runnable 인터페이스


    Thread는 두가지 방법을 통해 구현되어 질 수 있다.

     

    1. Thread class의 상속

    2. Runnable interface의 구현

     

     

    1. Thread class의 상속

    Thread class를 상속 받은 뒤에 run() 메소드에서 수행할 작업을 작성 할 수 있다.

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class MultithreadingDemo extends Thread{
        public void run() {
            try {
                // Displaying the thread that is running
                System.out.println("Thread " + Thread.currentThread().getId() + "is running");
            }
            catch (Exception e) {
                System.out.println("Exception is caught");
            }
        }
     
        public static void main(String[] args) {
            int n = 8;
            for (int i = 0; i < n; i++) {
                MultithreadingDemo object = new MultithreadingDemo();
                object.start();
            }
        }
    }
     
    cs

     

     

    Output:

     

     

     

     

    2. Runnable interface의 구현

    Runnable interface를 구현하고 run() 메소드를 override한다.

     

     

    Runnable Interface

    Runnable Interface

     

     

     

    Runnable Interface 구현

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    class MultithreadingDemo2 implements Runnable {
        @Override
        public void run() {
            try {
                System.out.println("Thread " + Thread.currentThread().getId() + " is running");
            }
            catch(Exception e) {
                System.out.println("Exception is caught");
            }
        }
    }
     
    public class Multithread {
        public static void main(String[] args) {
            int n = 8// Number of thread
            for(int i = 0; i < n; i++) {
                Thread object = new Thread(new MultithreadingDemo2());
                object.start();
            }
        }
    }
     
    cs

     

     

    Output:

     

     

     

     

    Thread Class vs Runnable Interface

     

    1. Thread 클래스를 상속하는 방법을 사용한다면, 자바에서는 다중 상속을 지원하지 않기 때문에 다른 클래스를 상속 받을 수 없다. 하지만 Runnable Interface를 구현한다면 다른 클래스를 상속 받을 수 있다.
    2. Thread Class를 상속하여 사용한다면 yield(), interrupt()와 같은 내장되어 있는 메소드를 상속 받아 사용 할 수 있지만 Runnable interface에서는 사용이 불가능하다.

     

     

     


    Java의 멀티스레딩에서 start() function을 사용하는 이유.

     

     

    스레드를 생성할 떄 멀티 스레딩에서 Run()함수를 사용하는 것이 아닌 start()를 사용하는데 이유는 다음과 같다.

     

    start() 함수를 실행 하면 새로운 call stack을 생성하고 run()함수를 실행한다

    하지만 단순히 run() 함수를 override 한 것을 실행하면 call stack은 생성되지 않고 run() 함수만이 실행된다.

     

     

    Example) start()대신 run()를 사용할 경우

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    class ThreadTest extends Thread {
        @Override
        public void run() {
            try {
                System.out.println("Thread " + Thread.currentThread().getId() + " is running");
            }
            catch(Exception e) {
                System.out.println("Exception is caught");
            }
        }
    }
     
    public class Main {
        public static void main(String args[]) {
            int n = 8;
            for (int i = 0; i < n; i++) {
                ThreadTest object = new ThreadTest();
                // start() 대신 run()을 사용
                object.run();
            }
        }
    }
     
    cs

     

    Output:

     

     

     

     

     


    스레드의 상태


     

    스레드의 생명주기

     

    1. New Thread

    • 스레드가 생성되어졌을 때의 상태. 아직 start()가 호출이 되어지지 않은 상태

     

    2. RUNNABLE State

    • 실행 가능한 상태, 이미 run중이거나 run할 준비가 되어있는 상태이다. 시분할 방식으로 스케쥴링되어진다.

     

    3. Blocked / Waiting

    •  스레드가 일시적으로 정지 되어 있을 때 스케쥴러에 따라 Blocked 혹은 Waiting과 같은 state를 따른다
    •  스레드는 I/O에 의한 인터럽트나, 다른 스레드에 의해 lock 되어진 임계영역에 접근 하려 시도 할 때 Block 되어 질 수 있다.
    •  임계 영역이 unlock 되거나 I/O 인터럽트가 끝나면 스케쥴러가 block된 스레드 중 하나를 runnable 스테이트로 옮   긴다.
    •  스레드가 어떤 조건을 충족하지 못할 때 Waiting state에 있게 되고 조건이 충족되면 스레드가 스케쥴러에 의해 runnable state로 이동되게 된다.

    4. Waiting, Timed Waiting

    • 스레드는 Thread 메소드가 timeout 파라미터와 함께 호출될 때 time waiting state에 놓이게 된다.
    • 스레드는 timeout이 완료되거나 notification을 받을 때 까지 이 상태에 있게 된다.
    • 예를들어 sleep이나 conditional wait을 호출하면 스레드는 timed waiting 상태로 이동되게 된다.

    5. Terminated State:

    • 다음과 같은 경우에 Terminated 된다.
    • thread의 코드가 전부 실행되었을 경우
    • segmentation fault나 unhandled exception등의 예측하지 못한 예외의 발생

     

     

     

     


    스레드 상태 구현

     스레드의 현재 state를 구하기 위해서 Thread.getState()라는 메소드를 사용한다. 

      java.lang.Thread.State 클래스에서 스레드의 상태에 대한 ENUM 상수를 정의 하고 있으며 목록은 다음과 같다.

    • public static final Thread.State NEW
    • public static final Thread.State RUNNABLE
    • public static final Thread.State BLOCKED
    • public static final Thread.State WAITING

     

    Example)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    class thread implements Runnable {
        @Override
        public void run() {
            try {
                Thread.sleep(1500);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
     
            System.out.println("State of thread1 while it called join() method on thread2 - " + Test.thread1.getState());
     
            try {
                Thread.sleep(200);
            }
            catch(InterruptedException e) {
                e.printStackTrace();
            }
     
        }
    }
    public class Test implements Runnable{
        public static Thread thread1;
        public static Test obj;
     
        public static void main(String[] args) {
            obj = new Test();
            thread1 = new Thread(obj);
     
            // thread1 created and is currently in the NEW state.
            System.out.println("State of thread1 after creating it - " + thread1.getState());
            thread1.start();
     
            // thread1 moved to Runnable state
            System.out.println("State of thread1 after calling .staer() method on it - " + thread1.getState());
        }
     
        @Override
        public void run() {
            thread myThread = new thread();
            Thread thread2 = new Thread(myThread);
     
            // thread1 created and is currnetly in the NEW state.
            System.out.println("State of thread2 after creating it - " + thread2.getState());
            thread2.start();
     
            System.out.println("State of thread2 after calling .start() method on it - " + thread2.getState());
     
            try {
                // moving thread1 to timed waiting state
                Thread.sleep(200);
            }
            catch(InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("State of thread2 after calling .sleep() method on it - " + thread2.getState());
     
            try {
                // waiting for thread2 to die
                thread2.join();
            }
            catch(InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("State of thread2 when it has finished it's execution: - " + thread2.getState());
        }
    }
     
    cs

     

    Output:

     

     

     

     


    쓰레드의 우선순위


    • 스레드가 생성될 때 마다 스레드는 우선순위가 부여된다.
    • JVM이나 프로그래머가 명시적으로 우선 순위를 부여 할 수 있다.
    • 스레드 스케쥴러는 우선순위에 따라서 스레드에 프로세서를 할당한다.
    • 1~10 범위의 스레드 우선순위가 있다.

     

     

    다음과 같은 우선 순위에 대한 3개의 static 변수가 Thread 클래스에 정의되어있다.

    • public static int MIN_PRIORITY: 가장 작은 우선순위를 가진다. '1'의 값을 가진다
    • public static int NORM_PRIORITY: 스레드가 생성될 때 기본적으로 가지는 값이다. '5'의 값을 가진다.
    • public static int MAX_PRIORITY: 가장 큰 우선순위를 가진다. '10'의 값을 가진다.

     

    Get, Set Method

    public final int getPriority():

    • java.lang.Thread내의 메소드로써 주어진 스레드의 우선순위를 반환한다.

     

    public final void setPriority(int newPriority):

    • java.lang.Thread내의 메소드로써 newPriority 값으로 우선순위를 변경한다.
    • 스레드 우선순위 값의 범위(1~10)을 벗어날 경우 IllegalArgumentException을 throw한다.

     

    Example)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    public class ThreadDemo3 extends Thread{
        public void run() {
            System.out.println("Inside run method");
        }
     
        public static void main(String[] args) {
            ThreadDemo3 t1 = new ThreadDemo3();
            ThreadDemo3 t2 = new ThreadDemo3();
            ThreadDemo3 t3 = new ThreadDemo3();
     
            // Default 5
            System.out.println("t1 thread priority : " + t1.getPriority());
     
            // Default 5
            System.out.println("t2 thread priority : " + t2.getPriority());
     
            // Default 5
            System.out.println("t3 thread priority : " + t3.getPriority());
     
            t1.setPriority(2);
            t2.setPriority(5);
            t3.setPriority(8);
     
            // t3.setPriority(21); will throw IllegalArgumentException
     
     
            // 2
            System.out.println("t1 thread priority : " + t1.getPriority());
     
            // 5
            System.out.println("t2 thread priority : " + t2.getPriority());
     
            // 8
            System.out.println("t3 thread priority : " + t3.getPriority());
     
            // Main Thread
     
            // Display the name of currently executing thread
            System.out.println("Currently Executing Thread : " + Thread.currentThread().getName());
     
            // Main thread priority is set to 10
            Thread.currentThread().setPriority(10);
            System.out.println("Main thread priority : " + Thread.currentThread().getPriority());
        }
    }
     
    cs

     

    Output

     

     

     

     

     

     


    Main Thread


    자바는 내장되어있는 멀티스레딩 지원 툴이있다. 멀티 스레드는 두개 혹은 더 많은 파트를 동시에 진행 시킬 수 있게 해준다.

    각각의 프로그램의 파트는 스레드라고 불리며 각각의 스레드는 별도의 실행경로를 정의한다.

     

    Main Thread

    자바 프로그램이 시작 될 때 시작되는 스레드.

     

    특징

    다른 "child" 스레드를 생성 할 수 있다.

    프로그램의 'shutdown'시 가장 마지막에 종료되는 스레드다.

     

    Thread Flow

    * 스레드의 참조를 얻기 위해서 currentThread()를 사용 할 수 있다.

     

    Example)

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    public class Test2 extends Thread{
        public static void main(String[] args) {
            // getting reference to Main thread
            Thread t = Thread.currentThread();
     
            // getting name of Main thread
            System.out.println("Current Thread: " + t.getName());
     
            // changing the name of Main thread
            t.setName("KJMain");
            System.out.println("After name change: " + t.getName());
     
            // getting priority of Main thread
            System.out.println("Main thread priority: " + t.getPriority());
     
            System.out.println("Main thread new priority: " + t.getPriority());
     
            for(int i = 0; i < 5; i++) {
                System.out.println("Main thread");
            }
     
            // Main thread creating a child thread
            ChildThread ct = new ChildThread();
     
            // getting priority of child class
            // which will be inherited from Main thread
            // as it is created by Main thread
            System.out.println("Child thread priority: " + ct.getPriority());
     
            ct.setPriority(MIN_PRIORITY);
            System.out.println("Child thread new priority: " + ct.getPriority());
     
            // starting child thread
            ct.start();
        }
    }
     
    class ChildThread extends Thread
    {
        public void run() {
            for(int i = 0; i < 5; i++) {
                System.out.println("Child thread");
            }
        }
    }
    cs

     

    Output:

     

     

     

     


    main() 메소드와 Main Thread 사이의 관계

     

    자바 프로그램은 실행되면 우선 JVM이 main thread를 만들게 된다. 그러고 나서 main thread가 main() 메소드를 찾아서 클래스로 초기화 시켜주는 역할을 맡는다.

     

     

     

     


    Daemon thread

    데몬스레드는 가비지 콜렉터와 같이 '백그라운드'에서 실행되는 낮은 우선순위를 가진 스레드를 말한다. 

     

    메소드

     

    void setDaemon(boolean status)

    • 현재 스레드를 데몬 메소드인지 유저 메소드인지 표시하는데 사용되어진다.
    • 어떠한 스레드 tU가 있다고 가정 할 때 tU.setDaemon(true) 과 같이 설정하면 tU스레드를 데몬 스레드로 만들수 있고, 반대로 인자값을 false로 주게 된다면 유저 스레드로 변경 할 수 있다.

    boolean isDaemon()

    • 현재 스레드가 데몬 스레드인지 체크해준다. 데몬 스레드라면 true를 리턴한다.

     

    Example)

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public class DaemonThread extends Thread{
        public DaemonThread(String name) {
            super(name);
        }
     
        @Override
        public void run() {
            // Checking whether the thread is Daemon or not
            if(Thread.currentThread().isDaemon()) {
                System.out.println(getName() + " is Daemon thread");
            }
            else {
                System.out.println(getName() + " is User thread");
            }
        }
     
        public static void main(String[] args) {
            DaemonThread t1 = new DaemonThread("t1");
            DaemonThread t2 = new DaemonThread("t2");
            DaemonThread t3 = new DaemonThread("t3");
     
            // Setting user thread t1 to Daemon
            t1.setDaemon(true);
     
            // starting first 2 threda
            t1.start();
            t2.start();
     
            // setting user thread t3 to Daemon
            t3.setDaemon(true);
            t3.start();
        }
    }
    cs

     

    Output:

    * 스레드가 start() 된 뒤에 setDaemon()을 사용하면 IllegalThreadStateException을 throw한다

    * 프로세스에 데몬 스레드만 남는다면 프로그램은 종료된다.

     

     

     

     

     

     

     


    동기화


    임계영역에 있는 공유자원에 접근하고자 할 때 Race condition등의 문제로 인해 에러나 예측하지 못한 문제가 생길 수 있기 때문에 동기화를 해야할 필요성이 생긴다.

     

    자바에서는 이를 Syncronized block을 통해 지원하고 있다.

    Syncronized block은 하나의 오브젝트에서 여러 스레드가 수행 되어야 할 때 한 순간에 한 스레드만 수행하도록 하여

    스레드간 충돌을 피할 수 있도록 해준다. 

     

    Syncronized block이 작업중인 코드에 다른 스레드가 접근하여 작업하려 할 때 다른 스레드는 block되어진다.

     

    '한 순간에 한 스레드만 수행 할 수 있다'는 컨셉은 자바에서 Monitor를 통해 구현되어진다.

    lock된 모니터에 들어가기를 원하는 스레드는 작업중인 스레드가 모니터에서 빠져나올때까지 block되어 중지된다.

     

    Example)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    class Sender {
        public void send(String msg) {
            System.out.println("Sending \t" + msg);
            try {
                Thread.sleep(1000);
            }
            catch (Exception e) {
                System.out.println("Thread interrupted.");
            }
            System.out.println("\n" + msg + "Sent");
        }
    }
     
    public class ThreadedSend extends Thread{
        private String msg;
        Sender sender;
     
        // Receive a message object and a string
        // message to be sent
        ThreadedSend(String m, Sender obj) {
            msg = m;
            sender = obj;
        }
     
        public void run() {
            // Only one thread can send a message at a time
            synchronized (sender) {
                // synchronizing the snd object
                sender.send(msg);
            }
        }
    }
     
    class SyncDemo {
        public static void main(String args[]) {
            Sender snd = new Sender();
            ThreadedSend S1 = new ThreadedSend(" Hi ", snd);
            ThreadedSend S2 = new ThreadedSend(" Bye ", snd);
     
            // start two thread of ThreadedSend type
            S1.start();
            S2.start();
     
            // wait for threads to end
            try {
                S1.join();
                S2.join();
            }
            catch(Exception e) {
                System.out.println("Interrupted");
            }
        }
    }
     
    cs

     

    Output:

     

     

     

     

     

     


    DeadLock


     Race condition등을 해결하기 위해 자바에서는 synchronized 키워드를 통한 동기화를 제공한다.

     

    synchronized 키워드는 한 자원에 하나의 스레드가 들어가 lock을 걸어 다른 스레드가 동시에 사용하지 못하게 하는 것인데, 이는 Deadlock을 유발 할 수 있다.

     

     Deadlock은 스레드가 발생 가능성이 없는 이벤트를 기다릴 경우 발생되는데 이는 다음과 같은 상황에서 발생 될 수 있다.

    Deadlock state - from; https://www.geeksforgeeks.org/deadlock-in-java-multithreading/

     

    위의 그림은 다음과 같은 과정을 거치는데

     

    1. Thread X가 자원 A를 점유하고 locking한다.

    2. Thread Y가 자원 B를 점유하고 locking한다.

    3. Thread X가 자원 A를 점유한 상태에서 자원 B를 요청한다.

    4. Thread Y가 자원 B를 점유한 상태에서 자원 A를 요청한다.

     

    이때 스레드는 하고 있는 작업이 끝날때까지 자원을 반환하지 않을 것임으로 Thread X가 자원 B를 받을 확률도 없고

    Thread Y가 자원 A를 받을 확률도 없다. 이때 이런 상태를 Deadlock(교착상태)라고 한다.

     

    Example)

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    // Java Program to illustrate Deadlock in multithreading
    class Util {
        // Util  class to sleep a thread
        static void sleep(long millis) {
            try {
                Thread.sleep(millis);
            }
            catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
     
    // This class is shared by both threads
    class Shared {
        // first synchronized method
        synchronized void test1(Shared s2) {
            System.out.println("test1-begin");
            Util.sleep(1000);
     
            // taking object lock of s2 enters
            // into test2 method
            s2.test2(this);
            System.out.println("test1-end");
        }
     
     
        // second synchronized method
        synchronized void test2(Shared s1) {
            System.out.println("test2-begin");
            Util.sleep(1000);
     
            s1.test1(this);
            // taking object lock of s1 enters
        }
    }
     
    class Thread1 extends Thread {
        private Shared s1;
        private Shared s2;
     
        // constructor to initialize fields
        public Thread1(Shared s1,Shared s2) {
            this.s1 = s1;
            this.s2 = s2;
        }
     
        // run method to start a thread
     
        @Override
        public void run() {
            // taking object lock of s1 enters
            // into test1 method
            s1.test1(s2);
        }
    }
     
    class Thread2 extends Thread {
        private Shared s1;
        private Shared s2;
     
        // constructor to initialize fields
        public Thread2(Shared s1, Shared s2) {
            this.s1 = s1;
            this.s2 = s2;
        }
     
        // run method to start a thread
     
        @Override
        public void run() {
            // taking object lock of s2
            // enter into test2 method
            s2.test2(s1);
        }
    }
     
    public class Deadlock {
        public static void main(String[] args) {
            // creating one object
            Shared s1 = new Shared();
     
            // creating second object
            Shared s2 = new Shared();
     
            // creating first thread and starting it
            Thread1 t1 = new Thread1(s1, s2);
            t1.start();
     
            // creating second thread and starting it
            Thread2 t2 = new Thread2(s1, s2);
            t2.start();
     
            // Sleeping main thread
            Util.sleep(2000);
        }
    }
     
    cs

     

    Output:

    Deadlock으로 인해 두 스레드가 끝나지 않는 것을 볼 수 있다.

     

     

    Deadlock을 피하는 방법

    • 하나의 스레드에 lock을 제공했으면 다른 스레드에 lock을 제공하지 않도록 하기
    • 필요하지 않은 Lock 사용하지 않기
    • Max-time을 두고 Thread.join()을 활용해 스레드를 종료시키기

     

     


    Reference

     

     

    www.geeksforgeeks.org/multithreading-in-java/

     

    Multithreading in Java - GeeksforGeeks

    A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

    www.geeksforgeeks.org

    www.geeksforgeeks.org/start-function-multithreading-java/?ref=rp

     

    What does start() function do in multithreading in Java? - GeeksforGeeks

    A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

    www.geeksforgeeks.org

    www.geeksforgeeks.org/difference-between-thread-start-and-thread-run-in-java/

     

    Difference between Thread.start() and Thread.run() in Java - GeeksforGeeks

    A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

    www.geeksforgeeks.org

    www.geeksforgeeks.org/lifecycle-and-states-of-a-thread-in-java/

     

    Lifecycle and States of a Thread in Java - GeeksforGeeks

    A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

    www.geeksforgeeks.org

    www.geeksforgeeks.org/java-thread-priority-multithreading/

     

    Java Thread Priority in Multithreading - GeeksforGeeks

    A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

    www.geeksforgeeks.org

    www.geeksforgeeks.org/main-thread-java/

     

    Main thread in Java - GeeksforGeeks

    A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

    www.geeksforgeeks.org

    www.geeksforgeeks.org/daemon-thread-java/

     

    Daemon thread in Java - GeeksforGeeks

    A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

    www.geeksforgeeks.org

    www.geeksforgeeks.org/synchronized-in-java/

     

    Synchronized in Java - GeeksforGeeks

    A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

    www.geeksforgeeks.org

    www.geeksforgeeks.org/deadlock-in-java-multithreading/

     

    Deadlock in Java Multithreading - GeeksforGeeks

    A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

    www.geeksforgeeks.org

     

    '자바 스터디' 카테고리의 다른 글

    Annotation  (0) 2021.03.01
    Enum  (0) 2021.02.04
    예외 처리  (0) 2021.01.30
    인터페이스  (0) 2021.01.28
    패키지  (0) 2021.01.22

    댓글

Designed by Tistory.