Java 多线程

1. 进程、线程

进程的特征是:

  • 进程就是一个执行中的程序,有自己独立的一块内存空间,一组系统资源。
    每一个进程的内部数据和状态都是完全独立的
  • 创建并执行一个进程的系统开销是比较大
  • 进程是程序的一次执行过程,是系统运行程序的基本单位

线程的特征是:

  • 程序中单个顺序的流控制称为线程
  • 多线程指的是在单个进程中可以同时运行多个不同的线程,执行不同的任务。多线程意味着一个程序的多行语句可以看上去几乎同时运行

同类的多个线程共享一块内存空间和一组系统资源,而线程本身的数据通常只有微处理器的寄存器数据,以及一个供程序执行时使用的堆栈。

  • 产生一个线程,或者在各个线程之间切换时,负担要比进程小得多,线程也被称为轻负荷进程

线程和进程的主要差别

  • 线程是比进程更小的执行单位
  • 每个进程都有一段专用的内存区域。与此相反,线程共享内存单元(包括代码和数据),通过共享的内存单元来实现数据交换、实时通信与必要的同步操作

2. 认识线程 Thread

class TestThread{
    public void run(){
        for(int i = 0; i < 10; ++i)
            System.out.println("TestThread is running");
    }
}
class ThreadDemo1 {
    public static void main(String[] args){
        new TestThread().run();
        for(int i = 0; i < 10; ++i)
            System.out.println("main 线程在运行");
    }
}

输出:(要执行main,必须等 Test运行完)

TestThread is running
TestThread is running
TestThread is running
TestThread is running
TestThread is running
TestThread is running
TestThread is running
TestThread is running
TestThread is running
TestThread is running
main 线程在运行
main 线程在运行
main 线程在运行
main 线程在运行
main 线程在运行
main 线程在运行
main 线程在运行
main 线程在运行
main 线程在运行
main 线程在运行

2.1 继承 Thread 类实现多线程

class TestThread extends Thread{ // 继承 Thread 类
    public void run(){
        for(int i = 0; i < 10; ++i)
            System.out.println("TestThread is running");
    }
}
class ThreadDemo1 {
    public static void main(String[] args){
        new TestThread().start(); // 使用 start 启动
        for(int i = 0; i < 10; ++i)
            System.out.println("main 线程在运行");
    }
}

输出:

main 线程在运行
main 线程在运行
main 线程在运行
main 线程在运行
main 线程在运行
main 线程在运行
main 线程在运行
main 线程在运行
main 线程在运行
main 线程在运行
TestThread is running
TestThread is running
TestThread is running
TestThread is running
TestThread is running
TestThread is running
TestThread is running
TestThread is running
TestThread is running
TestThread is running

2.2 实现 Runnable 接口实现多线程

class TestThread implements Runnable{ // 多线程实现类
    public void run(){
        for(int i = 0; i < 10; ++i)
            System.out.println("TestThread is running");
    }
}
class ThreadDemo1 {
    public static void main(String[] args){
        TestThread t = new TestThread();
        new Thread(t).start();//启动多线程
        for(int i = 0; i < 10; ++i)
            System.out.println("main 线程在运行");
    }
}

输出:

main 线程在运行
main 线程在运行
main 线程在运行
main 线程在运行
main 线程在运行
main 线程在运行
main 线程在运行
main 线程在运行
TestThread is running
TestThread is running
TestThread is running
TestThread is running
TestThread is running
TestThread is running
TestThread is running
TestThread is running
TestThread is running
TestThread is running
main 线程在运行
main 线程在运行

2.3 两者对比

class TestThread extends Thread {
    private int tickets = 20;
    public void run(){
        while(true){
            if(tickets > 0)
                System.out.println(Thread.currentThread().getName()
                    + "售出一张票,剩余票数:" + --tickets);
        }
    }
}
class ThreadDemo1 {
    public static void main(String[] args){
        new TestThread().start();
        new TestThread().start();
        new TestThread().start();
        new TestThread().start();
    }
}

输出:(部分结果)

Thread-1售出一张票,剩余票数:19
Thread-0售出一张票,剩余票数:19
Thread-1售出一张票,剩余票数:18
Thread-0售出一张票,剩余票数:18
Thread-1售出一张票,剩余票数:17
Thread-2售出一张票,剩余票数:19
Thread-0售出一张票,剩余票数:17
Thread-3售出一张票,剩余票数:19
Thread-3售出一张票,剩余票数:18
Thread-3售出一张票,剩余票数:17
Thread-3售出一张票,剩余票数:16

结论:继承Thread,不能达到线程资源共享

class TestThread implements Runnable {
    private int tickets = 20;
    public void run(){
        while(true){
            if(tickets > 0)
                System.out.println(Thread.currentThread().getName()
                    + "售出一张票,剩余票数:" + --tickets);
        }
    }
}
class ThreadDemo1 {
    public static void main(String[] args){
        TestThread t = new TestThread();
        new Thread(t).start();
        new Thread(t).start();
        new Thread(t).start();
        new Thread(t).start();
    }
}

输出:(完整)

Thread-1售出一张票,剩余票数:19
Thread-3售出一张票,剩余票数:16
Thread-3售出一张票,剩余票数:14
Thread-3售出一张票,剩余票数:13
Thread-3售出一张票,剩余票数:12
Thread-3售出一张票,剩余票数:11
Thread-3售出一张票,剩余票数:10
Thread-2售出一张票,剩余票数:17
Thread-0售出一张票,剩余票数:18
Thread-0售出一张票,剩余票数:7
Thread-0售出一张票,剩余票数:6
Thread-0售出一张票,剩余票数:5
Thread-2售出一张票,剩余票数:8
Thread-3售出一张票,剩余票数:9
Thread-3售出一张票,剩余票数:2
Thread-3售出一张票,剩余票数:1
Thread-3售出一张票,剩余票数:0
Thread-1售出一张票,剩余票数:15
Thread-2售出一张票,剩余票数:3
Thread-0售出一张票,剩余票数:4

结论:实现 Runnable 可以实现资源共享,这种方法更具优势!

3. 线程的状态

4. 线程操作方法

4.1 线程名称

  • getName(), setName()
class TestThread implements Runnable {
    private int tickets = 20;
    public void run(){
        while(true){
            if(tickets > 0)
                System.out.println(Thread.currentThread().getName()
                    + "售出一张票,剩余票数:" + --tickets);
        }
    }
}
class ThreadDemo1 {
    public static void main(String[] args){
        TestThread t = new TestThread();

        Thread t1 = new Thread(t);
        t1.setName("站点1");

        Thread t2 = new Thread(t);
        t2.setName("站点2");

        Thread t3 = new Thread(t);
        t3.setName("站点3");

        Thread t4 = new Thread(t);
        t4.setName("站点4");

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

输出:

站点1售出一张票,剩余票数:19
站点4售出一张票,剩余票数:16
站点3售出一张票,剩余票数:17
站点2售出一张票,剩余票数:18
站点2售出一张票,剩余票数:12
站点2售出一张票,剩余票数:11
站点2售出一张票,剩余票数:10
站点2售出一张票,剩余票数:9
站点2售出一张票,剩余票数:8
站点3售出一张票,剩余票数:13
站点3售出一张票,剩余票数:6
站点3售出一张票,剩余票数:5
站点3售出一张票,剩余票数:4
站点4售出一张票,剩余票数:14
站点4售出一张票,剩余票数:2
站点4售出一张票,剩余票数:1
站点1售出一张票,剩余票数:15
站点4售出一张票,剩余票数:0
站点3售出一张票,剩余票数:3
站点2售出一张票,剩余票数:7

4.2 线程是否启动

  • isAlive()
		System.out.println(t1.isAlive()); // false
        System.out.println(t2.isAlive());// false
        System.out.println(t3.isAlive());// false
        System.out.println(t4.isAlive());// false
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        System.out.println(t1.isAlive());// true
        System.out.println(t2.isAlive());// true
        System.out.println(t3.isAlive());// true
        System.out.println(t4.isAlive());// true

4.3 后台线程、setDaemon()

调用 start() 之前,调用 setDaemon(true) 方法,这个线程就变成了后台进程

class TestThread implements Runnable {
    private int tickets = 20;
    public void run(){
        while(true){
            if(tickets > 0)
                System.out.println(Thread.currentThread().getName()
                    + "售出一张票,剩余票数:" + --tickets);
        }
    }
}
class ThreadDemo1 {
    public static void main(String[] args){
        TestThread t = new TestThread();

        Thread t1 = new Thread(t);
        t1.setName("站点1");

        Thread t2 = new Thread(t);
        t2.setName("站点2");

        Thread t3 = new Thread(t);
        t3.setName("站点3");

        Thread t4 = new Thread(t);
        t4.setName("站点4");

        t1.setDaemon(true);//后台程序
        t2.setDaemon(true);
        t3.setDaemon(true);
        t4.setDaemon(true);

        System.out.println(t1.isAlive());
        System.out.println(t2.isAlive());
        System.out.println(t3.isAlive());
        System.out.println(t4.isAlive());
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        System.out.println(t1.isAlive());
        System.out.println(t2.isAlive());
        System.out.println(t3.isAlive());
        System.out.println(t4.isAlive());
    }
}

输出:

false
false
false
false
true
true
true
true
站点3售出一张票,剩余票数:19
站点3售出一张票,剩余票数:16
站点1售出一张票,剩余票数:17
站点2售出一张票,剩余票数:18
站点2售出一张票,剩余票数:12
站点1售出一张票,剩余票数:13
站点1售出一张票,剩余票数:10
进程已结束,退出代码0

结论:整个进程中,只有后台线程运行时,进程就会结束

4.4 线程的强制运行

class TestThread implements Runnable {
    public void run(){
        int i = 0;
        for(int x = 0; x < 10; ++x)
            System.out.println(Thread.currentThread().getName() + "-->" + i++);
    }
}
class ThreadDemo1 {
    public static void main(String[] args){
        TestThread t = new TestThread();
        Thread t1 = new Thread(t);

        t1.start();
        int i = 0;
        for(int x = 0; x < 10; ++x){
            if(i == 5){
                try{
                    t1.join(); // 运行完以后,才能运行后面的
                }
                catch(Exception e){
                    System.out.println(e.getMessage());
                }
            }
            System.out.println("main Thread" + i++);
        }
    }
}

输出:

以下总在 t1 线程 join 以后,全部运行完了,才开始运行 main,在此之前,main 等待
main Thread5
main Thread6
main Thread7
main Thread8
main Thread9

4.5 线程的休眠

		try {
            t1.sleep(800);
        }
        catch (InterruptedException e){

        }

4.6 线程的中断 interrupt()

class TestThread implements Runnable {
    public void run(){
        try{
            System.out.println("休眠10s");
            Thread.sleep(10000);
            System.out.println("run, 继续运行");
        }
        catch(InterruptedException e){
            System.out.println("run, 中断");
            return;
        }
        System.out.println("run, 休眠后继续");
        System.out.println("run, 正常结束");
    }
}
class ThreadDemo1 {
    public static void main(String[] args){
        TestThread t = new TestThread();
        Thread t1 = new Thread(t);

        t1.start();
        int i = 0;
        try {
            Thread.sleep(2000);
        }
        catch (InterruptedException e){

        }
        System.out.println("在main中, 中断t1");
        System.out.println(t1.isInterrupted());
        t1.interrupt();
        System.out.println(t1.isInterrupted());
        System.out.println("main 结束");
    }
}

输出:

休眠10s
在main中, 中断t1
false
true
run, 中断
main 结束

5. 多线程的同步

  • 问题引入
class TestThread implements Runnable {
    private int tickets = 20;

    public void run() {
        while (true) {
            if (tickets > 0) {
                try {
                    Thread.sleep(500);
                    // 某线程休息了,之前 tickets > 0
                    // CPU 给到别的线程 卖出去票了
                    // 休息完回来,可能没有票了,但是该线程已经进入了 if 块,
                    // 该线程又卖出一些(可能卖多了,超出实际)
                } catch (Exception e) {

                }
                System.out.println(Thread.currentThread().getName()
                        + "售出一张票,剩余票数:" + --tickets);
            }
        }
    }
}
class ThreadDemo1 {
    public static void main(String[] args){
        TestThread t = new TestThread();
        Thread t1 = new Thread(t);

        t1.setName("站点1");
        Thread t2 = new Thread(t);
        t2.setName("站点2");
        Thread t3 = new Thread(t);
        t3.setName("站点3");
        Thread t4 = new Thread(t);
        t4.setName("站点4");

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

输出:

站点3售出一张票,剩余票数:18
站点1售出一张票,剩余票数:18
站点2售出一张票,剩余票数:17
站点4售出一张票,剩余票数:19
站点1售出一张票,剩余票数:16
站点4售出一张票,剩余票数:14
站点2售出一张票,剩余票数:15
站点3售出一张票,剩余票数:13
站点1售出一张票,剩余票数:12
站点3售出一张票,剩余票数:10
站点4售出一张票,剩余票数:9
站点2售出一张票,剩余票数:11
站点3售出一张票,剩余票数:8
站点1售出一张票,剩余票数:8
站点2售出一张票,剩余票数:7
站点4售出一张票,剩余票数:6
站点1售出一张票,剩余票数:5
站点3售出一张票,剩余票数:4
站点2售出一张票,剩余票数:3
站点4售出一张票,剩余票数:2
站点3售出一张票,剩余票数:1
站点1售出一张票,剩余票数:1
站点4售出一张票,剩余票数:-1
站点2售出一张票,剩余票数:0
站点1售出一张票,剩余票数:-2
站点3售出一张票,剩余票数:-3

原因:资源数据访问不同步

5.1 同步代码块


某些代码,任何时候只能有一个线程在占用执行

只有该线程离开同步代码块后,其他线程才能进入同步代码块

class TestThread implements Runnable {
    private int tickets = 20;

    public void run() {
        while (true) {
            synchronized (this) { // 同步代码块
                if (tickets > 0) {
                    try {
                        Thread.sleep(50);
                        // 某线程休息了,之前 tickets > 0
                        // 别的线程 卖出去票了
                        // 休息完回来,可能没有票了,但是线程已经进入了 if 块
                    } catch (Exception e) {

                    }
                    System.out.println(Thread.currentThread().getName()
                            + "售出一张票,剩余票数:" + --tickets);
                }
            }
        }
    }
}
class ThreadDemo1 {
    public static void main(String[] args){
        TestThread t = new TestThread();
        Thread t1 = new Thread(t);

        t1.setName("站点1");
        Thread t2 = new Thread(t);
        t2.setName("站点2");
        Thread t3 = new Thread(t);
        t3.setName("站点3");
        Thread t4 = new Thread(t);
        t4.setName("站点4");

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

输出:

站点1售出一张票,剩余票数:19
站点1售出一张票,剩余票数:18
站点1售出一张票,剩余票数:17
站点1售出一张票,剩余票数:16
站点1售出一张票,剩余票数:15
站点1售出一张票,剩余票数:14
站点1售出一张票,剩余票数:13
站点1售出一张票,剩余票数:12
站点4售出一张票,剩余票数:11
站点4售出一张票,剩余票数:10
站点4售出一张票,剩余票数:9
站点4售出一张票,剩余票数:8
站点4售出一张票,剩余票数:7
站点4售出一张票,剩余票数:6
站点4售出一张票,剩余票数:5
站点4售出一张票,剩余票数:4
站点4售出一张票,剩余票数:3
站点4售出一张票,剩余票数:2
站点4售出一张票,剩余票数:1
站点4售出一张票,剩余票数:0

5.2 同步方法

在方法前面加上 synchronized

在这里插入图片描述

class TestThread implements Runnable {
    private int tickets = 20;

    public void run() {
        while (true) {
            sale();
        }
    }
    public synchronized void sale(){ // 同步方法
        if (tickets > 0) {
            try {
                Thread.sleep(100);
                // 某线程休息了,之前 tickets > 0
                // 别的线程 卖出去票了
                // 休息完回来,可能没有票了,但是线程已经进入了 if 块
            } catch (Exception e) {

            }
            System.out.println(Thread.currentThread().getName()
                    + "售出一张票,剩余票数:" + --tickets);
        }
    }
}
class ThreadDemo1 {
    public static void main(String[] args){
        TestThread t = new TestThread();
        Thread t1 = new Thread(t);

        t1.setName("站点1");
        Thread t2 = new Thread(t);
        t2.setName("站点2");
        Thread t3 = new Thread(t);
        t3.setName("站点3");
        Thread t4 = new Thread(t);
        t4.setName("站点4");

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

5.3 死锁

最常见的例子,线程1在对象A中(线程1持有锁),目前在等对象B解锁,线程2在对象B中,正在等待对象A解锁,这样,两个线程都无法进行下一步,称之死锁

如何避免:

  • 获取多个锁的时候,所有的线程中都以相同的顺序获取锁

死锁例子:

class A{
    synchronized void funA(B b){
        String name = Thread.currentThread().getName();
        System.out.println(name + "进入 funA");
        try{
            Thread.sleep(1000);
        }
        catch(Exception e){
            System.out.println(e.getMessage());
        }
        System.out.println(name + "调用B类的last()");
        b.last();
    }
    synchronized void last()
    {
        System.out.println("A 类的last()方法");
    }
}

class B{
    synchronized void funB(A a){
        String name = Thread.currentThread().getName();
        System.out.println(name + "进入 funB");
        try{
            Thread.sleep(1000);
        }
        catch(Exception e){
            System.out.println(e.getMessage());
        }
        System.out.println(name + "调用A类的last()");
        a.last();
    }
    synchronized void last()
    {
        System.out.println("B 类的last()方法");
    }
}

class DeadLockDemo implements Runnable{
    A a = new A();
    B b = new B();
    DeadLockDemo(){ // 构造函数
        Thread.currentThread().setName("Main->>Thread");
        new Thread(this).start();
        a.funA(b);
        System.out.println("main 线程运行结束");
    }
    public void run(){
        Thread.currentThread().setName("Test->>Thread");
        b.funB(a);
        System.out.println("其他线程运行完毕");
    }
    public static void main(String [] args){
        new DeadLockDemo();
    }
}

输出:

Main->>Thread进入 funA
Test->>Thread进入 funB
Main->>Thread调用B类的last()
Test->>Thread调用A类的last()

两个last() 都是同步的代码,两个线程各执一个锁,等待对方离开+解锁,僵持不下。

6. 线程间通信

在这里插入图片描述
例子:

class Producer implements Runnable{
    Pdata p = null;
    public Producer(Pdata p){
        this.p = p;
    }
    public void run(){
        int i = 0;
        while(true){
            // 往数据库里存放数据
            if(i == 0)
            {
                p.name = "张三";
                p.sex = "男";
            }
            else
            {
                p.name = "李四";
                p.sex = "女";
            }
            i = (i+1)%2;
        }
    }
}

class Consumer implements Runnable{
    Pdata p = null;
    public Consumer(Pdata p){
        this.p = p;
    }
    public void run(){
        while(true){
            // 从数据库里读取数据
            System.out.println("消费者读取数据:"+p.name+"-->"+p.sex);
        }
    }
}
class Pdata{
    String name;
    String sex;
}

class test9{
    public static void main(String[] args){
        Pdata p = new Pdata();
        new Thread(new Producer(p)).start();
        new Thread(new Consumer(p)).start();
    }
}

输出:

消费者读取数据:张三-->女
消费者读取数据:李四-->女
消费者读取数据:张三-->女
消费者读取数据:李四-->男
消费者读取数据:张三-->

两个线程共同操作一个 数据类,一个还没操作完,另一个就把数据取走了。

修改:

  • 在数据类中,增加两个同步方法,set()、get()
class Producer implements Runnable{
    Pdata p = null;
    public Producer(Pdata p){
        this.p = p;
    }
    public void run(){
        int i = 0;
        while(true){
            // 往数据库里存放数据
            if(i == 0)
            {
                p.set("张三", "男");
            }
            else
            {
                p.set("李四", "女");
            }
            i = (i+1)%2;
        }
    }
}

class Consumer implements Runnable{
    Pdata p = null;
    public Consumer(Pdata p){
        this.p = p;
    }
    public void run(){
        while(true){
            // 从数据库里读取数据
            p.get();
        }
    }
}
class Pdata{
    String name;
    String sex;
    public synchronized void set(String name, String sex){
        this.name = name;
        this.sex = sex;
    }
    public synchronized void get(){
        System.out.println("消费者读取数据:"+this.name+"-->"+this.sex);
    }
}

class test9{
    public static void main(String[] args){
        Pdata p = new Pdata();
        new Thread(new Producer(p)).start();
        new Thread(new Consumer(p)).start();
    }
}

输出:没有差错。

消费者读取数据:张三-->男
消费者读取数据:张三-->男
消费者读取数据:张三-->男
消费者读取数据:张三-->...
  • 上面又出现了一个问题:读取完了,还没有下一条数据,又读了一次。

解决上面问题:需要线程间通信

Java是通过 Object类 的 wait、 notify、 notifyall 这几个方法来实现线程间的通信的,又因为
所有的类都是从 Object 继承的,任何类都可以直接使用这些方法。

  • wait:告诉当前线程放弃监视器并进入睡眠状态,直到其他线程进入同一监视器并调用 notify
    为止
  • notify:唤醒同一对象监视器中调用 wait 的第1个线程。这类似排队买票,一个人买完之后,
    后面的人才可以继续买
  • notifyall:唤醒同一对象监视器中调用 wait 的所有线程,具有最高优先级的线程首先被唤醒
    并执行

修改:在数据类中定义一个新的成员变量 bFull 来表示数据存储空间的状态

  • 当 Consumer 线程取走数据后,bFull 值为 false, 当 Producer 线程放入数据后,bFull 值为 true
  • 只有 bFull 为 true 时, Consumer 线程才能取走数据,否则就必须等 Producer 放入数据
  • 只有 bFull 为 false 时,Producer 线程才能放入数据,否则就必须等 Consumer 取走数据
class Producer implements Runnable{
    Pdata p = null;
    public Producer(Pdata p){
        this.p = p;
    }
    public void run(){
        int i = 0;
        while(true){
            // 往数据库里存放数据
            if(i == 0)
            {
                p.set("张三", "男");
            }
            else
            {
                p.set("李四", "女");
            }
            i = (i+1)%2;
        }
    }
}

class Consumer implements Runnable{
    Pdata p = null;
    public Consumer(Pdata p){
        this.p = p;
    }
    public void run(){
        while(true){
            // 从数据库里读取数据
            p.get();
        }
    }
}
class Pdata{
    String name;
    String sex;
    boolean bFull = false;
    public synchronized void set(String name, String sex){
        if(bFull){
            try{
                wait(); // 写满了,后来写的线程要等待
            }
            catch (InterruptedException e)
            {}
        }
        notify(); // 可以写了,唤醒最先到达的线程
        this.name = name;
        this.sex = sex;
        this.bFull = true;
        try {
            Thread.sleep(1000);//让控制台输出慢一点
        }
        catch (Exception e){
                System.out.println(e.getMessage());
            }
    }
    public synchronized void get(){
        if(!bFull){
            try{
                wait();//还没写满,需要读取?等着
            }
            catch (InterruptedException e)
            {}
        }
        notify(); // 写满了,可以读取了,唤醒最先到达的线程
        System.out.println("消费者读取数据:"+this.name+"-->"+this.sex);
        this.bFull = false;
    }
}

class test9{
    public static void main(String[] args){
        Pdata p = new Pdata();
        new Thread(new Producer(p)).start();
        new Thread(new Consumer(p)).start();
    }
}

输出:(正常)

消费者读取数据:张三-->男
消费者读取数据:李四-->女
消费者读取数据:张三-->男
消费者读取数据:李四-->女
消费者读取数据:张三-->男
消费者读取数据:李四-->女
消费者读取数据:张三-->...

wait、 notify、 notifyall 这3个方法只能在 synchronized 方法中调用,即无论线程调用一个对
象的 wait 还是 notify 方法,该线程必须先得到该对象的锁标记

这样, notify就只能唤醒同一对象监视器中调用 wait 的线程。
而使用多个对象监视器,就可以分别有多个 wait、 notify 的情况,同组里的 wait 只能被同组的 notify 唤醒。

7. 线程生命周期控制


有些方法不推荐使用:

  • suspend()resume()stop() 方法
  • 前两个会导致死锁,前两个方法允许一个线程通过直接控制另外一个线程的代码来直接控制那个线程
  • stop 会停止正在操作数据的过程,会导致数据不完整

如何控制?看下面代码

class TestThread1 implements Runnable{
    private boolean flag = true;
    public void stopMe(){
        flag = false;
    }
    public void run(){
        while(flag){
            System.out.println(Thread.currentThread().getName()+"线程在运行");
        }
    }
}

class ThreadLifeDemo {
    public static void main(String[] args){
        TestThread1 t = new TestThread1();
        new Thread(t).start();
        for(int i = 0; i < 20; ++i){
            if(i == 5)
                t.stopMe();
            System.out.println("main线程在运行"+i);
        }
    }
}

输出:

main线程在运行0
Thread-0线程在运行
main线程在运行1
Thread-0线程在运行
Thread-0线程在运行
Thread-0线程在运行
Thread-0线程在运行
main线程在运行2
main线程在运行3
main线程在运行4
main线程在运行5
main线程在运行6
Thread-0线程在运行
main线程在运行7
main线程在运行8
main线程在运行9
main线程在运行10
main线程在运行11
main线程在运行12
main线程在运行13
main线程在运行14
main线程在运行15
main线程在运行16
main线程在运行17
main线程在运行18
main线程在运行19

进程已结束,退出代码为 0

解释: i = 5 的时候,通过将线程 run 方法里的循环条件破坏,run 结束,线程也就结束
当 i = 5 的时候,调用了 stopMe 后,CPU 不一定会马上切换到 Thread-0 线程上,计数器 i 还可能继续累加,之后 CPU 切换到 Thread-0 后,该线程才真正结束

结论:

  • 通过控制 run() 方法中循环条件的方式来结束一个线程,是实际中用的最多的方法
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页