线程的基本概念 – 实现多线程的四种基本方式

时间:2020-9-17 作者:admin

文章目录

线程概述

利用多线程技术可以使系统同时运行多个程序块,缩短出程序响应的时间,提高计算机资源的利用率,达到多任务处理的目的。

进程和线程

  • 进程是程序的一次动态执行过程,每个进程都有自己独立的内存空间。一个应用程序可以同时启动多个进程(比如浏览器可以开多个窗口,每个窗口就是一个进程)

  • 多进程操作系统能够运行多个进程,每个进程都能够循环利用所需要的CPU时间片,使的所有进程看上去像在同时运行一样。

  • 线程是进程的一个执行流程,一个进程可以由多个线程组成,也就是一个进程可以同时运行多个不同的线程,每个线程完成不同的任务。

  • 线程的并发运行:就是一个进程内若干个线程同时运行。(比如:word的拼写检查功能和首字母自动大写功能是word进程中的线程)

  • 线程进程的关系是一个局部整体的关系,每个进程都由操作系统分配独立的内存地址空间,而同一进程的所有线程都在同一地址空间工作。
    线程的基本概念 - 实现多线程的四种基本方式

线程的生命周期

一个线程的完整生命周期要经历5中状态:新建、就绪、运行、阻塞、死亡
线程的基本概念 - 实现多线程的四种基本方式
线程的基本概念 - 实现多线程的四种基本方式

  • 新建状态:使用new和某种线程的构造方法来创建线程对象,该线程就会进入新建状态,系统为该线程对象分配内存空间。处于新建状态的线程可以通过调用**start()**方法进入就绪状态。
  • 就绪状态:此时线程已经具备了运行的条件,进入了线程队列,等待系统分配CPU资源,一旦获得CPU资源,该线程就会进入运行状态。
  • 运行状态:进入运行在状态,线程会执行自己的**run()**方法中的代码。
  • 阻塞状态:一个正在执行的线程,如果执行了suspend、join或sleep方法,或等待io设备的使用权,那么该线程将会让出自己的CUP控制权并暂时中止自己的执行,进入阻塞状态。阻塞的线程,不能够进入就绪队列,只有当阻塞原因被消除的时候,线程才能进入就绪状态,重新进入线程队列中排队等待CPU资源,然后继续执行。
  • 死亡状态:一个线程完成了全部工作或者被提前强制性的中止,该线程就处于死亡状态。

线程常用方法归纳

方法名 描述
Thread(Runnable target) 利用Runnable接口子类对象实例化Thread对象
Thread(Runnable target,String name) 利用Runnable接口子类实例化Tread对象,并指定线程名
Thread(String name) 实例化Thread对象,指定线程名
Thread currentThread() 返回当前正在执行的线程
String getName() 返回线程名
int getPriority() 返回线程优先级
boolean isInterrupter() 判断线程是否中断,线程中断返回true,否则返回false
boolean isAlive() 判断线程是否活动,是返回true,否则返回false
final void join() 强制线程运行(有些任务需要紧急运行)
void run() 执行线程,线程执行的主要任务都写里面
void sleep() 使正在执行的线程暂时休眠(单位 / 毫秒),其他线程继续执行
interrupt() 强制终端线程运行,该线程后面的方法不继续执行
void yield() 将目前正在执行等的线程暂停,运行其他线程执行(让给其他线程执行,然后再恢复该线程)
setName() 设置线程名
setPriority(int new) 设置线程优先级,MIN_PRIORITY(常量值1)最低优先级,NORM_PRIORITY(值5)中等优先级(线程的默认优先级),MAX_PRIORITY(值10) 最高优先级
void start() 启动线程,进入就绪队列,等待cpu资源就可以进入运行状态
finalv void setDaemon(boolean on) 将一个线程设置成后台运行

多线程的实现方法

案例

1. 继承Thread类,重写run方法来实现多线程

public class MyThread extends Thread{

    private String name;//线程名

    public MyThread() {
    }
    public MyThread(String name) {
        this.name = name;
    }
    
    //完成线程功能的主体代码都在run()方法中
    @Override
    public void run() {
        for (int i = 0; i < 3; i++){
            System.out.println(name + "执行功能" + i);
        }
    }
}
public class ThreadDemo {
    public static void main(String[] args) {
        //定义线程
        MyThread thread1 = new MyThread("线程A");
        MyThread thread2 = new MyThread("线程B");
        MyThread thread3 = new MyThread("线程C");
        MyThread thread4 = new MyThread("线程D");
        MyThread thread5 = new MyThread("线程E");
        MyThread thread6 = new MyThread("线程F");

        //调用start()方法启动线程,让其进入就绪状态,等待系统分配CPU资源进入运行状态调用run()方法
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
        thread5.start();
        thread6.start();
    }
}

运行结果:(基于4核计算机)
运行三个线程(顺序执行)
线程的基本概念 - 实现多线程的四种基本方式
运行6个线程(交互执行)
线程的基本概念 - 实现多线程的四种基本方式

2.继承Runnable接口实现多线程

  重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target

class MyRunnable implements Runnable {

    @Override
    public void run() {
        for(int i = 0; i < 3; i++){
            System.out.println(Thread.currentThread().getName()+"运行功能"+i);
        }
    }
}

public class RunnableDemo {
    public static void main(String[] args) {
        //定义线程
        MyRunnable mt1 = new MyRunnable();
        MyRunnable mt2 = new MyRunnable();
        MyRunnable mt3 = new MyRunnable();
        MyRunnable mt4 = new MyRunnable();
        MyRunnable mt5 = new MyRunnable();
        MyRunnable mt6 = new MyRunnable();

        //利用Runnable接口子类对象来实例化Thread对象
        Thread thrad1 = new Thread(mt1,"线程1");
        Thread thrad2 = new Thread(mt2,"线程2");
        Thread thrad3 = new Thread(mt3,"线程3");
        Thread thrad4 = new Thread(mt4,"线程4");
        Thread thrad5 = new Thread(mt5,"线程5");
        Thread thrad6 = new Thread(mt6,"线程6");

        //启动线程
        thrad1.start();
        thrad2.start();
        thrad3.start();
        thrad4.start();
        thrad5.start();
        thrad6.start();
    }
}

运行结果:
线程的基本概念 - 实现多线程的四种基本方式

继承Thread类和实现Runnable接口完成多线程的区别

  • 实现Runnable接口可以做到资源共享,而继承Thread类不行(比如购票问题)
  • 实现Runnable接口相比继承Thread类来说,具有两大优势:避免单继承带来的局限和可以共享资源等优势
  • 相比Thread类的继承,一般采用实现Runnable接口来实现多线程

3. 通过Callable 和 FutureTask创建线程

3.1 创建Callable接口的实现类 ,实现它的Call方法
3.2 使用FutureTask类来包装Callable对象,这个FutureTask对象需要封装Callable对象的Call方法的返回值
3.3 使用FutureTask对象作为Thread对象的target创建并调用start方法启动线程


//1. 创建Callable接口的实现类 ,实现它的Call方法
class MyCallable<T> implements Callable<T>{

    //重写Callable的call方法
    @Override
    public T call() throws Exception {
        System.out.println(Thread.currentThread().getName() + "   ---->通过实现Callable接口来实现线程");
        return null;
    }
}


public class Callable_FutureTask {

    public static void main(String[] args) {

        //2. 实例化Callable对象
        Callable<Object> callable = new MyCallable<Object>();
        //3. 使用FutureTask类来包装Callable对象
        FutureTask<Object> futureTask = new FutureTask<Object>(callable);
        //使用FutureTask对象作为Thread对象的target创建并调用start方法启动线程
        Thread thread1 = new Thread((futureTask),"线程A");

        System.out.println("当前运行线程名:" + Thread.currentThread().getName());
        //启动线程
        thread1.start();
    }

}

运行结果:

线程的基本概念 - 实现多线程的四种基本方式

4. 通过线程池实现多线程

class MyRunnable implements Runnable
{
    @Override
    public void run()
    {
        System.out.println("通过线程池的方式创建的线程,线程名 :" + Thread.currentThread().getName());

    }
}

public class ThreadPool {

    //设置线程池的数量
    private static int threadPoolNum = 8;


    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for(int i = 0; i < threadPoolNum; i ++)
        {
            //创建线程对象
            MyRunnable thread = new MyRunnable();

            //Thread.sleep(1000);//使线程休眠1秒
            executorService.execute(thread);
        }
    }
}

运行结果:
线程的基本概念 - 实现多线程的四种基本方式

关于线程注意事项

  1. java中,所有线程都是同时启动的,哪个线程占有CPU等运行资源,哪个线程就可以运行。
  2. Java程序每次运行都需要启动两个线程(main线程和垃圾收集器线程)
  3. Java线程运行过程中,其他线程并不会随着主线程的结束而结束。
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。