学不懂多线程?代码加实例详解帮你深入理解多线程(2)

系列文章目录

进程调度的基本过程
学不懂多线程?代码加实例详解帮你深入理解多线程(1)


文章目录

  • 系列文章目录
  • 前言
  • 1. 线程的生命周期
  • 2. Runnable接口方式实现多线程
  • 3. 匿名内部类方式实现多线程
  • 4. 获取、修改线程对象的名字
    • 4.1 获取线程的名字
    • 4.2 获取当前线程的名字
    • 4.3 修改线程的名字
  • 5. 线程的休眠
    • 5.1 进入休眠
    • 5.2 终止线程休眠
  • 6. 终止线程执行
    • 6.1 强行终止线程执行
    • 6.2 合理地终止线程执行
  • 总结


前言

上一篇文章主要介绍了进程和线程的概念、单线程与多线程的执行方式以及实现多线程的一种方法,今天这篇文章继续介绍另一种实现多线程的方法,以及线程调度过程中可能会用到的几种方法;


1. 线程的生命周期

还记得上一篇文章末尾留个一个小问题吗? 输出结果为什么不是一个主栈一个支栈交替输出的呢?
学不懂多线程?代码加实例详解帮你深入理解多线程(2)_第1张图片
要解答这个问题,我们首先要了解多线程的生命周期,也就是多个线程从被创建到执行结束这个过程是怎么样的;

学不懂多线程?代码加实例详解帮你深入理解多线程(2)_第2张图片

一个进程在系统中的状态可以分为三种:就绪状态、运行状态和阻塞状态;

就绪状态:当start方法被调用时,进程进入就绪状态,标志着该进程可以抢夺控制台执行权(CPU时间片的执行时间),抢夺到执行权后即进入运行状态;

运行状态:run方法被调用后标志着这个进程进入运行状态,当抢夺到的时间片时间用完后,再次进入就绪状态抢夺时间,直到run方法运行结束,该进程生命周期结束;

阻塞状态:当一个进程遇到阻塞事件,例如用户键盘输入或sleep方法,会进入阻塞状态,并放弃时间片;

这也就可以解释为什么主线程和分支线程输出结果不是交替的了,这是因为它们抢夺到时间片的次数和时间的长短不一样;

2. Runnable接口方式实现多线程

首先,创建一个类实现Runnable接口;

class MyRunnable implements Runnable{
    @Override
    public void run(){
        for(int i=0;i<100;i++){
            System.out.println("这是一个分支栈——》"+i);
        }
    }
}

接着创建线程对象t;

 //创建一个可运行对象
        MyRunnable r = new MyRunnable();

然后再将可运行对象封装成一个线程对象;

 //将可运行对象封装成一个线程对象
        Thread t =new Thread(r);

最后启动线程;

//启动线程
        t.start();
        for(int i=0;i<100;i++){
            System.out.println("这是一个主栈——》"+i);
        }

完整代码如下:

public class myThread1 {
    public static void main(String[] args) {
        //创建一个可运行对象
        MyRunnable r = new MyRunnable();
        //将可运行对象封装成一个线程对象
        Thread t =new Thread(r);
        //启动线程
        t.start();
        for(int i=0;i<100;i++){
            System.out.println("这是一个主栈——》"+i);
        }
    }
}
class MyRunnable implements Runnable{
    @Override
    public void run(){
        for(int i=0;i<100;i++){
            System.out.println("这是一个分支栈——》"+i);
        }
    }
}

输出结果如下:

学不懂多线程?代码加实例详解帮你深入理解多线程(2)_第3张图片
两种实现多线程的方式,哪个更好?

第二种更好,第二种方式是面向接口编程,不影响继承其它类,更加灵活;

3. 匿名内部类方式实现多线程

首先,创建一个匿名内部类;

            Thread t= new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i=0;i<100;i++){
                    System.out.println("t线程——》"+i);
                }
            }
        });

启动线程

  //启动线程
        t.start();

此时分支栈开始运行,主栈继续向下运行;

   //启动线程
        t.start();
        for(int i=0;i<100;i++){
            System.out.println("main线程——》"+i);
        }

输出结果为:

学不懂多线程?代码加实例详解帮你深入理解多线程(2)_第4张图片

4. 获取、修改线程对象的名字

4.1 获取线程的名字

我们使用getName函数来获取线程对象的名字;

在上面的代码中插入getName函数;

public class myThread1 {
    public static void main(String[] args) {
        //创建一个可运行对象
        MyRunnable r = new MyRunnable();
        //将可运行对象封装成一个线程对象
        Thread t =new Thread(r);
        //获取线程对象的名字
        String tName= t.getName();
        //输出线程对象的名字
        System.out.println(tName);
        //启动线程
        t.start();
        for(int i=0;i<100;i++){
            System.out.println("这是一个主栈——》"+i);
        }
    }
}
class MyRunnable implements Runnable{
    @Override
    public void run(){
        for(int i=0;i<100;i++){
            System.out.println("这是一个分支栈——》"+i);
        }
    }
}

获取到线程对象 t 的名字为:

学不懂多线程?代码加实例详解帮你深入理解多线程(2)_第5张图片
for循环与‘是否能获取线程名字’无关;

4.2 获取当前线程的名字

获取当前线程的名字,我们使用currentThread方法;

Thread currentthread = Thread.currentThread();

将此语句插入main方法,即可获取主线程的名字;将此语句插入分支线程的方法中,即可获取分支线程的名字;

代码如下:

import java.util.Random;

public class testdemo {
    public static void main(String[] args) {

        System.out.println("当前线程的名字为:"+currentThread.getName());
        
        mythread.start();

    }
}
class myThread extends Thread{
        @Override
        public void run(){

        }
}

输出结果为:

学不懂多线程?代码加实例详解帮你深入理解多线程(2)_第6张图片
这里获取的是main线程的名字,并非实例化对象mythread的名字;

如果在run方法内部输出线程名字,会输出什么呢?

代码如下:

class myThread extends Thread{
        @Override
        public void run(){
            Thread currentThread =Thread.currentThread();
            System.out.println(currentThread.getName());
            }
}

然后在main方法中实例化线程对象,代码如下:

public class testdemo {
    public static void main(String[] args) {
        //实例化对象
        myThread thread0 = new myThread();
        myThread thread1 = new myThread();
        myThread thread2 = new myThread();
        
          thread0.start();
          thread1.start();
          thread2.start();
    }
}

输出结果为:

学不懂多线程?代码加实例详解帮你深入理解多线程(2)_第7张图片
由哪个实例化对象输出,就是哪个对象的名字;

4.3 修改线程的名字

修改线程名字,使用的语句是:对象名.setName(“修改后的线程名字”)

代码如下:

import java.util.Random;

public class testdemo {
    public static void main(String[] args) {
        //实例化对象
        myThread mythread = new myThread();
        //获取线程对象的名字
        System.out.println("修改前的名字:"+mythread.getName());
        //修改线程对象的名字
        mythread.setName("1234");
        //输出修改后的名字
        System.out.println("修改后的名字:"+mythread.getName());
        mythread.start();
    }
}
class myThread extends Thread{
        @Override
        public void run(){
        
        }
}

输出结果为:

学不懂多线程?代码加实例详解帮你深入理解多线程(2)_第8张图片

我们也可以试着修改一下主线程的名字,代码如下:

import java.util.Random;

public class testdemo {
    public static void main(String[] args) {
        //实例化对象
        myThread mythread = new myThread();
        Thread currentThread =Thread.currentThread();
        System.out.println("当前线程的名字为:"+currentThread.getName());
        //修改主线程的名字
        currentThread.setName("主线程");
        System.out.println("当前线程的名字为:"+currentThread.getName());
        //获取线程对象的名字
        System.out.println("修改前的名字:"+mythread.getName());
        //修改线程对象的名字
        mythread.setName("1234");
        //输出修改后的名字
        System.out.println("修改后的名字:"+mythread.getName());
        mythread.start();
    }
}
class myThread extends Thread{
        @Override
        public void run(){

            }
}

输出结果为:

学不懂多线程?代码加实例详解帮你深入理解多线程(2)_第9张图片

5. 线程的休眠

5.1 进入休眠

sleep方法可以使线程进入休眠,之后进入“阻塞状态”,放弃时间片占有;

在下面这段代码中,我们让线程休眠5秒后输出hello world;

public class myThread3 {
    public static void main(String[] args) throws InterruptedException {
        Thread.sleep(1000*5);
        System.out.println("hello world!");
    }
}

经过了5秒钟后,输出了helloworld;

在这里插入图片描述

5.2 终止线程休眠

如果一个线程休眠时间过长,我们想提前结束它的休眠,可以使用异常处理机制终止线程休眠;

代码如下:

public class MyThread5 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(new MyRunnable3());
        t.start();
        Thread.sleep(1000*5);
        t.interrupt();
    }
}

class MyRunnable3 implements Runnable{
    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName()+"--->开始休眠");
        try {
            Thread.sleep(1000*60*60*24);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"--->休眠结束");
    }
}

输出结果为:

学不懂多线程?代码加实例详解帮你深入理解多线程(2)_第10张图片

interrupt是利用try catch方法跳过线程休眠的,需要注意的是,使用interrupt加try catch方法会利用异常处理机制跳过 Thread.sleep(1000*60*60*24)语句,但并不影响后续代码的执行;

用代码验证一下:

public class myThread3 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(new MyRunnable1());
        t.start();
        Thread.sleep(1000*5);
        t.interrupt();
    }
}

class MyRunnable1 implements Runnable{
    @Override
    public void run(){
        for(int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"--->"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

输出结果为:

学不懂多线程?代码加实例详解帮你深入理解多线程(2)_第11张图片

6. 终止线程执行

6.1 强行终止线程执行

我们可以用stop方法终止线程执行,但是stop方法是强行终止线程执行,可能会造成数据丢失,非常的不安全,所以不建议使用,仅作了解即可;

代码如下:

public class myThread3 {
    public static void main(String[] args) throws InterruptedException {
        Thread t=new Thread(new MyRunnable1());
        t.start();
        Thread.sleep(1000*5);
        t.stop();
    }
}

class MyRunnable1 implements Runnable{
    @Override
    public void run(){
        for(int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName()+"--->"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

输出结果为:
学不懂多线程?代码加实例详解帮你深入理解多线程(2)_第12张图片

6.2 合理地终止线程执行

使用布尔标记法,可以更安全地结束一个线程;

代码如下:

public class MyThread4 {
    public static void main(String[] args) {
        MyRunnable2 m= new MyRunnable2();
        Thread t = new Thread(m);
        t.setName("t");
        t.start();
        try {
            Thread.sleep(1000*5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        m.a=false;
    }
}
class MyRunnable2 implements Runnable{
    boolean a=true;
    @Override
    public void run(){
            for(int i=0;i<10;i++){
                if(a){
                    System.out.println(Thread.currentThread().getName()+"--->"+i);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else{
                    return;
                }
            }
    }
}

输出结果如下:
学不懂多线程?代码加实例详解帮你深入理解多线程(2)_第13张图片


总结

这是我的多线程讲解系列文章之二,如果您觉得有帮助的话,不妨来个点赞收藏加关注啊,您的关注与支持是我继续创作地动力!

你可能感兴趣的:(操作系统,java,系统架构,系统安全,macos,windows)