《Java 简易速速上手小册》第6章:Java 并发编程(2024 最新版)

news2024/11/26 15:01:16

在这里插入图片描述

文章目录

  • 6.1 线程的创建和管理 - 召唤你的士兵
    • 6.1.1 基础知识
    • 6.1.2 重点案例:实现一个简单的计数器
    • 6.1.3 拓展案例 1:定时器线程
    • 6.1.4 拓展案例 2:使用 Executor 框架管理线程
  • 6.2 同步机制 - 维持军队的秩序
    • 6.2.1 基础知识
    • 6.2.2 重点案例:银行转账操作
    • 6.2.3 拓展案例 1:生产者消费者问题
    • 6.2.4 拓展案例 2:读写锁实现缓存系统
  • 6.3 并发工具类 - 你的特殊武器
    • 6.3.1 基础知识
    • 6.3.2 重点案例:使用 CountDownLatch 协调任务
    • 6.3.3 拓展案例 1:使用 CyclicBarrier 同步周期性任务
    • 6.3.4 拓展案例 2:使用 Semaphore 控制资源访问

6.1 线程的创建和管理 - 召唤你的士兵

在Java并发编程的世界中,线程是执行任务的基本单位。正确地创建和管理线程就像是召唤和指挥你的士兵一样重要。让我们深入探索如何有效地召唤这些勇士,并确保他们能够有效地完成任务。

6.1.1 基础知识

  • 创建线程的两种方式

    • 继承Thread类:创建一个新类继承Thread类,并重写run()方法。通过实例化这个类并调用其start()方法来启动线程。
    • 实现Runnable接口:创建一个实现了Runnable接口的类,并实现run()方法。然后将这个实现类的实例传递给Thread类的构造函数,并通过新线程的start()方法来启动。
  • 线程的生命周期:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和终止(Terminated)。

  • 线程的优先级:每个线程都有一个优先级,它们可以从Thread.MIN_PRIORITY(1)到Thread.MAX_PRIORITY(10)变化,Thread.NORM_PRIORITY(5)是默认优先级。

6.1.2 重点案例:实现一个简单的计数器

假设我们要实现一个简单的计数器,每个线程负责将一个共享变量增加到特定的值。

计数器Runnable实现:

public class Counter implements Runnable {
    private final int limit;
    private static int count = 0;

    public Counter(int limit) {
        this.limit = limit;
    }

    @Override
    public void run() {
        while (count < limit) {
            synchronized (Counter.class) {
                if (count < limit) {
                    System.out.println(Thread.currentThread().getName() + ": " + (++count));
                }
            }
        }
    }

    public static void main(String[] args) {
        Runnable counter = new Counter(10);
        new Thread(counter, "Thread-1").start();
        new Thread(counter, "Thread-2").start();
    }
}

在这个例子中,我们创建了一个实现了Runnable接口的Counter类。每个线程在run()方法中增加计数器,直到达到了限制值。我们使用synchronized关键字来确保在同一时刻只有一个线程能够增加计数器。

6.1.3 拓展案例 1:定时器线程

创建一个线程,定时打印消息到控制台,演示如何使用线程来执行定时任务。

import java.util.Timer;
import java.util.TimerTask;

public class Reminder {
    Timer timer;

    public Reminder(int seconds) {
        timer = new Timer();
        timer.schedule(new RemindTask(), seconds * 1000);
    }

    class RemindTask extends TimerTask {
        public void run() {
            System.out.println("Time's up!");
            timer.cancel();
        }
    }

    public static void main(String[] args) {
        new Reminder(5);
        System.out.println("Task scheduled.");
    }
}

6.1.4 拓展案例 2:使用 Executor 框架管理线程

Executor框架提供了更高级的接口来管理线程池,使得管理一组任务更加容易。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        
        for (int i = 0; i < 10; i++) {
            Runnable worker = new WorkerThread("" + i);
            executor.execute(worker);
        }
        
        executor.shutdown();
        while (!executor.isTerminated()) {
        }
        
        System.out.println("Finished all threads");
    }
}

class WorkerThread implements Runnable {
    private String message;
    
    public WorkerThread(String message) {
        this.message = message;
    }

    public void run() {
        System.out.println(Thread.currentThread().getName() + " (Start) message = " + message);
        processMessage();
       

 System.out.println(Thread.currentThread().getName() + " (End)");
    }

    private void processMessage() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

通过这些案例,我们看到了Java线程创建和管理的多样化方法。无论是通过实现Runnable接口,使用定时器任务,还是利用Executor框架管理线程池,正确的线程管理策略都能使你的并发程序运行得更加高效和稳定。现在,带着你的士兵们勇往直前,征服并发编程的世界吧!

在这里插入图片描述


6.2 同步机制 - 维持军队的秩序

在并发编程的战场上,正确的同步机制就像是用来维持你的数据军队秩序的军规,确保所有士兵行动协调,避免混乱和冲突。Java提供了多种同步工具和方法,帮助开发者有效管理线程间的协作和资源共享。

6.2.1 基础知识

  • synchronized关键字:可以用于方法或代码块,确保同一时刻只有一个线程可以执行该段代码,从而避免资源冲突或数据不一致的问题。

  • volatile关键字:用于标记变量,确保每次访问变量时都会从主内存中读取,而不是从线程的工作内存,从而保证了变量的可见性。

  • Lock接口:提供了比synchronized更灵活的锁定机制,包括可重入锁(ReentrantLock)、读写锁(ReadWriteLock)等,允许更细粒度的锁控制。

  • Condition接口:与Lock配合使用,允许线程间有更细致的通信(比如等待/通知机制),实现线程间的协调。

6.2.2 重点案例:银行转账操作

假设我们需要实现一个银行账户的转账操作,这个操作需要确保线程安全,避免在并发环境下出现资金错误。

public class Account {
    private int balance;
    private final Lock lock = new ReentrantLock();

    public Account(int balance) {
        this.balance = balance;
    }

    // 转账操作
    public void transfer(Account target, int amount) {
        lock.lock();
        try {
            if (this.balance >= amount) {
                this.balance -= amount;
                target.deposit(amount);
            }
        } finally {
            lock.unlock();
        }
    }

    public void deposit(int amount) {
        lock.lock();
        try {
            this.balance += amount;
        } finally {
            lock.unlock();
        }
    }

    public int getBalance() {
        return balance;
    }
}

在这个例子中,transfer方法使用了ReentrantLock来确保转账操作的原子性,避免了并发环境下的资金错误。

6.2.3 拓展案例 1:生产者消费者问题

生产者消费者是并发编程中的一个经典问题,它涉及到两个或多个线程间的协作。使用LockCondition可以优雅地解决这个问题。

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ProducerConsumer {
    private final Queue<Integer> queue = new LinkedList<>();
    private final int capacity = 10;
    private final Lock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();

    // 生产者方法
    public void produce(int value) throws InterruptedException {
        lock.lock();
        try {
            while (queue.size() == capacity) {
                notFull.await();
            }
            queue.add(value);
            notEmpty.signalAll();
        } finally {
            lock.unlock();
        }
    }

    // 消费者方法
    public int consume() throws InterruptedException {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                notEmpty.await();
            }
            int value = queue.poll();
            notFull.signalAll();
            return value;
        } finally {
            lock.unlock();
        }
    }
}

6.2.4 拓展案例 2:读写锁实现缓存系统

读写锁(ReadWriteLock)允许多个读操作同时进行,但写操作是互斥的。这对于实现缓存系统来说非常有用。

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWrite

Lock;

public class Cache {
    private final Map<String, Object> map = new HashMap<>();
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();

    public Object get(String key) {
        rwLock.readLock().lock();
        try {
            return map.get(key);
        } finally {
            rwLock.readLock().unlock();
        }
    }

    public void put(String key, Object value) {
        rwLock.writeLock().lock();
        try {
            map.put(key, value);
        } finally {
            rwLock.writeLock().unlock();
        }
    }
}

通过这些案例,我们看到了Java中同步机制的强大之处,它不仅帮助我们维持线程间的协作和数据的一致性,还使我们能够设计出高效且线程安全的并发应用。掌握这些同步工具,成为并发编程的指挥官吧!

在这里插入图片描述


6.3 并发工具类 - 你的特殊武器

Java的并发工具类就像是隐藏在你的武器库中的特殊武器,它们可以帮助你在并发编程的战场上更加游刃有余。这些工具类提供了强大的功能来帮助管理线程间的协调,以及对共享资源的访问控制,让你能够写出更高效、更健壮的并发程序。

6.3.1 基础知识

  • CountDownLatch:允许一个或多个线程等待其他线程完成一系列操作。当倒计时达到零时,等待的线程被释放继续执行。

  • CyclicBarrier:允许一组线程互相等待,直到所有线程都达到了某个共同点,然后继续执行。

  • Semaphore:一种基于计数的同步机制,可以控制对共享资源的访问。它可以限制同时访问某个特定资源的线程数量。

  • Concurrent Collections:提供了线程安全的集合类,如ConcurrentHashMapCopyOnWriteArrayList等,用于在并发环境中管理数据。

  • Executor框架:简化了线程的创建和管理,提供了线程池等高级功能,使得并发任务的调度和管理更加灵活和强大。

6.3.2 重点案例:使用 CountDownLatch 协调任务

假设我们有一个任务,需要在开始执行主任务之前,等待其他几个服务初始化完成。

import java.util.concurrent.CountDownLatch;

public class ServiceInitializer {
    private static final int NUM_OF_SERVICES = 3;
    private final CountDownLatch latch = new CountDownLatch(NUM_OF_SERVICES);

    public void initialize() {
        for (int i = 1; i <= NUM_OF_SERVICES; i++) {
            new Thread(new Service(latch), "Service " + i).start();
        }

        try {
            latch.await(); // 等待所有服务初始化完成
            System.out.println("All services are initialized. Main task is starting now.");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    static class Service implements Runnable {
        private final CountDownLatch latch;

        public Service(CountDownLatch latch) {
            this.latch = latch;
        }

        @Override
        public void run() {
            try {
                // 模拟服务初始化耗时
                Thread.sleep((long) (Math.random() * 1000));
                System.out.println(Thread.currentThread().getName() + " initialized.");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                latch.countDown();
            }
        }
    }

    public static void main(String[] args) {
        new ServiceInitializer().initialize();
    }
}

6.3.3 拓展案例 1:使用 CyclicBarrier 同步周期性任务

假设我们需要执行一个周期性任务,该任务需要在每个周期内的所有子任务完成后才能开始下一个周期。

import java.util.concurrent.CyclicBarrier;

public class CyclicTask implements Runnable {
    private CyclicBarrier barrier;

    public CyclicTask(CyclicBarrier barrier) {
        this.barrier = barrier;
    }

    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName() + " is waiting at the barrier.");
            barrier.await();
            System.out.println(Thread.currentThread().getName() + " has crossed the barrier.");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        final int parties = 3;
        CyclicBarrier barrier = new CyclicBarrier(parties, 
            () -> System.out.println("All parties have arrived at the barrier, let's proceed to the next step."));

        for (int i = 0; i < parties; i++) {
            new Thread(new CyclicTask(barrier), "Thread " + (i + 1)).start();
        }
    }
}

6.3.4 拓展案例 2:使用 Semaphore 控制资源访问

在某些情况下,我们需要限制对某个资源的并发访问数量

。Semaphore提供了一种简单有效的方法来实现这一目标。

import java.util.concurrent.Semaphore;

public class SemaphoreDemo {
    private static final int MAX_PERMITS = 3;
    private final Semaphore semaphore = new Semaphore(MAX_PERMITS);

    public void accessResource() {
        try {
            semaphore.acquire();
            System.out.println(Thread.currentThread().getName() + " is accessing the resource.");
            Thread.sleep(1000); // 模拟资源访问耗时
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release();
            System.out.println(Thread.currentThread().getName() + " has released the resource.");
        }
    }

    public static void main(String[] args) {
        SemaphoreDemo demo = new SemaphoreDemo();
        for (int i = 0; i < 6; i++) {
            new Thread(demo::accessResource, "Thread " + (i + 1)).start();
        }
    }
}

通过这些案例,我们可以看到Java并发工具类如何成为处理并发和多线程问题的强大武器。无论是协调多个任务的完成,同步周期性任务的执行,还是控制对共享资源的访问,这些工具类都能让你的并发编程工作变得更加简单和高效。使用这些特殊的武器,指挥你的数据军团,优雅地完成并发任务!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1449018.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何合理规划 PostgreSQL 的数据库用户

PostgreSQL 作为世界上最领先的开源数据库&#xff0c;有一套强大的用户角色权限系统&#xff0c;和 MySQL 做一个对比&#xff1a; 但硬币的另一面则是对于简单场景来说增加了复杂度。在许多单应用场景&#xff0c;其实也不需要额外的 schema 层&#xff0c;也不需要额外的 ow…

MATLAB | 情人节画个花瓣venn图?

之前七夕节情人节各种花&#xff0c;相册&#xff0c;爱心啥的都快画够了&#xff0c;今年画个花瓣韦恩图&#xff1f; 花瓣上的数字是仅属于该类的样本数&#xff0c;而中心的数字是属于每一类的样本数 教程部分 0 数据准备 % 给组起名t1 t2 t3...t15 setName compose(t%d,…

(五)【Jmeter】使用代理录制HTTP脚本操作步骤及注意事项

前置信息 软件版本Jmeter5.6.3 服务网址备注drupalhttp://192.168.88.88:18080/&#xff08;二&#xff09;【Jmeter】专栏实战项目靶场drupal部署 用户名密码test1test1test2test2 实操记录 1、启动jmeter&#xff0c;操作顺序见下图 2、在视图面板添加如下信息&#x…

【复现】某某ERP 信息泄露漏洞_49

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 该ERP基于SpringBoot框架和SaaS模式&#xff0c;立志为中小企业提供开源好用的ERP软件&#xff0c;目前专注进销存财务生产功能。…

【AI视野·今日NLP 自然语言处理论文速览 第七十八期】Wed, 17 Jan 2024

AI视野今日CS.NLP 自然语言处理论文速览 Wed, 17 Jan 2024 (showing first 100 of 163 entries) Totally 100 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Deductive Closure Training of Language Models for Coherence, Accur…

优思学院|工业4.0:世界上首个工业机器人诞生于50年前

五十年前&#xff0c;德国机床制造商库卡&#xff08;KUKA&#xff09;推出了FAMULUS工业机器人&#xff0c;这不仅是现代工业机器人发展史上的一个重要里程碑&#xff0c;也为后来的技术革新铺平了道路。1973年&#xff0c;世界见证了一种拥有六轴的多关节臂动力学设计的机器人…

『运维备忘录』之 Lsof 命令详解

运维人员不仅要熟悉操作系统、服务器、网络等只是&#xff0c;甚至对于开发相关的也要有所了解。很多运维工作者可能一时半会记不住那么多命令、代码、方法、原理或者用法等等。这里我将结合自身工作&#xff0c;持续给大家更新运维工作所需要接触到的知识点&#xff0c;希望大…

【王道数据结构】【chapter5树与二叉树】【P158t11】

在二叉树中查找值为x的结点&#xff0c;试编写算法&#xff08;用c语言&#xff09;打印值为x的节点的所有祖先&#xff0c;假设值为x的结点不多于1个 #include <iostream> #include <stack> #include<queue> typedef struct treenode{char data;struct tree…

arkTS开发鸿蒙OS个人商城案例【2024最新 新年限定开发案例QAQ】

龙年前述 源码获取>文章下方二维码&#xff0c;回复关键字“鸿蒙OS商场源码” 前言 arkTS是华为自己研发的一套前端语言&#xff0c;是在js和ts技术的基础上又进行了升级而成&#xff01; 本篇文章会带领大家通过arkTSnode.jsmongoDB来完成一个鸿蒙OS版本的商城案例&…

bpmn-js 事件总线处理

bpmn-js中使用EventBus作为事件的处理句柄&#xff0c;EventBus的使用和我们常规使用的事件总线没啥大的区别&#xff0c;其源码位于&#xff1a;/diagram-js/lib/core/EventBus.js &#xff08;bpmn-js使用diagram-js实现流程图的web端绘制呈现工具&#xff09;。 EventBus使用…

ubuntu22.04@laptop OpenCV Get Started: 008_image_filtering_using_convolution

ubuntu22.04laptop OpenCV Get Started: 008_image_filtering_using_convolution 1. 源由2. convolution应用Demo2.1 C应用Demo2.2 Python应用Demo 3. 重点分析3.1 identity矩阵3.2 all ones 5x5矩阵3.3 blur 5x5矩阵3.4 GaussianBlur 5x5矩阵3.5 medianBlur 5x5矩阵3.6 Sharpe…

「数据结构」MapSet

&#x1f387;个人主页&#xff1a;Ice_Sugar_7 &#x1f387;所属专栏&#xff1a;Java数据结构 &#x1f387;欢迎点赞收藏加关注哦&#xff01; Map&Set &#x1f349;概念&#x1f349;模型&#x1f349;Map&#x1f34c;TreeMap和HashMap的区别&#x1f34c;Map常用方…

儿童护眼台灯哪个值得推荐?推荐专业的儿童护眼台灯

现在的孩子很多都存在视力问题&#xff0c;而且年龄也越来越早&#xff0c;不少还为上学的孩子都早已戴上小眼镜。虽说这可能存在家族近视遗传的可能性&#xff0c;不过更多的还是后天导致的。长时间玩耍电子产品、缺乏运动、不良用眼习惯、不合适的光线等等都是导致孩子近视的…

【知识图谱--第四讲知识图谱的抽取与构建】

知识图谱的抽取与构建 实体识别与分类关系抽取与属性补全概念抽取事件识别与抽取 实体识别与分类 关系抽取与属性补全 概念抽取 事件识别与抽取

面试经典150题——最小覆盖子串(困难)

"The greatest glory in living lies not in never falling, but in rising every time we fall." - Nelson Mandela​ 1. 题目描述 2. 题目分析与解析 2.1 思路一——暴力求解 还是和之前讲的一样&#xff0c;看见题目没思路&#xff0c;先试试普通情况下人的解法…

ng : 无法加载文件 C:\Program Files\nodejs\node_global\ng.ps1, 因为在此系统上禁止运行脚本

ng : 无法加载文件 C:\Program Files\nodejs\node_global\ng.ps1&#xff0c;因为在此系统上禁止运行脚本 今天在VSCode中运行ng serve --port 8081运行基于Angular的项目时&#xff0c;报错了&#xff0c;错误如下图所示&#xff1a; 解决方法&#xff1a; 按照下图的5步即…

智能汽车行业产业研究报告:4D成像毫米波雷达—自动驾驶最佳辅助

今天分享的是智能汽车系列深度研究报告&#xff1a;《智能汽车行业产业研究报告&#xff1a;4D成像毫米波雷达—自动驾驶最佳辅助》。 &#xff08;报告出品方&#xff1a;开源证券&#xff09; 报告共计&#xff1a;43页 视觉感知最佳辅助——4D 成像毫米波雷达 感知是自动…

解决‘vue‘ 不是内部或外部命令,也不是可运行的程序(设置全局变量)

发现是没有执行&#xff1a; npm install -g vue/cli 但是发现还是不行 此时&#xff0c;我们安装了 Vue CLI&#xff0c;但是在运行 vue ui 命令时出现了问题。这通常是因为全局安装的 Vue CLI 的路径没有被正确地添加到系统的环境变量中。 可以尝试以下几种方法来解决这个问…

如何把华为手机上的数据转移到荣耀手机上?

方法/步骤 点击并进入华为手机&#xff08;旧手机&#xff09;的【手机克隆】应用&#xff0c;选择【这是旧设备】&#xff1b; 点击并进入荣耀手机&#xff08;新手机&#xff09;的【换机克隆】应用&#xff0c;选择【这是新设备】&#xff1b; 荣耀手机&#xff08;新…

《VulnStack》ATTCK-1

title: 《VulnStack》ATT&CK-1 date: 2024-01-29 14:53:49 updated: 2024-02-14 18:55:49 categories: WriteUp&#xff1a;Cyber-Range excerpt: 主机发现、端口扫描&#xff0c;服务探测&#xff0c;操作系统探测、nmap 漏洞库扫描、网站首页信息泄露、msf 渗透与信息收集…