> Java > java지도 시간 > 본문

Java 멀티스레딩에 대한 기본 지식 요약(코드 포함)

不言
풀어 주다: 2019-02-21 14:31:04
앞으로
2809명이 탐색했습니다.

이 기사는 Java 멀티스레딩(코드 포함)에 대한 기본 지식을 요약한 것입니다. 필요한 참고 자료가 있으면 도움이 될 것입니다.

Java 메인 스레드 이름

우리가 시작하는 프로그램은 프로세스로 이해될 수 있습니다. 프로세스에는 메인 스레드가 포함되어 있으며 스레드는 하위 작업으로 이해될 수 있습니다. 다음 코드.

System.out.println(Thread.currentThread().getName());
로그인 후 복사

실행 결과는 메인 메소드가 아닌 스레드의 이름입니다. 이 스레드를 통해 메인 메소드가 실행됩니다.

스레드를 생성하는 두 가지 방법

1. Thread 클래스

public class Thread1 extends Thread {
    @Override
    public void run() {
        System.out.println("qwe");
    }
}
로그인 후 복사

2. Runnable 인터페이스 구현

public class Thread2 implements Runnable {
    @Override
    public void run() {
        System.out.println("asd");
    }
}
로그인 후 복사
Thread는 Runnable 인터페이스를 구현합니다. 그리고 여러 스레드에서 실행될 때 코드의 실행 순서는 호출 순서와 관련이 없습니다. 여러 번 java.lang.IllegalThreadStateException이 발생합니다. currentThread 메소드

는 현재 호출 중인 코드가 무엇인지 반환합니다.

public class Thread1 extends Thread {

    public Thread1() {
        System.out.println("构造方法的打印:" + Thread.currentThread().getName());
    }

    @Override
    public void run() {
        System.out.println("run 方法的打印:" + Thread.currentThread().getName());
    }
}
로그인 후 복사
Thread1 thread1 = new Thread1();
thread1.start();
로그인 후 복사

isAlive 메소드

는 현재 스레드가 활성 상태인지 확인합니다.

public class Thread1 extends Thread {
    @Override
    public void run() {
        System.out.println("run 方法的打印 Thread.currentThread().isAlive() == " + Thread.currentThread().isAlive());
        System.out.println("run 方法的打印 this.isAlive() == " + this.isAlive());
        System.out.println("run 方法的打印 Thread.currentThread().isAlive() == this.isAlive() == " + (Thread.currentThread() == this ? "true" : "false"));
    }
}
로그인 후 복사
Thread1 thread1 = new Thread1();

System.out.println("begin == " + thread1.isAlive());
thread1.start();
Thread.sleep(1000);
System.out.println("end == " + thread1.isAlive());
로그인 후 복사

는 다음과 같습니다

begin == false
run 方法的打印 Thread.currentThread().isAlive() == true
run 方法的打印 this.isAlive() == true
run 方法的打印 Thread.currentThread() == this == true
end == false
로그인 후 복사

thread1은 1초 후에 실행되므로 출력 결과는 false입니다. 그리고 Thread.currentThread()는 동일한 객체이며 현재 실행 메서드를 실행하는 스레드 객체가 우리 자신이라는 것을 알 수 있습니다. this).

start() 시작을 위한 생성 매개변수 형식으로 스레드 객체를 Thread 객체에 전달하면 실행 결과가 이전 예제와 다릅니다. 이러한 차이가 발생하는 이유는 여전히 Thread.currentThread() 및

System.out.println("begin == " + thread1.isAlive());
//thread1.start();
// 如果将线程对象以构造参数的方式传递给 Thread 对象进行 start() 启动
Thread thread = new Thread(thread1);
thread.start();

Thread.sleep(1000);
System.out.println("end == " + thread1.isAlive());
로그인 후 복사

실행 결과

begin == false
run 方法的打印 Thread.currentThread().isAlive() == true
run 方法的打印 this.isAlive() == false
run 方法的打印 Thread.currentThread() == this == false
end == false
로그인 후 복사

Thread.currentThread().isAlive()가 true인 이유는 Thread.currentThread()가 스레드 개체를 반환하기 때문이며 우리도 이 개체를 사용하여 스레드를 시작하므로 활성 상태입니다. 스레드 개체를 사용하기 때문에 this.isAlive()가 false인지 이해하기 쉽습니다. 스레드가 run 메서드를 실행하기 시작했다는 의미이기도 합니다. 둘은 동일한 개체가 아닙니다.

sleep 메서드

는 현재 "실행 스레드"를 지정된 밀리초 내에 절전 모드로 만듭니다. "스레드 실행"은 Thread.currentThread()에서 반환된 스레드일 수 있습니다. .

Thread.sleep(1000);
로그인 후 복사

스레드 중지

스레드 중지는 스레드가 작업을 완료하기 전에 수행 중인 작업을 중지하는 것, 즉 현재 작업을 포기하는 것을 의미합니다. Thread.currentThread() 返回的线程.

public class Thread1 extends Thread {
    @Override
    public void run() {

        for(int i = 0; i < 500000; i++) {
            System.out.println(i);
        }
    }
}
로그인 후 복사

停止线程

停止一个线程意味着在线程处理完任务之前停掉正在做的操作, 也就是放弃当前的操作.

在 Java 中有以下 3 种方法可以终止正在运行的线程:

  1. 使用退出标志, 使线程正常退出, 也就是当 run 方法完成后线程终止.

  2. 使用 stop 方法强行终止线程, 但是不推荐使用这个方法.

  3. 使用 interrupt 方法中断线程.

停不了的线程

调用 interrupt 方法仅仅是当前线程打了一个停止标记, 并不是真正的停止线程.

Thread1 thread1 = new Thread1();
thread1.start();
Thread.sleep(2000);
thread1.interrupt();
로그인 후 복사
    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }
    
    public boolean isInterrupted() {
        return isInterrupted(false);
    }
로그인 후 복사

我们两秒后调用 interrupt 方法, 根据打印结果我们可以看到线程并没有停止, 而是在执行完 500000 此循环后 run 方法结束线程停止.

判断线程是否是停止状态

我们将线程标记为停止后, 需要在线程内部判断一下这个线程是否是停止标记, 如果是则停止线程.

两种判断方法:

  1. Thread.interrupted(); 也可以使用 this.interrupted();

  2. this.isInterrupted();

下面是两个方法的源码:

public class Thread1 extends Thread {
    @Override
    public void run() {
        try {
            for (int i = 0; i < 500000; i++) {
                if (this.isInterrupted()) {
                    System.out.println("线程停止");
                    throw new InterruptedException();
                }

                System.out.println("i = " + i);
            }
        } catch (InterruptedException e) {
            System.out.println("线程通过 catch 停止");
            e.printStackTrace();
        }
    }
}
로그인 후 복사

interrupted() 方法数据静态方法, 也就是判断当前线程是否已经中断. isInterrupted() 判断线程是否已经被中断.

来自官网的 interrupted() 方法重点.
线程的 中断状态 由该方法清除. 换句话说, 如果连续两次调用该方法, 则第二次调用将返回 false (在第一次调用已清除了其中断状态之后, 且第二次调用检验完中断状态前, 当前线程再次中断的情况除外).

异常停止线程

Thread1 thread1 = new Thread1();
thread1.start();
Thread.sleep(1000);
thread1.interrupt();
로그인 후 복사
i = 195173
i = 195174
i = 195175
线程停止
线程通过 catch 停止
java.lang.InterruptedException
    at Thread1.run(Thread1.java:9)
로그인 후 복사

输出结果, 这是最后几行:

java.lang.InterruptedException: sleep interrupted
    at java.lang.Thread.sleep(Native Method)
    at Thread1.run(Thread1.java:8)
로그인 후 복사
当然我们也可以将 throw new InterruptedException(); 换成 return. 都是一样可以结束线程的.

在沉睡中停止

如果线程调用 Thread.sleep() 方法使线程进行休眠, 这时我们调用 thread1.interrupt()后会抛出. InterruptedException 异常.

public class Thread1 extends Thread {

    private String userName = "a";
    private String pwd = "aa";

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    @Override
    public void run() {
        this.userName = "b";
        try {
            Thread.sleep(100000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.pwd = "bb";

    }
}
로그인 후 복사
로그인 후 복사

暴力停止线程

暴力停止线程可以使用 stop

스레드를 종료하는 방법에는 다음 3가지가 있습니다. Java에서 스레드 실행: 🎜
  1. 🎜종료 플래그를 사용하여 스레드가 정상적으로 종료되도록 합니다. 즉, 실행 시 메서드가 완료된 후 스레드가 종료됩니다.🎜 li>
  2. 🎜스레드를 강제로 종료하려면 stop 메소드를 사용하지만 이 방법은 권장하지 않습니다.🎜
  3. 🎜스레드를 중단하려면 인터럽트 메소드를 사용하세요.🎜
  4. 중지할 수 없는 스레드

    🎜인터럽트 메서드 호출은 현재 스레드에 대한 중지 표시만 표시할 뿐 실제로 스레드를 중지하지는 않습니다.🎜
    Thread1 thread1 = new Thread1();
    thread1.start();
    Thread.sleep(1000);
    thread1.stop();
    
    System.out.println(thread1.getUserName() + "     " + thread1.getPwd());
    로그인 후 복사
    로그인 후 복사
    b     aa
    로그인 후 복사
    로그인 후 복사
    🎜우리는 2초 후에 인터럽트 메서드를 호출합니다. 인쇄된 결과를 보면 스레드가 멈추지 않는 것을 알 수 있지만 이 루프를 500000회 실행한 후에는 run 메소드가 종료되고 스레드가 중지됩니다.🎜🎜🎜스레드가 중지되었는지 확인하려면🎜
    🎜🎜 스레드를 중지됨으로 표시한 후에는 이 스레드가 중지 표시인지 내부적으로 확인해야 하며, 그렇다면 스레드를 중지해야 합니다. 🎜🎜두 가지 판단 방법: 🎜
    1. 🎜Thread.interrupted(); 이 .interrupted();🎜
    2. 🎜this.isInterrupted();🎜
    🎜다음은 의 소스 코드입니다. 두 가지 메서드:🎜
    public class PrintObject {
        synchronized public void printString(){
            System.out.println("begin");
            if(Thread.currentThread().getName().equals("a")){
                System.out.println("线程 a 被中断");
                Thread.currentThread().suspend();
            }
            if(Thread.currentThread().getName().equals("b")){
                System.out.println("线程 b 运行");
            }
            System.out.println("end");
        }
    }
    로그인 후 복사
    로그인 후 복사
    🎜interrupted() 메서드 데이터 현재 스레드가 중단되었는지 여부를 확인하는 정적 메서드입니다. isInterrupted()는 스레드가 중단되었는지 여부를 확인합니다. 🎜🎜공식 홈페이지의 interrupted() 메소드를 중심으로
    이 메소드를 사용하면 스레드의 인터럽트 상태가 해제됩니다. 두 번째 호출은 false를 반환합니다(첫 번째 호출이 인터럽트 상태를 지운 후, 두 번째 호출은 현재 스레드가 다시 중단되는 경우를 제외하고 각 호출 후 중단 상태를 확인하기 전).🎜

    예외적으로 중지 스레드

    try{
        PrintObject  pb = new PrintObject();
        Thread thread1 = new Thread(pb::printString);
        thread1.setName("a");
        thread1.start();
        thread1.sleep(1000);
    
        Thread thread2 = new Thread(pb::printString);
        thread2.setName("b");
        thread2.start();
    }catch(InterruptedException e){ }
    로그인 후 복사
    로그인 후 복사
    begin
    线程 a 被中断
    로그인 후 복사
    로그인 후 복사
    🎜결과를 출력합니다. 다음은 마지막 몇 줄입니다:🎜rrreee🎜물론 throw new InterruptedException();를 사용할 수도 있습니다. return으로 바꾸기 >. 같은 방법으로 스레드를 종료할 수 있습니다. 🎜

    Stop in sleep

    🎜스레드가 Thread를 호출하면 sleep() 메서드가 스레드를 절전 모드로 전환합니다. thread1.interrupt()를 호출하면 InterruptedException 예외가 발생합니다. 🎜rrreee

    폭력 스레드 중지

    🎜 스레드를 폭력적으로 중지하려면 다음을 수행하세요. stop 방법을 사용하세요. 하지만 이 방법은 오래되었으며 다음과 같은 이유로 권장되지 않습니다.🎜
    1. 即刻抛出 ThreadDeath 异常, 在线程的run()方法内, 任何一点都有可能抛出ThreadDeath Error, 包括在 catch 或 finally 语句中. 也就是说代码不确定执行到哪一步就会抛出异常.

    2. 释放该线程所持有的所有的锁. 这可能会导致数据不一致性.

    public class Thread1 extends Thread {
    
        private String userName = "a";
        private String pwd = "aa";
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public String getPwd() {
            return pwd;
        }
    
        public void setPwd(String pwd) {
            this.pwd = pwd;
        }
    
        @Override
        public void run() {
            this.userName = "b";
            try {
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.pwd = "bb";
    
        }
    }
    로그인 후 복사
    로그인 후 복사
    Thread1 thread1 = new Thread1();
    thread1.start();
    Thread.sleep(1000);
    thread1.stop();
    
    System.out.println(thread1.getUserName() + "     " + thread1.getPwd());
    로그인 후 복사
    로그인 후 복사

    输出结果为:

    b     aa
    로그인 후 복사
    로그인 후 복사

    我们在代码中然线程休眠 Thread.sleep(100000); 是为了模拟一些其它业务逻辑处理所用的时间, 在线程处理其它业务的时候, 我们调用 stop 方法来停止线程.

    线程是被停止了也执行了 System.out.println(thread1.getUserName() + "     " + thread1.getPwd()); 来帮我们输出结果, 但是 this.pwd = "bb"; 并没有被执行.

    所以, 当调用了 stop 方法后, 线程无论执行到哪段代码, 线程就会立即退出, 并且会抛出 ThreadDeath 异常, 而且会释放所有锁, 从而导致数据不一致的情况.

    interrupt 相比 stop 方法更可控, 而且可以保持数据一致, 当你的代码逻辑执行完一次, 下一次执行的时候, 才会去判断并退出线程.

    如果大家不怎么理解推荐查看 为什么不能使用Thread.stop()方法? 这篇文章. 下面是另一个比较好的例子.

    如果线程当前正持有锁(此线程可以执行代码), stop之后则会释放该锁. 由于此错误可能出现在很多地方, 那么这就让编程人员防不胜防, 极易造成对象状态的不一致. 例如, 对象 obj 中存放着一个范围值: 最小值low, 最大值high, 且low不得大于high, 这种关系由锁lock保护, 以避免并发时产生竞态条件而导致该关系失效.

    假设当前low值是5, high值是10, 当线程t获取lock后, 将low值更新为了15, 此时被stop了, 真是糟糕, 如果没有捕获住stop导致的Error, low的值就为15, high还是10, 这导致它们之间的小于关系得不到保证, 也就是对象状态被破坏了!

    如果在给low赋值的时候catch住stop导致的Error则可能使后面high变量的赋值继续, 但是谁也不知道Error会在哪条语句抛出, 如果对象状态之间的关系更复杂呢?这种方式几乎是无法维护的, 太复杂了!如果是中断操作, 它决计不会在执行low赋值的时候抛出错误, 这样程序对于对象状态一致性就是可控的.

    suspend 与 resume 方法

    用来暂停和恢复线程.

    独占

    public class PrintObject {
        synchronized public void printString(){
            System.out.println("begin");
            if(Thread.currentThread().getName().equals("a")){
                System.out.println("线程 a 被中断");
                Thread.currentThread().suspend();
            }
            if(Thread.currentThread().getName().equals("b")){
                System.out.println("线程 b 运行");
            }
            System.out.println("end");
        }
    }
    로그인 후 복사
    로그인 후 복사
    try{
        PrintObject  pb = new PrintObject();
        Thread thread1 = new Thread(pb::printString);
        thread1.setName("a");
        thread1.start();
        thread1.sleep(1000);
    
        Thread thread2 = new Thread(pb::printString);
        thread2.setName("b");
        thread2.start();
    }catch(InterruptedException e){ }
    로그인 후 복사
    로그인 후 복사

    输出结果:

    begin
    线程 a 被中断
    로그인 후 복사
    로그인 후 복사

    当调用 Thread.currentThread().suspend(); 方法来暂停线程时, 锁并不会被释放, 所以造成了同步对象的独占.


위 내용은 Java 멀티스레딩에 대한 기본 지식 요약(코드 포함)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:segmentfault.com
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿