多线程学习

本文介绍了多线程的概念,包括进程与线程的区别,以及并发和并行的差异。文章详细讲解了Java中实现线程的三种方式:继承Thread类、实现Runnable接口和使用Callable接口结合Future。还涵盖了线程的常用方法,如设置线程名、优先级,以及线程的生命周期。此外,讨论了线程池的使用和自定义线程池的方法,最后总结了学习多线程的重点内容。
摘要由CSDN通过智能技术生成

签名:但行好事,莫问前程。


前言

记录一下学习多线程的过程。


一、什么是多线程?

我们首先明确一下进程和线程的基本概念:

进程:进程是电脑运行的最小单位,打开电脑管理器,每个运行的软件都是一个进程。

在这里插入图片描述

线程:线程是操作系统中能够进行运行调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。

多线程:多线程就是一个进程在运行的过程中,为了充分发挥CPU的效率,多个线程并行交替执行的过程。

二、并发和并行

并发:在同一时刻,多个指令在单个CPU上交替执行。

并行:在同一时刻,多个指令在多个CPU上同时执行。

三、实现线程的三种方式

1.继承Thread类

  1. 创建一个类继承Thread类
public class MyThread extends Thread {
    
}
  1. 重写run方法
public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println("hello world - " + i);
        }
    }
}
  1. 创建子类对象,启动线程
public class ThreadDemo01 {

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }

}

代码结构以及运行效果:
在这里插入图片描述

2.实现Runnable接口

  1. 定义一个类实现Runnable接口
public class MyRunnable implements Runnable{

}
  1. 重写run方法
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println("hello world - " + i);
        }
    }
}
  1. 创建MyRunnable对象,多线程要执行的任务对象
public class ThreadDemo02 {
    public static void main(String[] args) {
        // 创建MyRunnable对象,多线程要执行的任务对象
        MyRunnable myRunnable = new MyRunnable();
    }
}
  1. 创建线程对象,将myRunnable对象作为参数传递到线程对象,启动线程
public class ThreadDemo02 {
    public static void main(String[] args) {
        // 创建MyRunnable对象,多线程要执行的任务对象
        MyRunnable myRunnable = new MyRunnable();
        // 创建线程对象,将myRunnable对象作为参数传递到线程对象
        Thread thread = new Thread(myRunnable);
        thread.start();
    }
}

代码结构以及运行效果:
在这里插入图片描述

3.利用Callable接口和Future接口

  1. 定义一个类实现Callable接口
public class MyCallable implements Callable<Boolean> {

}
  1. 重写call方法,有返回值
public class MyCallable implements Callable<Boolean> {
    /**
     * 重写call方法,有返回值
     * @return
     * @throws Exception
     */
    @Override
    public Boolean call() throws Exception {
        for (int i = 1; i <= 100; i++) {
            System.out.println("hello world - " + i);
        }
        return true;
    }
}
  1. 创建MyCallable对象(线程要执行的任务)
public class ThreadDemo03 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建MyCallable对象(线程要执行的任务)
        MyCallable myCallable = new MyCallable();
    }
}
  1. 创建FutureTask的对象(管理多线程运行的结果)
public class ThreadDemo03 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建MyCallable对象(线程要执行的任务)
        MyCallable myCallable = new MyCallable();
        // 创建FutureTask的对象(管理多线程运行的结果)
        FutureTask<Boolean> futureTask = new FutureTask<>(myCallable);     
    }
}
  1. 创建线程对象,启动线程
public class ThreadDemo03 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建MyCallable对象(线程要执行的任务)
        MyCallable myCallable = new MyCallable();
        // 创建FutureTask的对象(管理多线程运行的结果)
        FutureTask<Boolean> futureTask = new FutureTask<>(myCallable);
        // 创建线程对象
        Thread thread = new Thread(futureTask);
        // 启动线程
        thread.start();
    }
}
  1. 获取线程的执行结果
public class ThreadDemo03 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建MyCallable对象(线程要执行的任务)
        MyCallable myCallable = new MyCallable();
        // 创建FutureTask的对象(管理多线程运行的结果)
        FutureTask<Boolean> futureTask = new FutureTask<>(myCallable);
        // 创建线程对象
        Thread thread = new Thread(futureTask);
        // 启动线程
        thread.start();
        // 获取线程的执行结果
        System.out.println("线程的执行结果:" + futureTask.get());
    }
}

代码结构以及运行效果:
在这里插入图片描述
多线程三种实现方式的对比:
在这里插入图片描述

四、多线程的常用方法

在这里插入图片描述

1.返回线程名称

1.创建一个类实现Thread ,重写run方法,调用getName()获取线程名称

public class MyThreadName extends Thread {
    @Override
    public void run() {
        System.out.println("当前线程的名称是:" + getName());
    }
}

2.启动线程,查看线程名称

public class ThreadMethodDemo01 {
    public static void main(String[] args) {
        MyThreadName myThreadName = new MyThreadName();
        myThreadName.start();
    }
}

3.代码结构及结果:
在这里插入图片描述
默认线程名字的由来:
在这里插入图片描述

2.设置线程名字

1.用setName()方法设置线程名字
在这里插入图片描述

2.创建线程是直接指定名称
子类重写父类的两个构造方法:
在这里插入图片描述
在这里插入图片描述
3.直接通过构造方法,在创建线程对象的时候,赋值线程名称
在这里插入图片描述

3. 获取当前线程对象

public class ThreadMethodDemo01 {
    public static void main(String[] args) {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
    }
}

获取当前线程对象的名称:
在这里插入图片描述

4.让线程休眠指定时间(单位:毫秒)

在这里插入图片描述

5 设置线程优先级

线程的优先级:线程的优先级从1-10依次提高,没有设置的话默认优先级是5.

在这里插入图片描述
哪个线程设置的优先级比较高,他抢到CPU的概率就会比较大,但也不是绝对的。
在这里插入图片描述

6. 获取线程优先级

线程的默认优先级是:5,我们获取看一下:

在这里插入图片描述

7. 设置为守护线程

1.创建两个线程:一个打印10次线程名称,一个打印100次
在这里插入图片描述
2.将打印一百次的线程设置为守护线程
在这里插入图片描述
运行两个线程,观察结果:
在这里插入图片描述

8.礼让线程

// 出让当前CPU执行权
Thread.yield();

在这里插入图片描述
当线程执行到Thread.yield(); 出让当前CPU执行权
查看两个线程运行结果:
在这里插入图片描述
尽可能比较均匀的获取cup的执行权

9.插队线程

public class ThreadMethodDemo01 {
    public static void main(String[] args) throws InterruptedException {
        MyThreadName m1 = new MyThreadName("线程一");
        m1.start();
        // 把线程一(m1)插入到当前线程(maim线程之前)
        m1.join();
        // 执行在main线程
        for (int i = 0; i < 10; i++) {
            System.out.println("main线程:" + i);
        }
    }
}

在这里插入图片描述

五、线程的生命周期

在这里插入图片描述
1、新建(new)

新建:使用new方法,new出来线程,此时仅仅由JAVA虚拟机为其分配内存,并初始化成员变量的值。此时仅仅是个对象。

2、就绪(runnable)

就绪:就是调用的线程的start()方法后,这时候线程处于等待CPU分配资源阶段,谁先抢的CPU资源,谁开始执行;
该线程进入就绪状态,JAVA虚拟机会为其创建方法调用栈和程序计数器。线程的执行是由底层平台控制, 具有一定的随机性。

3、运行(running)

运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能;(当处于就绪状态的线程获得CPU,它就会执行run()方法)
对于一个单核cpu(或者是一个内核)来说,只能同时执行一条指令,而JVM通过快速切换线程执行指令来达到多线程的,真正处理器就能同时处理一条指令,只是这种切换速度很快,我们根本不会感知到。为了线程切换后能恢复到正确的执行位置,每条线程都有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储。
当一个线程开始运行后,它不可能一直持有CPU(除非该线程执行体非常短,瞬间就执行结束了)。所以,线程在执行过程中需要被中断,目的是让其它线程获得执行的CPU的机会。线程的调度细节取决于底层平台所采用的策略。

4、阻塞(blocked)

阻塞:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态。原因如下:
在这里插入图片描述
状态切换图如下:
在这里插入图片描述
5、销毁(terminated)

如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源。

1、run()/call()方法执行完成,线程正常结束;
2、线程抛出一个未捕获的Exception或Error;
3、直接调用线程的stop()方法结束该线程——该方法容易导致死锁,通常不建议使用。

六、线程池

1.创建线程池
2.提交任务
3.所有的任务全部执行完毕,关闭线程池

  • 创建一个没有上限的线程池
public class MyThreadPoolDemo {
    public static void main(String[] args) {
        // 创建一个没有上限的线程池
        ExecutorService executorService = Executors.newCachedThreadPool();
        // 提交任务
        executorService.submit(new MyRun());
        executorService.submit(new MyRun());
        executorService.submit(new MyRun());
        executorService.submit(new MyRun());
        executorService.submit(new MyRun());
        // 销毁线程池(一般的不销毁)
        executorService.shutdown();
    }
}

运行效果:
在这里插入图片描述

  • 创建一个有上限的线程池
public class MyThreadPoolDemo {
    public static void main(String[] args) {
        // 创建一个没有上限的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        // 提交任务
        executorService.submit(new MyRun());
        executorService.submit(new MyRun());
        executorService.submit(new MyRun());
        executorService.submit(new MyRun());
        executorService.submit(new MyRun());
        // 销毁线程池(一般的不销毁)
        executorService.shutdown();
    }
}

运行效果:
在这里插入图片描述

七、自定义线程池

在这里插入图片描述

以下便是我们自己定义的一个线程池,每个参数对应的意思也有标注

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

        /**
         * int corePoolSize,核心线程数量
         * int maximumPoolSize,最大线程数
         * long keepAliveTime,空闲线程最大存活时间
         * TimeUnit unit,空闲线程最大存活时间的单位
         * BlockingQueue<Runnable> workQueue,任务队列
         * ThreadFactory threadFactory,创建线程工厂
         * RejectedExecutionHandler handler 任务的拒绝策略
         */
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                3,
                6,
                60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
    }
}

总结

博客主要记录了多线程的学习过程,有啥错误或不足地方请指正,如果对你有所帮助,请一键三连。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值