多线程的使用

文章探讨了多线程的原因,包括提升CPU利用率和并发性能,并展示了Java中通过继承Thread、实现Runnable和Callable创建线程的方法。此外,提到了多线程可能导致的内存泄漏、上下文切换和死锁问题,以及如何避免死锁。最后,文章介绍了线程池的状态,如ExecutorService的生命周期,并提供了自定义线程池的例子。
摘要由CSDN通过智能技术生成
  1. 为什么多线程

1)从计算机底层来说:线程运行比进程的负担要小,另外,多核CPU意味着多个线程可以同时运行,减少了线程上下文切换的开销。

2)从当代互联网发展趋势来说:利用好多线程可以大大提高系统整体的并发能力以及性能。

3)单核时代: 在单核时代多线程主要是为了提高 CPU 和 IO 设备的综合利⽤率。

4)多核时代: 多核时代多线程主要是为了提高 CPU 利⽤率。

  1. 多线程的创建

import java.util.concurrent.*;

/**
 * @packageName: PACKAGE_NAME
 * @user: lixi
 * @date: 2023/2/23 17:01
 * @email 1831309074@qq.com
 * @description: 创建多线程
 */
public class xiancheng {
//继承Thread
     static  class xiancheng1 extends Thread{
         @Override
         public void run() {
             System.out.println("继承Thread类");

         }
     }
//重写Runnable
     static class xiancheng2 implements Runnable{
         @Override
         public void run() {
             System.out.println("实现Runnable接口");

         }
     }
//实现Callable
    static class xiancheng3 implements Callable{
        @Override
        public Object call() throws Exception {
            System.out.println("实现Callable接口,有返回值");
            int sum = 0;
            for (int i = 0; i < 10; i++) {
                sum += i;
            }
            return sum;
        }
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        new xiancheng1().start();

        new Thread(new xiancheng2()).start();

        FutureTask<Integer> futureTask = new FutureTask<Integer>(new xiancheng3());
        new Thread(futureTask).start();
        Integer integer = futureTask.get();
        //等待1000再输出
        Thread.sleep(1000);
        System.out.println(integer);
        
//线程池
        ExecutorService executorService1 = Executors.newCachedThreadPool();
        executorService1.execute(()-> System.out.println(Thread.currentThread().getName()));
        ExecutorService executorService2 = Executors.newFixedThreadPool(3);
        executorService2.execute(()-> System.out.println(Thread.currentThread().getName()));
        ExecutorService executorService3 = Executors.newSingleThreadExecutor();
        executorService3.execute(()-> System.out.println(Thread.currentThread().getName()));
/**自定义线程池
参数1:Core Poll Size 核心线程数
参数2:Max Poll Size  最大线程数
参数3:Keep Alive Time存活时间
参数4:TimeUnit        存活时间的单位
参数5:WorkQueue       等待队列
参数6:Thread Factory  线程工厂
参数7:Handler         饱和策略
**/
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1,3,100,TimeUnit.SECONDS,new  LinkedBlockingDeque<Runnable>());
        executor.execute(()-> System.out.println(Thread.currentThread().getName()));
    }
}
  1. 使用多线程可能带来什么问题

内存泄漏、上下文切换、死锁

- 内存泄露:Thread Local

- 上下文切换:当前任务在执行完CPU时间片切换到另一个任务之前会先保存自己的状态,以便下次在切换回这个任务时,可以再加载这个任务的状态。任务从保存到再加载的过程就是一次上下文切换。Linux 相⽐与其他操作系统有很多的优点,其中有⼀项就是,其上下⽂切换和模式切换的时间消耗⾮常少。

- 死锁:

线程死锁描述的是,多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放

产生死锁必须具备的四个条件:

- 互斥条件:该资源任意一个时刻只由一个线程占用。

- 请求与保持条件:一个进程因请求资源而阻塞时,对已获取的资源保持不放。

- 不剥夺条件:线程已获得的资源在未使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。

- 循环等待条件:若干进程之间形成一种头尾相接的等待资源关系。

如何避免死锁:

- 破坏互斥条件:这个条件无法破坏,因为用锁本来就是想让它们互斥。

- 破坏请求与保持条件:一次性申请所有的资源。

- 破坏不剥夺条件:占用部分资源的线程,进一步申请其他资源时,如果申请不到,可以主动释放它当前占用的资源。

- 破坏循环等待条件:使用按序申请资源来预防。按某种顺序申请资源,释放资源则反序释放。

4.线程池的状态

- Running:线程池一旦创建,就处于Running状态,能够接受新任务;

- ShutDown:线程池调用shutdown接口的时候,进入该状态,这个状态下线程池不能接收新任务,但是能处理已经添加的任务

- Stop:线程池调用shutdownNow接口的时候,进入该状态,这个线程下线程池不接受新任务,不处理已经添加的任务,并会中断正在处理的任务

- Tidying:当线程在Stop状态下,线程池中执行的任务为空时,就处于该状态

- Terminated:当线程在Tidying状态下,执行完terminated()之后,就进入该状态

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值