多线程学习

25 篇文章 0 订阅
13 篇文章 0 订阅

进程和线程

进程:正在运行的软件

  • 独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位。
  • 动态性:进程的实质是程序的一次执行过程,进程是动态产生、动态消亡的。
  • 并发性:任何进程都可以同其他进程一起并发。

线程:是进程中的单个顺序控制流,是一个执行路径

  • 单线程:一个进程如果只有一条执行路径,则称为单线程程序。
  • 多线程:一个进程如果是有多条执行路径,则称为多线程程序。

线程

1、每一个线程都会有自己独立的栈内存空间
2、堆内存中的数据,是被多个线程所共享的

eg:多线程消费同一个商品,就可以将商品定义成共享资源,存放于堆内存中

线程和主线程区别

线程:是进程中执行任务的最小单元,负责使用资源在每一个进程(就是程序运行中)至少一条线程(主线程)我们也可以自己创建子线程帮助我们执行任务。
主线程:由系统程序自动创建,有且只有一个,我们创建的都是子线程

并发和并行

并行:在同一时刻,有多个指令在多个CPU上同时执行。(同一时刻,三道菜由三位厨师做,每人做一道)
并发:在同一时刻,有多个指令在单个CPU上交替执行。(统一时刻,三道菜由一个厨师做,一次做其中一道)

并发:指两个或者多个事件在同一个时间段内发生(并发:交替执行)
并行:指两个或者多个事件在同一时刻发生,同时发生(并行:同时执行)

创建线程的四种方式

1、继承Thread

步骤:

  • 创建一个类Thread01 ,继承Thread
  • 重写Thread类中的run()方法,具体逻辑代码在run()方法中实现
  • 创建Thread01 对象,并调用Thread01 父类中的start()方法开启线程
public class Thread01 extends Thread{
   private String name; // 线程名
   public Thread01(String name) {
       this.name = name;
   }
   // 重写Thread类中的run方法
   @Override
   public void run() {
       // 线程启动后所要执行的逻辑代码
       for (int i = 1; i <= 100; i++) {
           System.out.println(this.name + "开启线程 = " + i);
       }
   }
   
   public static void main(String[] args) {
       Thread01 t1 = new Thread01("t1");
       Thread01 t2 = new Thread01("t2");
       t1.start(); // 开启线程
       //t1.run(); // 只是调用了Thread01类中的run()方法,并未开启线程
       t2.start(); // 开启线程
       //t2.run(); // 只是调用了Thread01类中的run()方法,并未开启线程
   }
}

为什么要重写run()方法?

  • 因为run()方法是用来封装被线程所执行的代码的方法,只有重写了run()方法才能够实现具体要执行的逻辑代码

run()start()的区别:

  • run():封装线程所要执行的代码,直接调用相当于普通调用方法,并不会去开启线程
  • start():是启动线程的方法,然后由JVM调用此线程中的run()方法

Thread类本质是实现了【Runnable】接口的一个实例,代表一个线程的实例。
启动线程的唯一方法就是通过Thread类中的start()实例方法。
start()方法是一个native方法,他将启动一个新的线程,并执行run()方法。
通过自己的类直接extends Thread并重写run()方法,就可以启动线程并自行自己定义的run()方法。
优点:代码简单
缺点:该类无法继承别的类

2、实现Runnable接口

步骤:
创建一个类Runnable01 ,实现Runnable接口
实现Runnable接口中的run()方法,具体逻辑代码在run()方法中实现
创建Runnable01对象,把Runnable01对象当作参数传入到Thread对象中
创建Thread对象,并把Runnable01当作参数传入,调用Thread 类中的start()方法开启线程

public class Runnable01 implements Runnable{
   private String name;
   public Runnable01(String name){
       this.name = name;
   }

   @Override
   public void run() {
       // 线程启动后所要执行的逻辑代码
       for (int i = 0; i < 100; i++) {
           System.out.println(this.name + "线程启动了 = " + i);
       }
   }

   public static void main(String[] args) {
       Runnable01 r1 = new Runnable01("r1");
       Runnable01 r2 = new Runnable01("r2");
       Thread t1 = new Thread(r1);
       Thread t2 = new Thread(r2);
       t1.start();
       t2.start();
   }
}

Java中的类属于单继承,如果自己的类已经extends另一个类,就无法直接extends Thread,但是一个类继承另一个类的同时 可以实现多个接口
优点:继承其他类,统一实现该接口的实例可以共享资源
缺点:代码复杂

3、实现Callable接口

步骤:
创建一个类Callable01 ,实现Callable<T>接口
实现Callable<T>接口中的call()方法,具体逻辑代码在call()方法中实现,执行完毕后可以返回一个值
创建Callable01对象,把Callable01对象当作参数传入到FutureTask<T>对象中
创建FutureTask<T>对象,并把Callable01对象当作参数传入FutureTask<T>对象中
创建Thread对象,把FutureTask<T>当作参数传入,调用Thread 类中的start()方法开启线程
在通过调用FutureTask<T>对象中的get()方法获取线程结束后的返回值数据

public class Callable01 implements Callable<String> {
   private String name;
   public Callable01(String name){
       this.name = name;
   }
   @Override
   public String call() throws Exception {
       for (int i = 0; i < 100; i++) {
           System.out.println(this.name + "约会了 " + i + "次");
       }
       return "约会完毕了!";
   }

   public static void main(String[] args) throws Exception{
       Callable01 c1 = new Callable01("张三");
       FutureTask<String> f1 = new FutureTask<>(c1);
       Thread t1 = new Thread(f1);
       t1.start();
       String res = f1.get(); // get方法一定要在t1.start()之后
       System.out.println("运行结果:" + res);
   }
}

实现Runnable和实现Callable接口的方式基本相同,不过Callable接口中的call()方法有返回值,Runnable接口中的run()方法无返回值。

注意:FutureTask<T>get()方法一定要放在start()后,否则系统会出现“死等”现象,获取不到返回值

4、线程池方式

public class ExecutorPool {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.submit(() -> {
            System.out.println(Thread.currentThread().getName() + "正在执行...");
        });
        executorService.shutdown();

        ExecutorService service = Executors.newFixedThreadPool(3);
        service.submit(
            () -> {
                System.out.println(Thread.currentThread().getName() + "正在执行...");
        });
        service.shutdown();
    }
}

线程池是一个能容纳多个线程的容器,其中的线程可以重复使用,省去了频繁创建线程对象的操作,反复创建线程非常消耗资源。

优点:实现自动化装配,易于管理,循环利用资源。

runnable 和 callable 的区别

Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;

Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。

线程有哪些状态

线程通常都有五种状态,创建、就绪、运行、阻塞和死亡。

创建状态。在生成线程对象,并没有调用该对象的start方法,这是线程处于创建状态。

就绪状态。当调用了线程对象的start方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态。

运行状态。线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行run函数当中的代码。

阻塞状态。线程正在运行的时候,被暂停,通常是为了等待某个时间的发生(比如说某项资源就绪)之后再继续运行。sleep,suspend,wait等方法都可以导致线程阻塞。

死亡状态。如果一个线程的run方法执行结束或者调用stop方法后,该线程就会死亡。对于已经死亡的线程,无法再使用start方法令其进入就绪。

使用线程池有哪些好处

①提高系统的响应速度

②如果每次多线程操作都创建一个线程,会浪费时间和消耗系统资源,而线程池可以减少这些操作。

③可以对多个线程进行统一管理,统一调度,提高线程池的可管理性

线程池的状态有哪些

*RUNNING*:运行状态,接受新任务,持续处理任务队列里的任务。

*SHUTDOWN*:调用shutdown()方法会进入此状态,不再接受新任务,但要处理任务队列里的任务

*STOP*:调用shutdownNow()方法,不再接受新任务,不再处理任务队列里的任务,中断正在进行中的任务

*TIDYING*:表示线程池正在停止运作,中止所有任务,销毁所有工作线程。

*TERMINATED*:表示线程池已停止运作,所有工作线程已被销毁,所有任务已被清空或执行完毕。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值