多线程介绍

一、 什么是多线程?

	多线程是一种编程特性,它允许同时执行多个线程完成任务。线程是程序中一个执行路径,每个线程都可以同时运行代码,使得
			多核CPU的性能被榨干。
	生活上多线程是什么,一个办公室有很多的员工同时工作,每个人都处理工作,一个人正在写前端页面,一个人正在对接支付接口,还有一个人正在
	测试写好接口随便写着产品报告

什么是进程

	进程就是运行在操作系统上程序,也可以说是正在执行的实例,就是正在运行程序。比如QQ微信等软件

什么是线程

	线程算是进程的实例,它是程序中的执行路径,每一个JAVA程序至少有三个线程--main主线程,Gc回收机制的线程,异常处理的线程
当一个Java程序启动时,JVM会创建主线程,并且在该线程中调用程序的main()方法。
单线程
生活上来说:
		就是你有一个洗衣机,但是你有三批衣服需要清洗,
你不能直接把三批衣服直接放入洗衣机去洗,你只能一批一批的放入洗衣机去清洗,单线程简单来说只能做一件事情,完成这一件事情在去做下一件事情.
编程上来说:					
        什么是单程线程,规定一个程序或者一个应用只能执行一次任务,前面的任务没有完成不能继续完
成节接下来的任务,任务完成后,线程会进入等待状态,

单线程的特点是,被调用的方法执行完毕后当前方法才可能完成,前一个方法完成后才进行下一个方法。这是一种顺序调用。
偷我们老师的

多线程
生活上来说:
		在一个快餐店里,多名员工并行工作,以提高效率。例如,
一人负责点单,一人负责烹饪,另一人负责打包。这样,即使是多个客户同时到来,快餐店也能同时处
理多个订单,大大提高服务速度和效率。
编程上来说:
		一个网络服务器,要同时处理多个用户的请求的时候会使用多线程来处理,当用户请求到达了的时候,它就会开辟一个新线程来处理该请求,
这就允许服务器可以处理多个请求,

当程序同时用多个线程调用不同方法时,并不是像单线程一样的顺序调用,而是启动独立的线程之后立刻返回,各线程运行从run()方法开始,当run()结束后线程运行结束。
在这里插入图片描述
注意:真正的多线程指的是多CPU(即多核),如果只有单个CPU时,多线程是模拟实现的,在同一个时间点上,CPU只做一件事情。会产生二个非常的概念就是并行和并发

多线程的并行与并发

——————————————————

并行(parallel)是同一时间动手做(doing)多件事情的能力;**

从生活上来说

就拿做饭来举例 厨师只负责炒菜,杂工就负责切菜,服务员就负责上菜,他们几个互不干拢,在同一时间做了多件事情这就是并行
(想当于多核cpu同一时间应对多件事情)

从编程上

  网络服务器要同时处理多个用户的请求,当用户的请求发送到服务器的时候,服务器就生成一条线程专门为同一类型请求的     
用户进行处理,如果请求服务不一致则创建新一条线程
并发(concurrent)是同一时间应对(dealing with)多件事情的能力)

从生活上来说:

		你家只有一台洗衣机,但是你有好几堆衣服需要清洗,洗衣机只能塞下一堆,你等上一堆完成,再放下一堆,这就是并发必须等待上一件完成.
		(想当于1个cpu同一时间应对多件事情)

从编程上来讲

在软件开发中,一个典型的并发应用是一个网络服务器,它需要处理来自成百上千个客户端的并发请求。服
务器可能会采用多线程或异步编程技术来同时处理这些请求。
例如,一个线程可以处理数据库查询,同时另一个线程处理文件读写操作,而第三个线程处理网络数据的发送和接收。

线程创建

1、继承java.lang.Thread类
	// 继承 Thread 类
	public class Runner extends Thread() {
		// 重写 run 方法
	   @Override
	   public void run() {
        System.out.println("继承了Thread,重写了线程的run方法");
   }
} 

// 定义一个 Test 类进行测试
public class Test{
 	public static void main(String[] args){

     // 第二:创建线程对象(父类引用指向子类对象)
    // 也可以 Runner runner = new Runner();
   // Runner t = new Runner() ;
   Thread t=new Runner();
   
   // 第三:启动线程
   t.start();
 }
}

为什么这里使用的方法是Start 不是 run 方法呢,因为 Start方法被调用之后会创建新线程,然而run方法只是普通的方法调用罢了.

2、实现接口java.lang.Runnable
	public class Rummble implements Runnable() {
		 public void run(){
       System.out.println("继承了Thread,重写了线程的run方法");
    }	
}


public class Test() {
  public static void main(String[] args) {
  			// 创建Runner对象
			Rummble r = new Rummble();
			// 创建线程
			Thread thread = new Thread(r);
			// 调用线程
			thread.start();
	
	}
}

两种实现对比,实现Runnable接口有以下好处

1)适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码、数据有效的分离,较好地体现了面向对象的设计思想;

2)可以避免由于Java的单继承特性带来的局限;

3)有利于程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。当多个线程的执行代码(run方法体)来自同一个类的实例(实现Runnable接口的对象)时,即称它们共享相同代码。

4)Thread类实际上是Runnable接口的实现类

3 实现 callable 类接口
第一步:实现 Callable 接口 并重写call方法
// 指定泛型
// 自定为String
public class MyCallble implements Callable<String> {
   @verride
   public String call() {
        System.out.println("实现了Callable接口,重写了call方法,并返回了一个参数");
        return "程序梦爆款";
}
第二步:
第三:创建FutureTask对象
	 Future接口的唯一实现类
	 同时也实现了Runnable接口
	能够接收 Callable 类型的参数,用来处理有返回结果的情况
	get():获取值
	第四:创建线程对象
	第五:启动线程
public static void main(String ags[]) {
   // 创建 Callable 对象
        MyCallable myCallable = new MyCallable();
        // 创建 FutureTask 对象 这里的泛型必须是MyCallable 的泛型
        FutureTask<String> futureTask = new FutureTask<>(myCallable);
        // 创建线程
        Thread thread = new Thread(futureTask);

        // 启动线程
        thread.start();
        try {
            // 获取线程执行结果
            String result = futureTask.get();
            System.out.println(result);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
}

执行结果:
在这里插入图片描述

4. Callable接口与Runnable接口的对比
Callable接口与Runnable接口类似,实现Callable接口的类和实现Runnable接口的类都是可被其它线程执
行的任务。二者区别如下:
  1. Callable接口定义的方法是call 而 Runnable 接口的定义的方法是 run方法
  2. call方法可以抛出报错,而run方法不行
  3. Callable接口执行完任务后可以返回值因为运行Callable任务可拿到一个Future对象可以返回值,而Runnable接口做不到,
  4. Runnable 接口不支持泛型 而Callable 接口支持泛型
  5. Callable 接口返回值,需要借助Future对象的get方法

1. Future 接口
	Future接口是用来表示异步计算接口的接口,它提供一系列方法、用来检索计算是否完成、等待计算完成、
检索结果等功能,
2. FutureTask 实现类
	FutureTask是JAVA中一个类,它实现了Futute接口 和 Ruunable 接口,可以允许你在创建一个线程
并执行一个任务

主要特点:

  1. 异步执行任务:可以使用FutureTask在一个线程中执行一个耗时的任务,而不会阻塞主线程。
  2. 2.获取任务结果:可以在需要的时候获取任务的执行结果,通过get()方法获取,如果任务还没有执行完成,get()方法将会阻塞直到任务完成。
  3. 取消任务:可以通过cancel()方法取消任务的执行,取消之后,get()方法将会返回CancellationException。
  4. 判断任务状态:可以通过isDone()、isCancelled()等方法判断任务的执行状态。

线程池

  1. 什么是线程池

线程池就是一种管理和重复使用的机制,用来有效处理更多任务,它里面有事先创建好线程,使用这些线程重复处理不同的任务,不用去创建新的线程

  1. 好处是什么

好处:减少了线程创建和销毁的开销;提高了性能和资源利用率;更好地控制了并发度;提高线程的可管理性。


怎么使用线程池
1 .实现Callable接口+重写call()方法

// 实现了Callable接口
public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        return 1024;
    }
}

2.创建测试类,使用线程池执行线程任务


public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建线程池并创建10条线程
        ExecutorService exec = Executors.newFixedThreadPool(10);

        // 创建 Future 对象 提交任务给线程池并返回 Future 对象

		// 这里是使用了线程池里面的线程去执行这个任务的
        Future<Integer> future = exec.submit(new MyCallable());

        // 输出结果 通过 future对象的get方法拿到值
        System.out.println(future.get());

        // 关闭线程池
        exec.shutdown();

    }
}

3.得到输出结果
在这里插入图片描述

  1. 线程池API

  2. FixedThreadPool: 该线程池包含固定数量的线程,当有新的任务提交时,如果当前线程池中有空闲线程,则立即执行任务,否则任务将被放入队列中等待执行。

  3. CachedThreadPool: 该线程池会根据需要创建新的线程,但如果有空闲线程可用,则会重用它们。当线程处于空闲状态一段时间后,它们会被回收。

  4. SingleThreadPool: 只包含单个工作线程的线程池,所有任务按照先进先出的顺序执行。

  5. ScheduledThreadPool: 用于执行定时任务和周期性任务,可以指定延迟时间或固定的周期来执行任务。

线程API

  1. Start(); 启动线程方法

启动一个新的线程,在新的线程中运行run方法的代码;
不能直接调用run方法;
start方法只能调用一次;
start方法只是让线程进行就绪状态,不一定马上运行线程体,需要CPU分配时间片;

1.1 实现 Runnable 接口 并重写Run方法

public class My implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            System.out.println("子线程" + i);
        }
    }
}

1.2 创建测试类,使用start()方法

public class Main {
    public static void main(String[] args) {
        My my = new My();

        // 创建线程
        Thread thead = new Thread(my);

        thead.start();

        //主线程与子线程同时运行循环(并发)
        for (int i = 0; i < 100; i++) {
            System.out.println("主线程:" + i);
        }
    }
}

1.3 结果截图
在这里插入图片描述
会发现我们的子线程和主线程是交替执行,因为这就出现了并发这个问题,二个线程分别抢占CPU的时间片来回交替执行


未写完等待更新

  • 25
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值