多线程入门(概念、api等等相关基础)

关于多线程的名词

进程: 每个进程都有独立的代码和数据空间,进程间的切换会有较大的开销,一个进程包含1–n个线程

线程: 同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小。


并发: 并发是指一个线程或者是计算机的内核同时执行多个任务。
举例:一个人种一棵树需要5天, 五个人(线程)种一棵树需要一天

并行: 并行是指任务在同一时间运行,每个内核同一时间内只执行一个任务。
举例:一个人种一棵树需要5天, 两个人种两棵树需要10天


线程优先级: java程序属于抢占式调度,优先让优先级高的线程使用cpu,如果线程的优先级相同的,那么随机选择一个线程执行


注意:

  1. 多线程程序是乱序执行。因此,只有乱序执行的代码才有必要设计为多线程。
  2. 对象.run()可以执行方法,但是不是使用的多线程的方式,就是一个普通的方法,要想实现多线程的方式,一定需要通过对象.start()方法。

继承Thread类和实现Runnable接口的区别:

在程序开发中只要是多线程肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下好处:

  1. 避免点继承的局限,一个类可以实现多个接口
  2. Thread类也是Runnable接口的子类
  3. 增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
  4. 线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类

线程的状态 生命周期

在这里插入图片描述

  1. 新建状态(New):新创建了一个线程对象。
  2. 就绪状态(Runnable):一旦调用了Start()方法 那么线程就处与可以运行的状态,等待获取CPU的使用权。
  3. 运行状态(Running):就绪状态的线程获取了CPU执行权力,开始正真的执行Run方法中的代码。
  4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
    1. 等待阻塞:运行的线程调用wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
    2. 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
    3. 其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。wait和sleep的区别 一个是放弃锁并进入等待,一个是不放弃锁,时间到了就开始执行。
  5. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。

多线程的实现方式(三种)

  1. 继承Thread类(不常用)、

  2. 实现Runnable接口(常用)、

  3. 实现Callable接口通过FutureTask包装器来创建Thread线程、

  4. 使用ExecutorService、Callable、Future实现有返回结果的多线程。

    其中前两种方式线程执行完后都没有返回值,后两种是带返回值的。

    线程池内容很多,以后单独写一篇;

继承Thread


import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;

/**
 * explain:继承Thread类的方式来实现多线程
 *
 * @author Hope
 * @date 2022/4/5
 * @see #start()
 * @see #run()
 */

@AllArgsConstructor
@NoArgsConstructor
public class ThreadRunTest extends Thread {

    private String threadName;

    @lombok.SneakyThrows
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(threadName + "运行: " + i);

            // Thread.sleep()方法调用目的是突出线程抢夺CPU资源,线程是交替执行。
            sleep(100);
        }
    }


    public static void main(String[] args) {
        ThreadRunTest startA = new ThreadRunTest("线程A");
        ThreadRunTest startB = new ThreadRunTest("线程B");

        startA.start();
        startB.start();

        // 从程序运行的结果可以发现,多线程程序是乱序执行。因此,只有乱序执行的代码才有必要设计为多线程。

    }
}

实现Runnable


/**
 * explain: 实现Runnable接口来实现多线程
 *
 * @author Hope
 * @date 2022/4/5
 * @see java.lang.Runnable
 */

@AllArgsConstructor
@NoArgsConstructor
public class RunnableRunTest implements Runnable {

    private String threadName;

    @SneakyThrows
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(threadName + "运行: " + i);

            // Thread.sleep()方法调用目的是突出线程抢夺CPU资源,线程是交替执行。
            sleep(100);
        }
    }


    public static void main(String[] args) {
        RunnableRunTest startA = new RunnableRunTest("线程A");
        RunnableRunTest startB = new RunnableRunTest("线程B");
        
        new Thread(startA).start();
        new Thread(startB).start();

        // run()方法是多线程程序的一个约定。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。
        // 在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,
        // 然后调用Thread对象的start()方法来运行多线程代码。实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。

    }
}


实现Callable

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

import static java.lang.Thread.sleep;

/**
 * explain:采用实现Callable接口实现多线程
 * 采用实现Callable接口实现多线程启动方式和以往两种的方式不太一样,
 * 下面就看一下怎样启动采用实现Callable接口的线程,首先我 new 一个我的实现实例,然后将我生成的实例对象注册进入到
 * FutureTask类中,然后将FutureTask类的实例注册进入Thread中运行。最后可以采用FutureTask<V>中的get方法获取自定义线程的返回值
 * <p>
 * <p>
 * 为什么实现Callable接口实现的多线程的形式需要采用这样的方式去启动线程。
 * Callable接口的源码,其中只有一个call方法的定义,
 * FutureTask<V>类其中我们可以看到FutureTask<V>类是实现了RunnableFuture<V>接口的,
 * 我们再看FutureTask<V>类其中的一个构造方法如下,其中需要传入一个Callable<V>类型的参数
 *
 * @author Hope
 * @date 2022/4/5
 * @see FutureTask
 * @see Runnable
 * @see Thread
 */

@AllArgsConstructor
@NoArgsConstructor
public class CallableRunTest implements Callable {

    private String threadName;

    @Override
    public Object call() throws Exception {

        int age = 0;
        for (int i = 0; i < 5; i++) {
            System.out.println(threadName + "运行: " + i);
            age++;

            // Thread.sleep()方法调用目的是突出线程抢夺CPU资源,线程是交替执行。
            sleep(100);
        }
        return threadName + "打印次数:" + age;
    }


    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CallableRunTest startA = new CallableRunTest("线程A");
        CallableRunTest startB = new CallableRunTest("线程B");


        FutureTask futureTaskA = new FutureTask(startA);
        FutureTask futureTaskB = new FutureTask(startB);


        new Thread(futureTaskA).start();
        new Thread(futureTaskB).start();

        System.out.println(futureTaskA.get());
        System.out.println(futureTaskB.get());


    }
}

Thread-Api


/**
 * explain:Thread类中的方法
 *
 * @author Hope
 * @date 2022/4/6
 */
public class ThreadApiTest {
    @SneakyThrows
    public static void main(String[] args) {
        Thread thread = new Thread(() -> System.out.println("线程执行中!"));

        //void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
        // 多次启动一个线程是非法的:一个线程对象只能调用一次start方法。特别是当线程已经结束执行后,不能再重新启动。
        thread.start();

        //void setName(String name)->设置线程名字
        thread.setName("线程001");

        // static void sleep(long millis)->让当前线程睡眠 millis:设置的是线程休眠的时间(毫秒值)
        Thread.sleep(100);

        //String getName():获取线程名字
        System.out.println(thread.getName());

        //String toString() 返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
        System.out.println(thread.toString());

        //模拟线程在处理数据 等待3秒
        Thread.sleep(3000);
        //void join() 等待该线程终止, 在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。
        thread.join();


        //static Thread currentThread()->获取当前正在运行的线程对象,在哪个线程中调用,获取的就是哪个线程对象
        Thread currentThread = Thread.currentThread();
        System.out.println(currentThread.getName());
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

香辣奥利奥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值