学完这篇并发多线程还会觉得心虚?

1. 认识多线程*

  • 是软件或硬件的多个线程并发执行的技术。
  • 在硬件的支持下,提高计算机在同一时间执行多个线程的性能。

2. 前置基础概念

  • 并发:同一时刻,多个指令在单个CPU上交替执行
  • 并行:同一时刻,多个指令在多个CPU上同时执行

3. 进程和线程

  • 进程:正在执行的任务(软件)
    * 独立性:进程是独立运行的基本单位,为系统分配资源和调度的独立单位。
    * 动态性:程序的一次执行过程,进程是动态产生,动态消亡。程序=>编译=>打包=>部署…
    * 并发性:多个进程之间执行的任务是一起并发执行
  • 线程:是进程中的一条控制路径
    * 单线程:进程只有一条路径执行程序
    * 多线程:进程有多条路径执行程序

4. 多线程的实现方式

4.1 继承Thread类

两条线程效果图:
在这里插入图片描述

@Override
    public void run() {
        //模拟线程一
        for (int i = 0; i < 100; i++) {
            System.out.println("=========111======>"+i);
        }

        //模拟线程二
        for (int i = 0; i < 100; i++) {
            System.out.println("==========222======>"+i);
        }
    }
public static void main(String[] args) {
        //创建线程一
        MyThread t1 = new MyThread();
        //开启线程一
        t1.start();
        //创建线程二
        MyThread t2 = new MyThread();
        //开启线程二
        t2.start();
    }

思考:

  • 继承Thread类,为什么要重写run()方法?
    run()用来封装被线程执行的代码。
  • run()方法和start()方法有什么区别?
    start()开启线程由虚拟机调用线程的run()方法;run()没有开启线程,相当于普通方法的调用。

4.2 实现Runnable接口
两条线程效果图:
在这里插入图片描述

@Override
    public void run() {
        System.out.println("runnable.....");
        for (int i = 0; i < 100; i++) {
            System.out.println("=====111=====>"+i);
        }
    }
public static void main(String[] args) {
        //创建一个参数对象
        MyRunnable mr1 = new MyRunnable();
        //创建一个线程对象,并把参数传递给线程
        //线程启动后执行run方法
        Thread t1 = new Thread(mr1);
        t1.start();
        
        MyRunnable mr2 = new MyRunnable();
        Thread t2 = new Thread(mr2);
        t2.start();
    }

4.3 实现Callable接口
在这里插入图片描述
在这里插入图片描述

//Callable<Object>  泛型表示返回值的数据类型:字符串  integer  List集合  对象 ....
    @Override
    public String call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println("=====和心上的女孩表白====="+i);
        }
        //返回值表示线程运行完毕之后的结果
        return "愿意做女朋友";
    }
//线程开启后执行call方法
        MyCallable mc1 = new MyCallable();
        //可以获取线程执行完毕之后的结果,也可以作为参数传递给Thread对象
        FutureTask<String> ft1 = new FutureTask<>(mc1);
        //创建线程对象
        Thread t1 = new Thread(ft1);
        //开启线程
        t1.start();
        String s1 = ft1.get();
        System.out.println("=====女孩返回结果111=========="+s1);

        MyCallable mc2 = new MyCallable();
        FutureTask<String> ft2 = new FutureTask<>(mc2);
        Thread t2 = new Thread(ft2);
        t2.start();
        String s2 = ft2.get();
        System.out.println("======女孩返回结果222========>"+s2);

5. Thread方法中常用的API

获取线程名称:getName

 System.out.println("线程:"+getName()+"=========111======>"+i);

设置线程名称:setName 构造器

   	MyThread t1 = new MyThread();
       t1.setName("hsp001"); 

获取当前线程对象以及当前对象的名称:Thread.currentThread().getName();

System.out.println(Thread.currentThread().getName()+"=====111=====>"+i);

设置线程休眠:单位: ms Thread.sleep(3000);

@Override
    public void run() {
        System.out.println("runnable.....");
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(300);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"=====111=====>"+i);
        }
    }

守护线程:setDaemon(true);
线程调度:

  • 分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
  • 抢占式调度模型:优先让优先级高的线程使用CPU,如果优先级相同,就随机选择
    * getPriority(); //获取线程优先级
    * setPriority(1); //设置线程的优先级 优先级范围:1 – 10 默认值:5

6 线程的生命周期

在这里插入图片描述

7 线程安全的问题

案例引入

  • 需求:电影院售票系统(12306火车票),一共100张票,有3个窗口卖票,模拟卖票的过程

在这里插入图片描述
核心代码

//当前系统的总票数(张)
    private Integer ticketCount = 100;
    @Override
    public void run() {
        while (true){
            if (ticketCount == 0){
                //卖完啦
                //线程结束
                break;
            }else {
                //还有余票
                ticketCount--;
                System.out.println(Thread.currentThread().getName()+"正在卖票,余票"+ticketCount+"张");
            }
        }
    }

测试类

Ticket ticket = new Ticket();    //共享100张票的资源
        //创建三个线程 ====> 三个窗口
        Thread t1 = new Thread(ticket);
        Thread t2 = new Thread(ticket);
        Thread t3 = new Thread(ticket);

        t1.setName("窗口1");
        t2.setName("窗口2");
        t3.setName("窗口3");

        t1.start();
        t2.start();
        t3.start();

问题:

  • 实际的卖票过程中买完票后出票需要时间等待?
				try {
                   Thread.sleep(100); 
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

在这里插入图片描述
出现以上两个问题的原因?

  • 多个线程共享了操作数据

如何解决这两个问题呢?

  • 多条语句操作共享数据的代码给锁起来,在任意时刻只给一条线程执行。

在这里插入图片描述

//创建锁对象
    private Object obj = new Object();
    @Override
    public void run() {
        while (true){
            //同步代码块:给我们操作共享数据的代码锁上啦
            synchronized (obj) {
                if (ticketCount == 0) {
                    //卖完啦
                    //线程结束
                    break;
                } else {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //还有余票
                    ticketCount--;
                    System.out.println(Thread.currentThread().getName() + "正在卖票,余票" + ticketCount + "张");
                }
            }
        }
    }

如果同步代码块锁不唯一出现的现象?
在这里插入图片描述

核心代码

//当前系统的总票数(张)
    private static Integer ticketCount = 100;

    @Override
    public void run() {
        while (true){
            synchronized (this){     //当前线程对象
                if (ticketCount == 0) {
                    //卖完啦
                    //线程结束
                    break;
                } else {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //还有余票
                    ticketCount--;
                    System.out.println(Thread.currentThread().getName() + "正在卖票,余票" + ticketCount + "张");
                }
            }
        }

    }
//创建2个线程 ====> 2个窗口
        MyThreadTicket t1 = new MyThreadTicket();
        MyThreadTicket t2 = new MyThreadTicket();

        t1.setName("======窗口1=====>");
        t2.setName("======窗口2=====>");

        t1.start();
        t2.start();

在这里插入图片描述

  • 成员变量用staitc修饰,可以解决部分重复票问题。但是存在负数票问题。

在这里插入图片描述

  • synchronized锁对象用this,表示对象线程对象不唯一。

在这里插入图片描述
同步代码块和同步方法区别

  • 同步代码块可以锁住指定代码;同步方法可以锁住所有代码
  • 同步代码块可以指定锁对象;同步方法不能指定锁对象

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
核心代码

//当前系统的总票数(张)
    private Integer ticketCount = 100;

    @Override
    public void run() {
        while (true){
            if ("窗口1".equals(Thread.currentThread().getName())){
                //执行同步方法
                boolean result = synchronizedMethod();
                if (result){
                    break;
                }
            }
            if ("窗口2".equals(Thread.currentThread().getName())){
                //执行同步代码块
                synchronized (this){
                    if (ticketCount == 0){
                        break;
                    }else {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //还有余票
                        ticketCount--;
                        System.out.println(Thread.currentThread().getName() + "正在卖票,余票" + ticketCount + "张");
                    }
                }
            }

        }

    }

    private synchronized boolean synchronizedMethod() {
        if (ticketCount == 0){
            return true;
        }else {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //还有余票
            ticketCount--;
            System.out.println(Thread.currentThread().getName() + "正在卖票,余票" + ticketCount + "张");
            return false;
        }

    }

测试类

MyRunnableTicket mr = new MyRunnableTicket();
        Thread t1 = new Thread(mr);
        Thread t2 = new Thread(mr);
        t1.setName("窗口1");
        t2.setName("窗口2");
        t1.start();
        t2.start();

同步静态方法的锁对象

  • 类名.class // MyRunnableTicket.class

Lock锁

 //当前系统的总票数(张)
    private Integer ticketCount = 100;
    //创建自动锁对象
    //private Object obj = new Object();
    //创建Lock锁对象
    private ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            //同步代码块:给我们操作共享数据的代码锁上啦
            //synchronized (obj) {
            try {
                //加lock锁
                lock.lock();
                if (ticketCount == 0) {
                    //卖完啦
                    //线程结束
                    break;
                } else {
                    Thread.sleep(100);
                    //还有余票
                    ticketCount--;
                    System.out.println(Thread.currentThread().getName() + "正在卖票,余票" + ticketCount + "张");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                //释放锁
                lock.unlock();
            }
            //}
        }
    }

死锁

  • 多个线程互相持有对方所需要的资源,导致这些线程处于等待,无法执行。
    * 锁不能嵌套使用
Object obj1 = new Object();
        Object obj2 = new Object();

        new Thread(() -> {
            while (true) {
                synchronized (obj1) {
                    synchronized (obj2) {
                        System.out.println("======彤彤001正在通行====");
                    }
                }
            }
        }).start();

        new Thread(() -> {
            while (true){
                synchronized (obj2){
                    synchronized (obj1){
                        System.out.println("========彤彤002正在通行=======");
                    }
                }
            }
        }).start();

8 生产者-消费者模式

  • 生产者抢到CPU的执行权:
    * 首先判断有没有资源
    * 如果有等待消费者用,如果没有资源就生产
    * 把生成好的资源放到容器中
    * 叫醒消费者来用
  • 消费者抢到CPU的执行权:
    * 判断容器里有没有资源
    * 如果没有资源就休眠等待
    * 如果有就用
    * 叫醒等待的生产者来继续生产
    * 资源数量 减一

核心代码
共享资源(sharedRes)

//定义桌子的标记
    //true 表示桌子上有资源 允许消费者执行
    //false 表示桌子上没有资源 允许生产者执行
    public static boolean flag = false;

    //定义资源的总数量 count
    public static Integer count = 10;

    //锁对象 消费者 和 生产者用同一把锁
    public static final Object obj = new Object();

生产者

@Override
    public void run() {
        while (true){
            synchronized (sharedRes.obj){    //锁对象 和 消费者用同一把锁
                if (sharedRes.count == 0){
                    //有资源  不生产
                    break;
                }else {
                    //生产者继续做
                    if (!sharedRes.flag){
                        //需要生产
                        System.out.println("=====001正在给彤彤做=======");
                        sharedRes.flag = true;
                        sharedRes.obj.notifyAll();
                    }else {
                        //等待消费者
                        try {
                            sharedRes.obj.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

消费者

@Override
    public void run() {
/*
        * 方法:
        *     1. while()
        *     2. synchronized 锁对象
        */
        while (true){
            synchronized (sharedRes.obj){    //锁对象 和 生产者用同一把锁
                if (sharedRes.count == 0){
                    //没有资源啦 结束线程
                    break;
                }else {
                    if (sharedRes.flag){
                        //有资源
                        System.out.println("=====彤彤正在吃========");
                        sharedRes.flag = false;
                        sharedRes.obj.notifyAll();
                        sharedRes.count--;
                    }else {
                        //没有资源,等待生产者
                        try {
                            sharedRes.obj.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

测试类

		Consumers consumers = new Consumers();
        Producters producters = new Producters();

        consumers.start();
        producters.start();

生产者–消费者代码改写
共享资源(sharedRes)

//定义桌子的标记
    //true 表示桌子上有资源 允许消费者执行
    //false 表示桌子上没有资源 允许生产者执行
    //public static boolean flag = false;
    private boolean flag;

    //定义资源的总数量 count
    //public static Integer count = 10;
    //private Integer count =10;  //默认值 count = 0
    private Integer count;

    //锁对象 消费者 和 生产者用同一把锁
    //public static final Object obj = new Object();
    private final Object obj = new Object();
    //构造方法、get/set方法、toString()方法自己生成

消费者

private sharedRes sharedRes;

    public Consumers(sharedRes sharedRes) {
        this.sharedRes = sharedRes;
    }

    @Override
    public void run() {
        /*
        * 方法:
        *     1. while()
        *     2. synchronized 锁对象
        */
        while (true){
            synchronized (sharedRes.getObj()){    //锁对象 和 生产者用同一把锁
                System.out.println("===彤彤是消费者1111=======");
                if (sharedRes.getCount() == 0){    //sharedRes 对象的值 0   0.getCount()  NullPointerException
                    System.out.println("===彤彤是消费者222=======");
                    //没有资源啦 结束线程
                    break;
                }else {
                    System.out.println("===彤彤是消费者3333=======");
                    if (sharedRes.isFlag()){
                        //有资源
                        System.out.println("=====彤彤正在吃========");
                        sharedRes.setFlag(false);
                        sharedRes.getObj().notifyAll();
                        sharedRes.setCount(sharedRes.getCount() - 1);
                    }else {
                        //没有资源,等待生产者
                        try {
                            sharedRes.getObj().wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

生产者

private sharedRes sharedRes;

    public Producters(sharedRes sharedRes) {
        this.sharedRes = sharedRes;
    }

    @Override
    public void run() {
        while (true){
            synchronized (sharedRes.getObj()){    //锁对象 和 消费者用同一把锁
                System.out.println("======彤彤生产者1111=======");
                if (sharedRes.getCount() == 0){
                    System.out.println("======彤彤生产者2222=======");
                    //有资源  不生产
                    break;
                }else {
                    System.out.println("======彤彤生产者3333=======");
                    //生产者继续做
                    if (!sharedRes.isFlag()){
                        //需要生产
                        System.out.println("=====001正在给彤彤做=======");
                        sharedRes.setFlag(true);
                        sharedRes.getObj().notifyAll();
                    }else {
                        //等待消费者
                        try {
                            sharedRes.getObj().wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

测试类

		//创建共享资源对象
        sharedRes sharedRes = new sharedRes();
        //创建生产者和消费者线程
        Consumers consumers = new Consumers(sharedRes);
        Producters producters = new Producters(sharedRes);
        //开启线程
        consumers.start();
        producters.start();

效果图
在这里插入图片描述

以上内容是JavaSE的多线程初次相识,记录了我的学习路径,重点需要理解其中的概念和最后的生产者消费者模式。对此如果更好的理解欢迎在评论区留言!如果你在看就点击下方“在看”,点个“赞”,你会变好看!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Husp0707

你的小小点赞、关注是我的动力

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

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

打赏作者

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

抵扣说明:

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

余额充值