关于多线程最全面的整理,简单易懂

本文全面介绍了Java中的多线程,包括进程与线程的概念、线程调度方式、创建线程的多种方法,以及线程安全问题的解决方案,如同步代码块、同步方法和显式锁。同时,探讨了生产者与消费者问题,并概述了Java四种线程池的使用和原理,旨在帮助读者深入理解并掌握多线程技术。
摘要由CSDN通过智能技术生成

多线程是面试绕不开的必问题目,今天系统的来让大家了解下多线程

一.多线程

首先明确一个概念,就是线程与进程的关系

1.进程

是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间

2.线程

是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少有一个线程线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程

线程调度

分时调度

所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
抢占式调度
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),
Java使用的为
抢占式调度。
CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻,
只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是 在同一时
刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的 使
用率更高。

3.创建线程的方式

1.继承Thread类

public class MyThread extends Thread{
   
    /**
     * run方法就是线程要执行的任务方法
     */
    @Override
    public void run() {
   
        for (int i = 0;i<10;i++){
   
            System.out.println("一起加油"+i);
        }
    }
}

主线程

public class Demo {
   
    public static void main(String[] args) {
   
        MyThread myThread = new MyThread();
        myThread.start();
        for (int i = 0;i<10;i++){
   
            System.out.println("一定成功"+i);
        }
    }
}

运行结果如下

在这里插入图片描述
由结果可知,其实他们是交替运行的,也就是作为的抢占式分配.

2.使用Runnable接口

在这里插入图片描述
在这里插入图片描述
运行结果如下
在这里插入图片描述
由此得出结论,也是抢占式的方法.请大家思考一个问题,相比于继承Thread相比有什么好处? 答案如下
1.通过创建任务,然后线程分配的方式实现的多线程,更适合多个线程同时执行相同任务
2.避免单继承所来的局限性,因为java只有单继承,所以当一个类继承了Thread他就不能继承其他类了,接口的多实现java可以.
3.任务与线程本身是分离的,提高了程序的健壮性
4.后续学习的线程池技术,接受Runnable的任务,不接受Thread类型的线程

3带返回值的线程Callable

之前的线程和主线程其实是并发的,但是Callable这种方式也可以实现一起走.也可以等结束,拿到结果

  1. 编写类实现Callable接口 , 实现call方法 class XXX implements Callable {
    @Override public call() throws Exception { return T; } }
    2. 创建FutureTask对象 , 并传入第一步编写的Callable类对象 FutureTask future = new FutureTask<>(callable);
    3. 通过Thread,启动线程 new Thread(future).start();

4举例子

public class Demo7 {
   
    public static void main(String[] args) throws ExecutionException, InterruptedException {
   
        Callable<Integer> c= new MyCallable();
        FutureTask<Integer> task = new FutureTask<>(c);
        new Thread(task).start();
        Integer j = task.get();
        System.out.println("返回值"+j);
        for (int i = 0;i<10;i++){
   
            try {
   
                Thread.sleep(1000);
            } catch (InterruptedException e) {
   
                e.printStackTrace();
            }
            System.out.println(i);
        }
    }
    static class MyCallable implements Callable<Integer> {
   

        @Override
        public Integer call() throws Exception {
   
            Thread.sleep(3000);
            for (int i = 0;i<10;i++){
   
                Thread.sleep(1000);
                System.out.println(i);
            }
            return 100;
        }
    }
}
这是一个启动的线程例子

二 线程安全问题

1.线程不安全经典例子

对于线程不安全问题,有一个非常经典的例子,就是卖票问题,
假设一共有10张票,我们开始卖票,假设3个窗口同时卖,意味着创建3个线程

public class Demo2 {
   
    //线程不安全问题
    public static void main(String[] args) {
   
        Runnable runnable = new Ticket();
        new Thread(runnable).start();
        new Thread(runnable).start();
        new Thread(runnable).start();
    }
    static class Ticket implements Runnable{
   
        private int count = 10;//总共十张票;
        @Override
        public void run() {
   
            while (count>0){
   
                System.out.println("正在准备卖票");
                try {
   
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }
                count--;
                System.out.println("出票成功,剩余的票数"+count);
            }
        }
    }
}

在这里插入图片描述
出现了问题,票数不能为负数

2 解决方案

1同步代码块

public class Demo2 {
   
    //线程不安全问题
    //解决方案,同步代码块,
    //synchronized方法解决
    public static void main(String[] args) {
   
        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值