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

在这里插入图片描述

文章目录

  • 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.mfbz.cn/a/385848.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

pytorch花式索引提取topk的张量

文章目录 pytorch花式索引提取topk的张量问题设定代码实现索引方法gather方法验证 补充知识expand方法gather方法randint pytorch花式索引提取topk的张量 问题设定 或者说&#xff0c;有一个(bs, dim, L)的大张量&#xff0c;索引的index形状为(bs, X)&#xff0c;想得到一个(…

Java 基于 SpringBoot 的大药房管理系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

备战蓝桥杯---动态规划(入门1)

先补充一下背包问题&#xff1a; 于是&#xff0c;我们把每一组当成一个物品&#xff0c;f[k][v]表示前k组花费v的最大值。 转移方程还是max(f[k-1][v],f[k-1][v-c[i]]w[i]) 伪代码&#xff08;注意循环顺序&#xff09;&#xff1a; for 所有组&#xff1a; for vmax.....0…

使用Word Embedding+Keras进行自然语言处理NLP

目录 介绍&#xff1a; one-hot&#xff1a; pad_sequences: 建模: 介绍&#xff1a; Word Embedding是一种将单词表示为低维稠密向量的技术。它通过学习单词在文本中的上下文关系&#xff0c;将其映射到一个连续的向量空间中。在这个向量空间中&#xff0c;相似的单词在空间…

实现JNDI

实现JNDI 问题陈述 Smart Software Developer Ltd.想要开发一款Web应用程序,它使用servlt基于雇员ID显示雇员信息,雇员ID由用户通过HTML用户界面传递。雇员详细信息存储在Employee_Master表中。另外,Web应用程序应显示网站被访问的次数。 解决方案 要解决上述问题,需要执…

2024.2.6 模拟实现 RabbitMQ —— 数据库操作

目录 引言 选择数据库 环境配置 设计数据库表 实现流程 封装数据库操作 针对 DataBaseManager 单元测试 引言 硬盘保存分为两个部分 数据库&#xff1a;交换机&#xff08;Exchange&#xff09;、队列&#xff08;Queue&#xff09;、绑定&#xff08;Binding&#xff0…

腾讯云4核8G服务器够用吗?能支持多少人?

腾讯云4核8G服务器支持多少人在线访问&#xff1f;支持25人同时访问。实际上程序效率不同支持人数在线人数不同&#xff0c;公网带宽也是影响4核8G服务器并发数的一大因素&#xff0c;假设公网带宽太小&#xff0c;流量直接卡在入口&#xff0c;4核8G配置的CPU内存也会造成计算…

webpack面试解析

参考&#xff1a; 上一篇webpack相关的系列&#xff1a;webpack深入学习&#xff0c;搭建和优化react项目 爪哇教育字节面试官解析webpack-路白 1、Webpack中的module是什么&#xff1f; 通常来讲&#xff0c;一个 module 模块就是指一个文件中导出的内容&#xff0c;webpack…

全国计算机等级考试二级,MySQL数据库考试大纲(2023年版)

基本要求&#xff1a; 1.掌握数据库的基本概念和方法。 2.熟练掌握MySQL的安装与配置。 3.熟练掌握MySQL平台下使用&#xff33;&#xff31;&#xff2c;语言实现数据库的交互操作。 4.熟练掌握 MySQL的数据库编程。 5.熟悉 PHP 应用开发语言&#xff0c;初步具备利用该语言进…

Vue-自定义属性和插槽(五)

目录 自定义指令 基本语法 (全局&局部注册) 指令的值 练习&#xff1a;v-loading 指令封装 总结&#xff1a; 插槽&#xff08;slot&#xff09; 默认插槽 插槽 - 后备内容&#xff08;默认值&#xff09; 具名插槽 具名插槽基本语法: 具名插槽简化语法: 作…

RocksDB:高性能键值存储引擎初探

在现代的分布式系统和大数据应用中&#xff0c;一个高效、可靠的存储引擎是不可或缺的。RocksDB&#xff0c;由Facebook于2012年开发并随后开源&#xff0c;正是为了满足这类需求而诞生的。它是一个持久化的键值存储系统&#xff0c;特别适合在闪存&#xff08;Flash&#xff0…

AtCoder Beginner Contest 340 C - Divide and Divide【打表推公式】

原题链接&#xff1a;https://atcoder.jp/contests/abc340/tasks/abc340_c Time Limit: 2 sec / Memory Limit: 1024 MB Score: 300 points 问题陈述 黑板上写着一个整数 N。 高桥将重复下面的一系列操作&#xff0c;直到所有不小于2的整数都从黑板上移除&#xff1a; 选择…

SCI论文作图规范

SCI论文作图规范包括以下几个方面&#xff1a; 一、图片格式 SCI论文通常接受的图片格式包括TIFF、EPS和PDF等。其中&#xff0c;TIFF格式是一种高质量的图像格式&#xff0c;适用于需要高分辨率和颜色准确性的图片&#xff1b;EPS格式是一种矢量图形格式&#xff0c;适用于需…

Leetcode 1035 不相交的线

题意理解&#xff1a; 在两条独立的水平线上按给定的顺序写下 nums1 和 nums2 中的整数。 现在&#xff0c;可以绘制一些连接两个数字 nums1[i] 和 nums2[j] 的直线&#xff0c;这些直线需要同时满足满足&#xff1a; nums1[i] nums2[j]且绘制的直线不与任何其他连线&#xff…

Spring 事务

Spring 事务传播&#xff08;Propagation&#xff09;特性 REQUIRED 支持一个当前的事务&#xff0c;如果不存在创建一个新的。SUPPORTS 支持一个当前事务&#xff0c;如果不存在以非事务执行。MANDATORY 支持一个当前事务&#xff0c;如果不存在任何抛出异常。REQUIRES_NEW 创…

百面嵌入式专栏(面试题)驱动开发面试题汇总 2.0

沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们将介绍驱动开发面试题 。 1、Linux系统的组成部分? Linux内核、Linux文件系统、Linux shell、Linux应用程序。 2、Linux内核的组成部分? (1)第一种分类方式:内存管理子系统、进程管理子系统、文件管理子系…

react【六】 React-Router

文章目录 1、Router1.1 路由1.2 认识React-Router1.3 Link和NavLink1.4 Navigate1.5 Not Found页面配置1.6 路由的嵌套1.7 手动路由的跳转1.7.1 在函数式组件中使用hook1.7.2 在类组件中封装高阶组件 1.8 动态路由传递参数1.9 路由的配置文件以及懒加载 1、Router 1.1 路由 1.…

VC++ 绘制折线学习

win32 有三个绘制折线的函数&#xff1b; Polyline&#xff0c;根据给定点数组绘制折线&#xff1b; PolylineTo&#xff0c;除了绘制也更新当前位置&#xff1b; PolyPolyline&#xff0c;绘制多条折线&#xff0c;第一个参数是点数组&#xff0c;第二个参数是一个数组、指…

论文阅读-One for All : 动态多租户边缘云平台的统一工作负载预测

论文名称&#xff1a;One for All: Unified Workload Prediction for Dynamic Multi-tenant Edge Cloud Platforms 摘要 多租户边缘云平台中的工作负载预测对于高效的应用部署和资源供给至关重要。然而&#xff0c;在多租户边缘云平台中&#xff0c;异构的应用模式、可变的基…

HTTP的基本格式

HTTP的基本格式 .HTTPhttp的协议格式HTTPhttp的协议格式 . HTTP 应用层,一方面是需要自定义协议,一方面也会用到一些现成的协议. HTTP协议,就是最常用到的应用层协议. 使用浏览器,打开网站,使用手机app,加载数据,这些过程大概率都是HTTP来支持的 HTTP是一个超文本传输协议, 文…