>  기사  >  Java  >  Java 리뷰를 바탕으로 멀티스레딩에 대한 자세한 설명

Java 리뷰를 바탕으로 멀티스레딩에 대한 자세한 설명

黄舟
黄舟원래의
2016-12-19 14:50:431197검색

스레드는 운영 체제 작업의 기본 단위입니다. 프로세스는 여러 스레드를 포함할 수 있습니다. 스레드를 수동으로 생성하지 않더라도 프로세스에는 기본 스레드가 실행됩니다.

JVM의 경우 실행할 단일 스레드 프로그램을 작성할 때 JVM에는 최소한 두 개의 스레드가 실행되고 있는데, 하나는 우리가 만든 프로그램이고 다른 하나는 가비지 수집입니다.

스레드 기본 정보

Thread.currentThread() 메소드를 통해 현재 스레드에 대한 정보를 얻어서 수정할 수 있습니다.

다음 코드를 살펴보겠습니다.

查看并修改当前线程的属性
 String name = Thread.currentThread().getName();
         int priority = Thread.currentThread().getPriority();
         String groupName = Thread.currentThread().getThreadGroup().getName();
         boolean isDaemon = Thread.currentThread().isDaemon();
         System.out.println("Thread Name:" + name);
         System.out.println("Priority:" + priority);
         System.out.println("Group Name:" + groupName);
         System.out.println("IsDaemon:" + isDaemon);

         Thread.currentThread().setName("Test");
         Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
         name = Thread.currentThread().getName();
         priority = Thread.currentThread().getPriority();
         groupName = Thread.currentThread().getThreadGroup().getName();
         isDaemon = Thread.currentThread().isDaemon();
         System.out.println("Thread Name:" + name);
         System.out.println("Priority:" + priority);

나열된 속성은 다음과 같습니다.


GroupName, 각 스레드는 By 기본적으로 스레드 그룹에 있습니다. 또한 스레드 그룹을 명시적으로 생성할 수도 있습니다. 스레드 그룹은 스레드와 스레드 그룹이 트리 구조를 형성하도록 하위 스레드 그룹을 포함할 수도 있습니다.

이름, 각 스레드에는 이름이 지정됩니다. 명시적으로 지정하지 않으면 이름 규칙은 "Thread-xxx"입니다.

우선순위, 각 스레드는 고유한 우선순위를 가지며 JVM의 우선순위 처리 방식은 "선점형"입니다. JVM은 우선순위가 높은 스레드를 찾으면 즉시 우선순위가 동일한 여러 스레드에 대해 스레드를 실행하고 이를 폴링합니다. Java의 스레드 우선순위 범위는 1에서 10까지이며 기본값은 5입니다. Thread 클래스는 가장 높은 우선순위와 가장 낮은 우선순위를 나타내는 MIN_PRIORITY 및 MAX_PRIORITY라는 두 가지 상수를 정의합니다.

우선순위가 다른 두 개의 스레드를 정의하는 다음 코드를 볼 수 있습니다.

线程优先级示例
 public static void priorityTest()
 {
     Thread thread1 = new Thread("low")
     {
         public void run()
         {
             for (int i = 0; i < 5; i++)
             {
                 System.out.println("Thread 1 is running.");
             }
         }
     };

     Thread thread2 = new Thread("high")
     {
         public void run()
         {
             for (int i = 0; i < 5; i++)
             {
                 System.out.println("Thread 2 is running.");
             }
         }
     };

     thread1.setPriority(Thread.MIN_PRIORITY);
     thread2.setPriority(Thread.MAX_PRIORITY);
     thread1.start();
     thread2.start();
 }

실행 결과에서 볼 수 있듯이 우선순위가 높은 스레드의 실행이 완료된 후 낮은 우선순위 스레드 우선순위 스레드만 실행됩니다.
isDaemon, 이 속성은 상위 스레드와 하위 스레드 간의 관계를 제어하는 ​​데 사용됩니다. true로 설정하면 상위 스레드가 종료되면 그 아래의 모든 하위 스레드도 종료됩니다. 상위 스레드의 영향을 받지 않습니다.
다음 예를 살펴보겠습니다.

IsDaemon 示例
 public static void daemonTest()
 {
     Thread thread1 = new Thread("daemon")
     {
         public void run()
         {
             Thread subThread = new Thread("sub")
             {
                 public void run()
                 {
                     for(int i = 0; i < 100; i++)
                     {
                         System.out.println("Sub Thread Running " + i);
                     }
                 }
             };
             subThread.setDaemon(true);
             subThread.start();
             System.out.println("Main Thread end.");
         }
     };

     thread1.start();
 }

위 코드의 실행 결과와 subThread.setDaemon(true);을 삭제한 후의 실행 결과를 비교하면 하위 스레드가 후자의 작업 중에 실행이 완료되고 전자의 경우 하위 스레드가 곧 종료됩니다.


스레드 생성 방법

위 내용은 모두 기본 스레드의 일부 정보를 보여주는 내용인데, 스레드를 어떻게 생성해야 할까요? Java에서는 스레드를 생성하는 세 가지 방법이 있습니다.

Java의 스레드는 Thread 클래스를 상속하거나 Runnable 인터페이스를 구현합니다.

내부 클래스를 사용하여 스레드 생성

내부 클래스를 사용하여 스레드를 생성할 수 있습니다. 프로세스는 Thread 유형 변수를 선언하고 실행 메서드를 재정의하는 것입니다. 샘플 코드는 다음과 같습니다.

使用内部类创建线程
 public static void createThreadByNestClass()
 {
     Thread thread = new Thread()
     {
         public void run()
         {
             for (int i =0; i < 5; i++)
             {
                 System.out.println("Thread " + Thread.currentThread().getName() + " is running.");
             }
             System.out.println("Thread " + Thread.currentThread().getName() + " is finished.");
         }
     };
     thread.start();
 }

Thread를 상속하여 스레드 생성


Thread에서 클래스를 파생시키고 실행 메서드를 재정의할 수 있습니다. 방법은 위와 비슷합니다. 샘플 코드는 다음과 같습니다.

派生Thread类以创建线程
 class MyThread extends Thread
 {
     public void run()
     {
         for (int i =0; i < 5; i++)
         {
             System.out.println("Thread " + Thread.currentThread().getName() + " is running.");
         }
         System.out.println("Thread " + Thread.currentThread().getName() + " is finished.");
     }
 }

 
 public static void createThreadBySubClass()
 {
     MyThread thread = new MyThread();
     thread.start();
 }

스레드 생성을 위한 Runnable 인터페이스 구현


Runnable 인터페이스를 구현하도록 클래스를 정의할 수 있습니다. , 그리고 클래스 인스턴스를 매개변수로 사용하여 Thread 변수 생성자를 구성합니다. 샘플 코드는 다음과 같습니다.

实现Runnable接口以创建线程
 class MyRunnable implements Runnable
 {
     public void run() 
     {
         for (int i =0; i < 5; i++)
         {
             System.out.println("Thread " + Thread.currentThread().getName() + " is running.");
         }
         System.out.println("Thread " + Thread.currentThread().getName() + " is finished.");
     }
 }

 
 public static void createThreadByRunnable()
 {
     MyRunnable runnable = new MyRunnable();
     Thread thread = new Thread(runnable);
     thread.start();
 }

위 3가지 방법으로 스레드를 생성할 수 있으며, 샘플 코드에서 스레드가 수행하는 기능은 동일하므로 이 3가지 생성 방법의 차이점은 무엇입니까? 행동 양식?


Java의 경우 멀티스레딩이 실행되는 경우 "멀티객체 멀티스레딩"과 "싱글 스레드"가 있습니다. -객체 멀티 스레딩" "차이점:

다중 객체 멀티 스레딩에서는 프로그램이 실행 중에 여러 스레드 개체를 생성하고 각 개체에서 하나의 스레드가 실행됩니다.
단일 객체 멀티스레딩인 프로그램은 실행 중에 스레드 객체를 생성하고 그 위에서 여러 스레드를 실행합니다.

분명히 스레드 동기화 및 예약 관점에서 볼 때 다중 객체 다중 스레딩이 더 간단합니다. 위의 세 가지 스레드 생성 방법 중 처음 두 가지는 "다중 객체 다중 스레드"이고, 세 번째는 "다중 객체 다중 스레드" 또는 "단일 객체 단일 스레드"를 사용할 수 있습니다.

Object.notify 메서드를 사용하는 다음 샘플 코드를 살펴보겠습니다. 이 메서드는 개체의 스레드를 깨우고 Object.notifyAll 메서드는 개체의 모든 스레드를 깨웁니다.

notify示例
 public class NotifySample {

     public static void main(String[] args) throws InterruptedException
     {
         notifyTest();
         notifyTest2();
         notifyTest3();
     }

     private static void notifyTest() throws InterruptedException
     {
         MyThread[] arrThreads = new MyThread[3];
         for (int i = 0; i < arrThreads.length; i++)
         {
             arrThreads[i] = new MyThread();
             arrThreads[i].id = i;
             arrThreads[i].setDaemon(true);
             arrThreads[i].start();
         }
         Thread.sleep(500);
         for (int i = 0; i < arrThreads.length; i++)
         {
             synchronized(arrThreads[i])
             {
                 arrThreads[i].notify();
             }
         }
     }

     private static void notifyTest2() throws InterruptedException
     {
         MyRunner[] arrMyRunners = new MyRunner[3];
         Thread[] arrThreads = new Thread[3];
         for (int i = 0; i < arrThreads.length; i++)
         {
             arrMyRunners[i] = new MyRunner();
             arrMyRunners[i].id = i;
             arrThreads[i] = new Thread(arrMyRunners[i]);
             arrThreads[i].setDaemon(true);
             arrThreads[i].start();
         }
         Thread.sleep(500);
         for (int i = 0; i < arrMyRunners.length; i++)
         {
             synchronized(arrMyRunners[i])
             {
                 arrMyRunners[i].notify();
             }
         }
     }

     private static void notifyTest3() throws InterruptedException
     {
         MyRunner runner = new MyRunner();
         Thread[] arrThreads = new Thread[3];
         for (int i = 0; i < arrThreads.length; i++)
         {
             arrThreads[i] = new Thread(runner);
             arrThreads[i].setDaemon(true);
             arrThreads[i].start();
         }
         Thread.sleep(500);

         synchronized(runner)
         {
             runner.notifyAll();
         }
     }
 }

 class MyThread extends Thread
 {
     public int id = 0;
     public void run()
     {
         System.out.println("第" + id + "个线程准备休眠5分钟。");
         try
         {
             synchronized(this)
             {
                 this.wait(5*60*1000);
             }
         }
         catch(InterruptedException ex)
         {
             ex.printStackTrace();
         }
         System.out.println("第" + id + "个线程被唤醒。");
     }
 }

 class MyRunner implements Runnable
 {
     public int id = 0;
     public void run() 
     {
         System.out.println("第" + id + "个线程准备休眠5分钟。");
         try
         {
             synchronized(this)
             {
                 this.wait(5*60*1000);
             }
         }
         catch(InterruptedException ex)
         {
             ex.printStackTrace();
         }
         System.out.println("第" + id + "个线程被唤醒。");
     }

 }

샘플 코드에서 informTest() 및 informTest2()는 "다중 객체 다중 스레딩"입니다. 통지 테스트2()의 스레드는 Runnable 인터페이스를 구현하지만 그 안에 Thread 배열을 정의할 때 , 각 요소는 새로운 Runnable 인스턴스를 사용합니다. informTest3()은 하나의 Runnable 인스턴스만 정의하고 모든 스레드가 이 인스턴스를 사용하기 때문에 "단일 객체 다중 스레딩"에 속합니다.


notifyAll 메소드는 "단일 객체 다중 스레드" 시나리오에 적합합니다. 왜냐하면 통지 메소드는 객체에서 하나의 스레드만 무작위로 깨우기 때문입니다.

스레드 상태 전환

스레드의 경우 스레드를 생성한 시점부터 스레드 실행이 끝날 때까지 이 과정에서 스레드의 상태는 다음과 같을 수 있습니다.

    创建:已经有Thread实例了, 但是CPU还有为其分配资源和时间片。
    就绪:线程已经获得了运行所需的所有资源,只等CPU进行时间调度。
    运行:线程位于当前CPU时间片中,正在执行相关逻辑。
    休眠:一般是调用Thread.sleep后的状态,这时线程依然持有运行所需的各种资源,但是不会被CPU调度。
    挂起:一般是调用Thread.suspend后的状态,和休眠类似,CPU不会调度该线程,不同的是,这种状态下,线程会释放所有资源。
    死亡:线程运行结束或者调用了Thread.stop方法。

下面我们来演示如何进行线程状态切换,首先我们会用到下面方法:

    Thread()或者Thread(Runnable):构造线程。
    Thread.start:启动线程。
    Thread.sleep:将线程切换至休眠状态。
    Thread.interrupt:中断线程的执行。
    Thread.join:等待某线程结束。
    Thread.yield:剥夺线程在CPU上的执行时间片,等待下一次调度。
    Object.wait:将Object上所有线程锁定,直到notify方法才继续运行。
    Object.notify:随机唤醒Object上的1个线程。
    Object.notifyAll:唤醒Object上的所有线程。

下面,就是演示时间啦!!!

线程等待与唤醒

这里主要使用Object.wait和Object.notify方法,请参见上面的notify实例。需要注意的是,wait和notify都必须针对同一个对象,当我们使用实现Runnable接口的方式来创建线程时,应该是在Runnable对象而非Thread对象上使用这两个方法。

线程的休眠与唤醒

Thread.sleep实例
 public class SleepSample {

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

     private static void sleepTest() throws InterruptedException
     {
         Thread thread = new Thread()
         {
             public void run()
             {
                 System.out.println("线程 " + Thread.currentThread().getName() + "将要休眠5分钟。");
                 try
                 {
                     Thread.sleep(5*60*1000);
                 }
                 catch(InterruptedException ex)
                 {
                     System.out.println("线程 " + Thread.currentThread().getName() + "休眠被中断。");
                 }
                 System.out.println("线程 " + Thread.currentThread().getName() + "休眠结束。");
             }
         };
         thread.setDaemon(true);
         thread.start();
         Thread.sleep(500);
         thread.interrupt();
     }

 }

线程在休眠过程中,我们可以使用Thread.interrupt将其唤醒,这时线程会抛出InterruptedException。

线程的终止


虽然有Thread.stop方法,但该方法是不被推荐使用的,我们可以利用上面休眠与唤醒的机制,让线程在处理IterruptedException时,结束线程。

Thread.interrupt示例
 public class StopThreadSample {

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

     private static void stopTest() throws InterruptedException
     {
         Thread thread = new Thread()
         {
             public void run()
             {
                 System.out.println("线程运行中。");
                 try
                 {
                     Thread.sleep(1*60*1000);
                 }
                 catch(InterruptedException ex)
                 {
                     System.out.println("线程中断,结束线程");
                     return;
                 }
                 System.out.println("线程正常结束。");
             }
         };
         thread.start();
         Thread.sleep(500);
         thread.interrupt();
     }
 }

线程的同步等待


当我们在主线程中创建了10个子线程,然后我们期望10个子线程全部结束后,主线程在执行接下来的逻辑,这时,就该Thread.join登场了。

Thread.join示例
 public class JoinSample {

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

     private static void joinTest() throws InterruptedException
     {
         Thread thread = new Thread()
         {
             public void run()
             {
                 try
                 {
                     for(int i = 0; i < 5; i++)
                     {
                         System.out.println("线程在运行。");
                         Thread.sleep(1000);
                     }
                 }
                 catch(InterruptedException ex)
                 {
                     ex.printStackTrace();
                 }
             }
         };
         thread.setDaemon(true);
         thread.start();
         Thread.sleep(1000);
         thread.join();
         System.out.println("主线程正常结束。");
     }
 }

我们可以试着将thread.join();注释或者删除,再次运行程序,就可以发现不同了。

线程间通信


我们知道,一个进程下面的所有线程是共享内存空间的,那么我们如何在不同的线程之间传递消息呢?在回顾 Java I/O时,我们谈到了PipedStream和PipedReader,这里,就是它们发挥作用的地方了。

下面的两个示例,功能完全一样,不同的是一个使用Stream,一个使用Reader/Writer。

PipeInputStream/PipedOutpueStream 示例
 public static void communicationTest() throws IOException, InterruptedException
 {
     final PipedOutputStream pos = new PipedOutputStream();
     final PipedInputStream pis = new PipedInputStream(pos);

     Thread thread1 = new Thread()
     {
         public void run()
         {
             BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
             try
             {
                 while(true)
                 {
                     String message = br.readLine();
                     pos.write(message.getBytes());
                     if (message.equals("end")) break;
                 }
                 br.close();
                 pos.close();
             }
             catch(Exception ex)
             {
                 ex.printStackTrace();
             }
         }
     };

     Thread thread2 = new Thread()
     {
         public void run()
         {
             byte[] buffer = new byte[1024];
             int bytesRead = 0;
             try
             {
                 while((bytesRead = pis.read(buffer, 0, buffer.length)) != -1)
                 {
                     System.out.println(new String(buffer));
                     if (new String(buffer).equals("end")) break;
                     buffer = null;
                     buffer = new byte[1024];
                 }
                 pis.close();
                 buffer = null;
             }
             catch(Exception ex)
             {
                 ex.printStackTrace();
             }
         }
     };

     thread1.setDaemon(true);
     thread2.setDaemon(true);
     thread1.start();
     thread2.start();
     thread1.join();
     thread2.join();
 }
PipedReader/PipedWriter 示例
 private static void communicationTest2() throws InterruptedException, IOException
 {
     final PipedWriter pw = new PipedWriter();
     final PipedReader pr = new PipedReader(pw);
     final BufferedWriter bw = new BufferedWriter(pw);
     final BufferedReader br = new BufferedReader(pr);

     Thread thread1 = new Thread()
     {
         public void run()
         {

             BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
             try
             {
                 while(true)
                 {
                     String message = br.readLine();
                     bw.write(message);
                     bw.newLine();
                     bw.flush();
                     if (message.equals("end")) break;
                 }
                 br.close();
                 pw.close();
                 bw.close();
             }
             catch(Exception ex)
             {
                 ex.printStackTrace();
             }
         }
     };

     Thread thread2 = new Thread()
     {
         public void run()
         {

             String line = null;
             try
             {
                 while((line = br.readLine()) != null)
                 {
                     System.out.println(line);
                     if (line.equals("end")) break;
                 }
                 br.close();
                 pr.close();
             }
             catch(Exception ex)
             {
                 ex.printStackTrace();
             }
         }
     };

     thread1.setDaemon(true);
     thread2.setDaemon(true);
     thread1.start();
     thread2.start();
     thread1.join();
     thread2.join();
 }

 以上就是基于Java回顾之多线程详解的内容,更多相关内容请关注PHP中文网(www.php.cn)! 


성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.