三则案例,助你掌握多线程 | 带你学《Java语言高级特性》之十五

简介: 经历了前几节的学习,相信你已经对多线程有了自己的见解,现在,跟作者一起实操三则案例,助你快速消化所学知识吧!

上一篇:另类的“同步”-volatile关键字 | 带你学《Java语言高级特性》之十四
【本节目标】
通过阅读本节内容,你将通过数字加减、生产电脑、竞争抢答三则案例的多线程相关逻辑实现,进一步掌握多线程开发的相关能力,对线程的各类操作更加得心应手。

多线程案例分析一:数字加减

设计4个线程对象,两个线程执行减操作,两个线程执行加操作。

public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        Resource res = new Resource();
        SubThread st = new SubThread(res);
        AddThread at = new AddThread(res);
        new Thread(at, "加法线程-A ").start();
        new Thread(at, "加法线程-B ").start();
        new Thread(st, "减法线程-X ").start();
        new Thread(st, "减法线程-Y ").start();
    }
}
class AddThread implements Runnable {
    private Resource resource;
    public AddThread(Resource resource) {
        this.resource = resource;
    }
    @Override
    public void run() {
        for (int x = 0 ; x< 50 ; x++) {
            try {
                this.resource.add();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
class SubThread implements Runnable {
    private Resource resource;
    public SubThread(Resource resource) {
        this.resource = resource;
    }
    @Override
    public void run() {
        for (int x = 0 ; x < 50 ; x++) {
            try {
                this.resource.sub();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
class Resource {     //定义一个操作的资源
    private int num = 0;      //这个是要进行加减操作的数据
    private boolean flag = true;       //加减的切换
    //flag = true:表示可以进行加法操作,但是无法进行减法操作
    //flag = false:表示可以进行减法操作,但是无法进行加法操作
    public synchronized void add() throws Exception {      //执行加法操作
        if (this.flag == false) {         //现在需要执行的是减法操作,加法操作要等待
            super.wait();
        }
        Thread.sleep(100) ;
        this.num++ ;
        System.out.println("【加法操作 -" + Thread.currentThread().getName() + "】num = " + this.num) ;
        this.flag = false ;    //加法操作执行完毕,需要执行减法处理
        super.notifyAll() ;   //唤醒全部等待线程

    }
    public synchronized void sub() throws Exception {    //执行减法操作
        if (this.flag == true) {      //减法操作需要等待
            System.out.println("【减法操作 -" + 
Thread.currentThread().getName() + "】进行等待") ;
            super.wait() ;
        }
        Thread.sleep(200) ;
        this.num-- ;
        System.out.println("【减法操作 -" + Thread.currentThread().getName() + "】num = " + this.num) ;
        this.flag = true ;
        super.notifyAll() ;
    }
}

image.png
图一 多线程加减法操作

这是一个经典的多线程开发操作,这一个程序里面一定要考虑的核心本质在于:加一个、减一个,整体的计算结果应该在0、-1、1 中循环出现才是合理的。

多线程案例分析二:生产电脑

设计一个生产电脑和搬运电脑类,要求生产出一台电脑就搬走一台电脑,如果没有新的电脑生产出来,则搬运工需要等待新电脑产出;如果生产出的电脑没有搬走,则要等待电脑搬走再生产,并统计出生产的电脑数量。
在本程序中,就是一个标准的生产者与消费者的处理模型,那么下面实现具体的程序代码。

public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        Resource res = new Resource() ;
        new Thread(new Producer(res)).start() ;
        new Thread(new Consumer(res)).start() ;
        new Thread(st).start() ;
        new Thread(st).start() ;
    }
}
class Producer implements Runnable {
    private Resource resource ;
    public Producer(Resource resource) {
        this.resource = resource ;
    }
    @Override
    public void run() {
        for (int x = 0; x < 50; x++) {
            try {
                this.resource.make() ;
            } catch (Exception e) {
                e.printStackTrace() ;
            }
        }
    }
}
class Consumer implements Runnable {
    private Resource resource ;
    public Consumer(Resource resource) {
        this.resource = resource ;
    }
    @Override
    public void run() {
        for (int x = 0 ; x < 50 ; x++) {
            try {
                this.resource.get() ;
            } catch (Exception e) {
                e.printStackTrace() ;
            }
        }
    }
}
class Resource {
    private Computer computer ;
    private boolean flag = true ;         //标记
    public synchronized void make() throws Exception {
        if (this.computer != null) {   //已经生产过了
            super.wait() ;
        }
        Thread.sleep(100) ;
        this.computer = new Computer("MLDN牌电脑", 1.1) ;
        System.out.println("【生产电脑】"+ this.computer) ;
        super.notifyAll() ;
    }
    public synchronized void get() throws Exception {
        if (this.computer == null) {    //没有生产过
            super.wait() ;
        }
        Thread.sleep(10) ;
        System.out.println("【取走电脑】"+ this.computer) ;
        this.computer = null ;    //已经取走了
        super.notifyAll() ;
    }
}
class Computer {
    private static int count = 0 ;    //表示生产的个数
    private String name ;
    private double price ;
    public Computer(String name, double price) {
        this.name = name ;
        this.price = price ;
        count++ ;
    }
    public String toString() {
        return "【第" + count + "台电脑】电脑名字:" + this.name + "价值:" + this.price ;
    }
}

image.png
图二 实现生产电脑

多线程案例分析三:竞争抢答

实现一个竞拍抢答程序:要求设置三个抢答者(三个线程),而后同时发出抢答指令,抢答成功者给出成功提示,未抢答成功者给出失败提示。

对于这个多线程操作,由于里面需要牵扯到数据返回问题,那么现在最好使用Callable是比较方便的一种处理形式。

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        MyThread mt = new MyThread();
        FutureTask<String> taskA = new FutureTask(mt);
        FutureTask<String> taskB = new FutureTask(mt);
        FutureTask<String> taskC = new FutureTask(mt);
        new Thread(taskA, "竞赛者A").start();
        new Thread(taskB, "竞赛者B").start();
        new Thread(taskC, "竞赛者C").start();
        System.out.println(taskA.get());
        System.out.println(taskB.get());
        System.out.println(taskC.get());
    }
}
class MyThread implements Callable<String> {
    private boolean flag = false;          //抢到处理
    @Override
    public String call() throws Exception {
        synchronized (this) {                //数据同步
            if (this.flag == false) {        //抢答成功
                this.flag = true;
                return Thread.currentThread().getName() + "抢答成功!" ;
            } else {
                return Thread.currentThread().getName() + "抢答失败!" ;
            }
        }
    }
}

image.png
图三 多线程竞答

想学习更多的Java的课程吗?从小白到大神,从入门到精通,更多精彩不容错过!免费为您提供更多的学习资源。
本内容视频来源于阿里云大学

下一篇:让String拥有变化之力 | 带你学《Java语言高级特性》之十六
更多Java面向对象编程文章查看此处

相关文章
|
5天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
25 9
|
8天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####
|
4天前
|
监控 安全 Java
Java中的多线程编程:从入门到实践####
本文将深入浅出地探讨Java多线程编程的核心概念、应用场景及实践技巧。不同于传统的摘要形式,本文将以一个简短的代码示例作为开篇,直接展示多线程的魅力,随后再详细解析其背后的原理与实现方式,旨在帮助读者快速理解并掌握Java多线程编程的基本技能。 ```java // 简单的多线程示例:创建两个线程,分别打印不同的消息 public class SimpleMultithreading { public static void main(String[] args) { Thread thread1 = new Thread(() -> System.out.prin
|
8天前
|
Java
JAVA多线程通信:为何wait()与notify()如此重要?
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是实现线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件满足时被唤醒,从而确保数据一致性和同步。相比其他通信方式,如忙等待,这些方法更高效灵活。 示例代码展示了如何在生产者-消费者模型中使用这些方法实现线程间的协调和同步。
22 3
|
6天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
7天前
|
Java
java小知识—进程和线程
进程 进程是程序的一次执行过程,是系统运行的基本单位,因此进程是动态的。系统运行一个程序即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个进程还占有某些系统资源如CPU时间,内存空间,文件,文件,输入输出设备的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。 线程 线程,与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间做切换工作时,负担要比
18 1
|
8天前
|
Java UED
Java中的多线程编程基础与实践
【10月更文挑战第35天】在Java的世界中,多线程是提升应用性能和响应性的利器。本文将深入浅出地介绍如何在Java中创建和管理线程,以及如何利用同步机制确保数据一致性。我们将从简单的“Hello, World!”线程示例出发,逐步探索线程池的高效使用,并讨论常见的多线程问题。无论你是Java新手还是希望深化理解,这篇文章都将为你打开多线程的大门。
|
8天前
|
安全 Java 编译器
Java多线程编程的陷阱与最佳实践####
【10月更文挑战第29天】 本文深入探讨了Java多线程编程中的常见陷阱,如竞态条件、死锁、内存一致性错误等,并通过实例分析揭示了这些陷阱的成因。同时,文章也分享了一系列最佳实践,包括使用volatile关键字、原子类、线程安全集合以及并发框架(如java.util.concurrent包下的工具类),帮助开发者有效避免多线程编程中的问题,提升应用的稳定性和性能。 ####
34 1
|
8天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
17天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?