首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【day18】多线程高级应用

【day18】多线程高级应用

作者头像
程序员波特
发布2024-12-26 08:23:07
发布2024-12-26 08:23:07
2470
举报
文章被收录于专栏:魔法书魔法书

day17回顾

在深入探讨模块18之前,让我们回顾一下【day17】中的关键内容:

  1. 创建多线程
    • 继承Thread类:
      • 定义一个类,继承Thread
      • 重写run方法,设置线程任务。
      • 创建自定义线程对象。
      • 调用start方法,开启线程,JVM自动执行run方法。
    • 实现Runnable接口:
      • 定义一个类,实现Runnable
      • 重写run方法,设置线程任务。
      • 创建自定义线程对象,传递到Thread对象中。
      • 调用start方法,开启线程,JVM自动调用run方法。
    • 匿名内部类形式创建:
      • new Thread(new Runnable(){...}).start();
  2. Thread中的方法
    • start():开启线程,JVM自动调用run方法。
    • getName():获取线程名字。
    • setName(String name):设置线程名字。
    • currentThread():获取当前正在执行的线程对象。
    • sleep(long time):线程睡眠。
    • setPriority(int n):设置线程优先级。
    • getPriority():获取线程优先级。
    • setDaemon(true):设置为守护线程。
    • yield():礼让线程。
    • join():插队线程。
  3. 线程安全
    • 同步代码块:
      • synchronized(锁对象){...}
    • 同步方法:在定义方法的时候加上synchronized关键字。
      • 非静态:默认锁this
      • 静态的:默认锁class对象。

模块17重点

本模块将深入探讨多线程的高级应用,包括:

  1. 使用waitnotify两个方法。
  2. 使用Lock锁对象。
  3. 利用Callable接口实现多线程。
  4. 使用线程池完成多线程。

第一章:等待唤醒机制

1.等待唤醒案例分析(线程之间的通信)

在生产者-消费者模型中,我们需要协调线程之间的通信,确保生产和消费的同步进行。这就需要用到等待唤醒机制。

  • wait():线程等待,等待的过程中线程会释放锁,需要被其他线程调用notify方法将其唤醒,重新抢锁执行。
  • notify():线程唤醒,一次唤醒一个等待线程;如果有多条线程等待,则随机唤醒一条等待线程。
  • notifyAll():唤醒所有等待线程。

waitnotify方法需要锁对象调用,所以需要用到同步代码块中,而且必须是同一个锁对象。

2.等待唤醒案例实现

以下是生产者-消费者模型的实现代码:

代码语言:javascript
复制
/*
   count和flag可以定义成包装类
   但是要记得给count和flag手动赋值
   不然对于本案例来说,容易出现空指针异常
 */
public class BaoZiPu {
    //代表包子的count
    private int count;
    //代表是否有包子的flag
    private boolean flag;

    public BaoZiPu() {
    }

    public BaoZiPu(int count, boolean flag) {
        this.count = count;
        this.flag = flag;
    }

    /*
       getCount 改造成消费包子方法
       直接输出count
     */
    public void getCount() {
        System.out.println("消费了..............第"+count+"个包子");
    }

    /*
       setCount 改造成生产包子
       count++
     */
    public void setCount() {
        count++;
        System.out.println("生产了...第"+count+"个包子");
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}
代码语言:javascript
复制
public class Product implements Runnable{
    private BaoZiPu baoZiPu;

    public Product(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }

    @Override
    public void run() {
        while(true){

            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            synchronized (baoZiPu){
                //1.判断flag是否为true,如果是true,证明有包子,生产线程等待
                if (baoZiPu.isFlag()==true){
                    try {
                        baoZiPu.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }

                //2.如果flag为false,证明没有包子,开始生产
                baoZiPu.setCount();
                //3.改变flag状态,为true,证明生产完了,有包子了
                baoZiPu.setFlag(true);
                //4.唤醒消费线程
                baoZiPu.notify();
            }
        }
    }
}
代码语言:javascript
复制
public class Consumer implements Runnable{
    private BaoZiPu baoZiPu;

    public Consumer(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }

    @Override
    public void run() {
        while(true){

            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            synchronized (baoZiPu){
                //1.判断flag是否为false,如果是false,证明没有包子,消费线程等待
                if (baoZiPu.isFlag()==false){
                    try {
                        baoZiPu.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }

                //2.如果flag为true,证明有包子,开始消费
                baoZiPu.getCount();
                //3.改变flag状态,为false,证明消费完了,没 有包子了
                baoZiPu.setFlag(false);
                //4.唤醒生产线程
                baoZiPu.notify();
            }
        }
    }
}
代码语言:javascript
复制
public class Test01 {
    public static void main(String[] args) {
        BaoZiPu baoZiPu = new BaoZiPu();

        Product product = new Product(baoZiPu);
        Consumer consumer = new Consumer(baoZiPu);

        Thread t1 = new Thread(product);
        Thread t2 = new Thread(consumer);

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

第二章:多等待多唤醒

1.解决多生产多消费问题

在多生产者和多消费者的情况下,我们需要使用while循环来替代if判断,并将notify改为notifyAll,以确保所有等待的线程都能被唤醒。

代码语言:javascript
复制
public class Test01 {
    public static void main(String[] args) {
        BaoZiPu baoZiPu = new BaoZiPu();

        Product product = new Product(baoZiPu);
        Consumer consumer = new Consumer(baoZiPu);

        new Thread(product).start();
        new Thread(product).start();
        new Thread(product).start();

        new Thread(consumer).start();
        new Thread(consumer).start();
        new Thread(consumer).start();
    }
}
代码语言:javascript
复制
/*
   count和flag可以定义成包装类
   但是要记得给count和flag手动赋值
   不然对于本案例来说,容易出现空指针异常
 */
public class BaoZiPu {
    //代表包子的count
    private int count;
    //代表是否有包子的flag
    private boolean flag;

    public BaoZiPu() {
    }

    public BaoZiPu(int count, boolean flag) {
        this.count = count;
        this.flag = flag;
    }

    /*
       getCount 改造成消费包子方法
       直接输出count
     */
    public synchronized void getCount() {
        //1.判断flag是否为false,如果是false,证明没有包子,消费线程等待
        while (this.flag == false) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        //2.如果flag为true,证明有包子,开始消费
        System.out.println("消费了..............第" + count + "个包子");

        //3.改变flag状态,为false,证明消费完了,没 有包子了
        this.flag = false;
        //4.唤醒所有等待线程
        this.notifyAll();
    }

    /*
       setCount 改造成生产包子
       count++
     */
    public synchronized void setCount() {
        //1.判断flag是否为true,如果是true,证明有包子,生产线程等待
        while (this.flag == true) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        //2.如果flag为false,证明没有包子,开始生产
        count++;
        System.out.println("生产了...第" + count + "个包子");
        //3.改变flag状态,为true,证明生产完了,有包子了
        this.flag = true;
        //4.唤醒所有等待线程
        this.notifyAll();
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }
}
代码语言:javascript
复制
public class Product implements Runnable{
    private BaoZiPu baoZiPu;

    public Product(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }

    @Override
    public void run() {
        while(true){

            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            baoZiPu.setCount();
        }
    }
}
代码语言:javascript
复制
public class Consumer implements Runnable{
    private BaoZiPu baoZiPu;

    public Consumer(BaoZiPu baoZiPu) {
        this.baoZiPu = baoZiPu;
    }

    @Override
    public void run() {
        while(true){

            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            baoZiPu.getCount();
        }
    }
}

第三章:Lock锁

1.Lock对象的介绍和基本使用

Lock是一个接口,其实现类为ReentrantLock,提供了比synchronized更灵活的锁控制。

  • lock():获取锁。
  • unlock():释放锁。
代码语言:javascript
复制
public class MyTicket implements Runnable {
    //定义100张票
    int ticket = 100;

    //创建Lock对象
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(100L);

                //获取锁
                lock.lock();
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票");
                    ticket--;
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }finally {
                //释放锁
                lock.unlock();
            }

        }
    }
}
代码语言:javascript
复制
public class Test01 {
    public static void main(String[] args) {
        MyTicket myTicket = new MyTicket();

        Thread t1 = new Thread(myTicket, "赵四");
        Thread t2 = new Thread(myTicket, "刘能");
        Thread t3 = new Thread(myTicket, "广坤");

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

synchronized: 不管是同步代码块还是同步方法,都需要在结束一对{}之后,释放锁对象 Lock: 是通过两个方法控制需要被同步的代码,更灵活

第四章:Callable接口_实现多线程方式三

Callable接口是实现多线程的另一种方式,它允许任务返回值。

  • call():设置线程任务的方法,类似于run方法,但可以返回值和抛出异常。
代码语言:javascript
复制
public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "涛哥和金莲...的故事";
    }
}
代码语言:javascript
复制
public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable myCallable = new MyCallable();
        /*
           FutureTask(Callable<V> callable)
         */
        FutureTask<String> futureTask = new FutureTask<>(myCallable);

        //创建Thread对象-> Thread(Runnable target)
        Thread t1 = new Thread(futureTask);
        t1.start();

        //调用get方法获取call方法返回值
        System.out.println(futureTask.get());
    }
}

第五章:线程池_实现多线程方式四

问题:之前来一个线程任务,就需要创建一个线程对象去执行,用完还要销毁线程对象,如果线程任务多了,就需要频繁创建线程对象和销毁线程对象,这样会耗费内存资源,所以我们就想线程对象能不能循环利用,用的时候直接拿线程对象,用完还回去

线程池是管理线程对象的一种机制,可以循环利用线程对象,减少创建和销毁线程对象的开销。

  • Executors.newFixedThreadPool(int nThreads):创建固定大小的线程池。
  • submit(Runnable task)submit(Callable<T> task):提交任务到线程池执行。
  • Future.get():获取任务的返回值。
代码语言:javascript
复制
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"...执行了");
    }
}
代码语言:javascript
复制
public class Test01 {
    public static void main(String[] args) {
        //创建线程池对象
        ExecutorService es = Executors.newFixedThreadPool(2);
        es.submit(new MyRunnable());
        es.submit(new MyRunnable());
        es.submit(new MyRunnable());

        //es.shutdown();//关闭线程池对象
    }
}
代码语言:javascript
复制
public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        return 1;
    }
}
代码语言:javascript
复制
public class Test02 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService es = Executors.newFixedThreadPool(2);
        Future<Integer> future = es.submit(new MyCallable());
        System.out.println(future.get());
    }
}
练习

需求:创建两个线程任务,一个线程任务完成1-100的和,一个线程任务返回一个字符串

代码语言:javascript
复制
public class MyString implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "那一夜,你没有拒绝我,那一夜,你伤害了我";
    }
}
代码语言:javascript
复制
public class MySum implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            sum+=i;
        }
        return sum;
    }
}
代码语言:javascript
复制
public class Test01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建线程池对象
        ExecutorService es = Executors.newFixedThreadPool(2);
        Future<String> f1 = es.submit(new MyString());
        Future<Integer> f2 = es.submit(new MySum());
        System.out.println(f1.get());
        System.out.println(f2.get());
    }
}

第六章:定时器_Timer

Timer类用于定时执行任务。

  • schedule(TimerTask task, Date firstTime, long period):安排任务在指定时间开始执行,每隔一定时间重复执行。
代码语言:javascript
复制
public class Demo01Timer {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("金莲对涛哥说:涛哥,快起床了~~~");
            }
        }, new Date(), 2000L);
    }
}

小结

通过今天的学习,希望可以帮助你掌握多线程的高级应用,包括等待唤醒机制、Lock锁、Callable接口、线程池以及定时器的使用,这些都是Java并发编程中非常重要的知识点。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-12-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • day17回顾
  • 模块17重点
  • 第一章:等待唤醒机制
    • 1.等待唤醒案例分析(线程之间的通信)
    • 2.等待唤醒案例实现
  • 第二章:多等待多唤醒
    • 1.解决多生产多消费问题
  • 第三章:Lock锁
    • 1.Lock对象的介绍和基本使用
  • 第四章:Callable接口_实现多线程方式三
  • 第五章:线程池_实现多线程方式四
    • 练习
  • 第六章:定时器_Timer
  • 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档