Java 中文官方教程 2022 版(九)(4)

简介: Java 中文官方教程 2022 版(九)

Java 中文官方教程 2022 版(九)(3)https://developer.aliyun.com/article/1486344

生产者线程,在Producer中定义,发送一系列熟悉的消息。字符串"DONE"表示所有消息都已发送。为了模拟真实应用程序的不可预测性,生产者线程在消息之间暂停一段随机时间。

import java.util.Random;
public class Producer implements Runnable {
    private Drop drop;
    public Producer(Drop drop) {
        this.drop = drop;
    }
    public void run() {
        String importantInfo[] = {
            "Mares eat oats",
            "Does eat oats",
            "Little lambs eat ivy",
            "A kid will eat ivy too"
        };
        Random random = new Random();
        for (int i = 0;
             i < importantInfo.length;
             i++) {
            drop.put(importantInfo[i]);
            try {
                Thread.sleep(random.nextInt(5000));
            } catch (InterruptedException e) {}
        }
        drop.put("DONE");
    }
}

消费者线程,在Consumer中定义,简单地检索消息并打印出来,直到检索到"DONE"字符串为止。该线程还会暂停一段随机时间。

import java.util.Random;
public class Consumer implements Runnable {
    private Drop drop;
    public Consumer(Drop drop) {
        this.drop = drop;
    }
    public void run() {
        Random random = new Random();
        for (String message = drop.take();
             ! message.equals("DONE");
             message = drop.take()) {
            System.out.format("MESSAGE RECEIVED: %s%n", message);
            try {
                Thread.sleep(random.nextInt(5000));
            } catch (InterruptedException e) {}
        }
    }
}

最后,这是主线程,在ProducerConsumerExample中定义,启动生产者和消费者线程。

public class ProducerConsumerExample {
    public static void main(String[] args) {
        Drop drop = new Drop();
        (new Thread(new Producer(drop))).start();
        (new Thread(new Consumer(drop))).start();
    }
}

注意: Drop 类是为了演示受保护的代码块而编写的。在尝试编写自己的数据共享对象之前,请查看 Java 集合框架中的现有数据结构,以避免重复造轮子。有关更多信息,请参考问题和练习部分。


不可变对象

原文:docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html

如果一个对象在构造后其状态不能改变,则被认为是不可变的。广泛接受的一种创建简单可靠代码的策略是最大程度地依赖不可变对象。

不可变对象在并发应用程序中特别有用。由于它们不能改变状态,因此它们不会受到线程干扰的破坏,也不会以不一致的状态被观察到。

程序员通常不愿使用不可变对象,因为他们担心创建一个新对象的成本,而不是就地更新对象。对象创建的影响经常被高估,可以通过一些与不可变对象相关的效率来抵消。这些效率包括由于垃圾回收而减少的开销,以及消除了为了保护可变对象免受破坏而需要的代码。

以下小节以一个实例是可变的类为例,并从中派生出一个实例是不可变的类。这样做,它们给出了这种转换的一般规则,并展示了不可变对象的一些优势。

一个同步类的示例

原文:docs.oracle.com/javase/tutorial/essential/concurrency/syncrgb.html

这个类,SynchronizedRGB,定义了代表颜色的对象。每个对象将颜色表示为三个代表主要颜色值的整数和一个给出颜色名称的字符串。

public class SynchronizedRGB {
    // Values must be between 0 and 255.
    private int red;
    private int green;
    private int blue;
    private String name;
    private void check(int red,
                       int green,
                       int blue) {
        if (red < 0 || red > 255
            || green < 0 || green > 255
            || blue < 0 || blue > 255) {
            throw new IllegalArgumentException();
        }
    }
    public SynchronizedRGB(int red,
                           int green,
                           int blue,
                           String name) {
        check(red, green, blue);
        this.red = red;
        this.green = green;
        this.blue = blue;
        this.name = name;
    }
    public void set(int red,
                    int green,
                    int blue,
                    String name) {
        check(red, green, blue);
        synchronized (this) {
            this.red = red;
            this.green = green;
            this.blue = blue;
            this.name = name;
        }
    }
    public synchronized int getRGB() {
        return ((red << 16) | (green << 8) | blue);
    }
    public synchronized String getName() {
        return name;
    }
    public synchronized void invert() {
        red = 255 - red;
        green = 255 - green;
        blue = 255 - blue;
        name = "Inverse of " + name;
    }
}

必须小心使用SynchronizedRGB,以避免出现不一致的状态。例如,假设一个线程执行以下代码:

SynchronizedRGB color =
    new SynchronizedRGB(0, 0, 0, "Pitch Black");
...
int myColorInt = color.getRGB();      //Statement 1
String myColorName = color.getName(); //Statement 2

如果另一个线程在语句 1 之后但在语句 2 之前调用color.setmyColorInt的值将不匹配myColorName的值。为了避免这种结果,这两个语句必须绑定在一起:

synchronized (color) {
    int myColorInt = color.getRGB();
    String myColorName = color.getName();
} 

这种不一致性只对可变对象有效 — 对于不可变版本的SynchronizedRGB不会有问题。

定义不可变对象的策略

原文:docs.oracle.com/javase/tutorial/essential/concurrency/imstrat.html

以下规则定义了创建不可变对象的简单策略。并非所有被记录为“不可变”的类都遵循这些规则。这并不一定意味着这些类的创建者粗心大意 — 他们可能有充分的理由相信他们的类的实例在构造后永远不会改变。然而,这种策略需要复杂的分析,不适合初学者。

  1. 不提供“setter”方法 — 修改字段或字段引用的对象的方法。
  2. 使所有字段都是finalprivate
  3. 不允许子类重写方法。这样做的最简单方法是将类声明为final。更复杂的方法是将构造函数设为private,并在工厂方法中构造实例。
  4. 如果实例字段包括对可变对象的引用,请不要允许更改这些对象:
  • 不要提供修改可变对象的方法。
  • 不共享对可变对象的引用。永远不要存储传递给构造函数的外部可变对象的引用;如果必要,创建副本,并存储对副本的引用。类似地,在必要时创建内部可变对象的副本,以避免在方法中返回原始对象。

将这种策略应用于SynchronizedRGB会产生以下步骤:

  1. 这个类中有两个 setter 方法。第一个set方法任意地转换对象,并且在类的不可变版本中没有位置。第二个invert方法可以通过创建一个新对象来适应,而不是修改现有对象。
  2. 所有字段已经是private;它们进一步被标记为final
  3. 类本身被声明为final
  4. 只有一个字段引用一个对象,而该对象本身是不可变的。因此,不需要防止改变“包含”可变对象状态的保护措施。

在这些更改之后,我们有了ImmutableRGB

final public class ImmutableRGB {
    // Values must be between 0 and 255.
    final private int red;
    final private int green;
    final private int blue;
    final private String name;
    private void check(int red,
                       int green,
                       int blue) {
        if (red < 0 || red > 255
            || green < 0 || green > 255
            || blue < 0 || blue > 255) {
            throw new IllegalArgumentException();
        }
    }
    public ImmutableRGB(int red,
                        int green,
                        int blue,
                        String name) {
        check(red, green, blue);
        this.red = red;
        this.green = green;
        this.blue = blue;
        this.name = name;
    }
    public int getRGB() {
        return ((red << 16) | (green << 8) | blue);
    }
    public String getName() {
        return name;
    }
    public ImmutableRGB invert() {
        return new ImmutableRGB(255 - red,
                       255 - green,
                       255 - blue,
                       "Inverse of " + name);
    }
}

高级并发对象

原文:docs.oracle.com/javase/tutorial/essential/concurrency/highlevel.html

到目前为止,本课程已经专注于 Java 平台从一开始就存在的低级 API。这些 API 对于非常基本的任务是足够的,但对于更高级的任务需要更高级的构建块。这对于充分利用当今的多处理器和多核系统的大规模并发应用程序尤为重要。

在本节中,我们将介绍 Java 平台 5.0 版本引入的一些高级并发特性。这些特性大多数都是在新的java.util.concurrent包中实现的。Java 集合框架中还有新的并发数据结构。

  • 锁对象支持简化许多并发应用程序的锁定习语。
  • 执行器定义了一个用于启动和管理线程的高级 API。java.util.concurrent提供的执行器实现提供了适用于大规模应用程序的线程池管理。
  • 并发集合使得管理大量数据集变得更加容易,并且可以大大减少同步的需求。
  • 原子变量具有最小化同步和避免内存一致性错误的特性。
  • ThreadLocalRandom(在 JDK 7 中)提供了多线程有效生成伪随机数的功能。

锁对象

原文:docs.oracle.com/javase/tutorial/essential/concurrency/newlocks.html

同步代码依赖于一种简单的可重入锁。这种类型的锁易于使用,但有许多限制。更复杂的锁习语由 java.util.concurrent.locks 包支持。我们不会详细讨论此包,而是专注于其最基本的接口 Lock

Lock 对象的工作方式与同步代码中使用的隐式锁非常相似。与隐式锁一样,一次只有一个线程可以拥有 Lock 对象。Lock 对象还支持通过其关联的 Condition 对象实现 wait/notify 机制。

Lock 对象相对于隐式锁的最大优势在于其能够在尝试获取锁时撤销操作。如果指定了超时时间,tryLock 方法在锁不可用时或超时之前会撤销操作。lockInterruptibly 方法在获取锁之前如果另一个线程发送中断信号,则会撤销操作。

让我们使用 Lock 对象来解决我们在 Liveness 中看到的死锁问题。阿方索和加斯顿已经训练自己注意到朋友即将鞠躬的时刻。我们通过要求我们的 Friend 对象必须在继续鞠躬之前为两个参与者获取锁来模拟这种改进。这是改进模型的源代码,Safelock。为了展示这种习语的多功能性,我们假设阿方索和加斯顿如此迷恋他们新发现的安全鞠躬能力,以至于他们无法停止向彼此鞠躬:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.Random;
public class Safelock {
    static class Friend {
        private final String name;
        private final Lock lock = new ReentrantLock();
        public Friend(String name) {
            this.name = name;
        }
        public String getName() {
            return this.name;
        }
        public boolean impendingBow(Friend bower) {
            Boolean myLock = false;
            Boolean yourLock = false;
            try {
                myLock = lock.tryLock();
                yourLock = bower.lock.tryLock();
            } finally {
                if (! (myLock && yourLock)) {
                    if (myLock) {
                        lock.unlock();
                    }
                    if (yourLock) {
                        bower.lock.unlock();
                    }
                }
            }
            return myLock && yourLock;
        }
        public void bow(Friend bower) {
            if (impendingBow(bower)) {
                try {
                    System.out.format("%s: %s has"
                        + " bowed to me!%n", 
                        this.name, bower.getName());
                    bower.bowBack(this);
                } finally {
                    lock.unlock();
                    bower.lock.unlock();
                }
            } else {
                System.out.format("%s: %s started"
                    + " to bow to me, but saw that"
                    + " I was already bowing to"
                    + " him.%n",
                    this.name, bower.getName());
            }
        }
        public void bowBack(Friend bower) {
            System.out.format("%s: %s has" +
                " bowed back to me!%n",
                this.name, bower.getName());
        }
    }
    static class BowLoop implements Runnable {
        private Friend bower;
        private Friend bowee;
        public BowLoop(Friend bower, Friend bowee) {
            this.bower = bower;
            this.bowee = bowee;
        }
        public void run() {
            Random random = new Random();
            for (;;) {
                try {
                    Thread.sleep(random.nextInt(10));
                } catch (InterruptedException e) {}
                bowee.bow(bower);
            }
        }
    }
    public static void main(String[] args) {
        final Friend alphonse =
            new Friend("Alphonse");
        final Friend gaston =
            new Friend("Gaston");
        new Thread(new BowLoop(alphonse, gaston)).start();
        new Thread(new BowLoop(gaston, alphonse)).start();
    }
}


相关文章
|
9天前
|
Java 测试技术 Python
《手把手教你》系列技巧篇(二十九)-java+ selenium自动化测试- Actions的相关操作上篇(详解教程)
【4月更文挑战第21天】本文介绍了Selenium中处理特殊测试场景的方法,如鼠标悬停。Selenium的Actions类提供了鼠标悬停功能,用于模拟用户在网页元素上的悬停行为。文中通过实例展示了如何使用Actions悬停并展开下拉菜单,以及在搜索时选择自动补全的字段。代码示例包括了打开百度首页,悬停在“更多”元素上显示下拉菜单并点击“音乐”,以及在搜索框输入关键词并自动补全的过程。
33 0
|
2天前
|
Java 测试技术 Python
《手把手教你》系列技巧篇(三十六)-java+ selenium自动化测试-单选和多选按钮操作-番外篇(详解教程)
【4月更文挑战第28天】本文简要介绍了自动化测试的实战应用,通过一个在线问卷调查(&lt;https://www.sojump.com/m/2792226.aspx/&gt;)为例,展示了如何遍历并点击问卷中的选项。测试思路包括找到单选和多选按钮的共性以定位元素,然后使用for循环进行点击操作。代码设计方面,提供了Java+Selenium的示例代码,通过WebDriver实现自动答题。运行代码后,可以看到控制台输出和浏览器的相应动作。文章最后做了简单的小结,强调了本次实践是对之前单选多选操作的巩固。
10 0
|
2天前
|
Java 测试技术 项目管理
Java基础教程(22)-构建工具Maven的基本使用
【4月更文挑战第22天】Maven是Java项目管理及构建工具,简化构建、测试、打包和部署等任务。遵循约定优于配置原则,核心是`pom.xml`配置文件,用于管理依赖和项目信息。安装涉及下载、解压、配置环境变量。在IDEA中使用Maven创建项目,通过`pom.xml`添加依赖和管理版本。常用命令包括`clean`、`compile`、`test`、`package`、`install`和`deploy`。IDEA支持直接执行这些命令。
|
2天前
|
NoSQL Java 关系型数据库
Java基础教程(21)-Java连接MongoDB
【4月更文挑战第21天】MongoDB是开源的NoSQL数据库,强调高性能和灵活性。Java应用通过MongoDB Java驱动与之交互,涉及MongoClient、MongoDatabase、MongoCollection和Document等组件。连接MongoDB的步骤包括:配置连接字符串、创建MongoClient、选择数据库和集合。伪代码示例展示了如何建立连接、插入和查询数据。
|
3天前
|
存储 前端开发 测试技术
《手把手教你》系列技巧篇(三十五)-java+ selenium自动化测试-单选和多选按钮操作-下篇(详解教程)
【4月更文挑战第27天】本文介绍了使用Java+Selenium进行Web自动化测试时,如何遍历和操作多选按钮的方法。文章分为两个部分,首先是一个本地HTML页面的示例,展示了多选按钮的HTML代码和页面效果,并详细解释了遍历多选按钮的思路:找到所有多选按钮的共同点,通过定位这些元素并放入list容器中,然后使用for循环遍历并操作。 第二部分介绍了在JQueryUI网站上的实战,给出了被测网址,展示了代码设计,同样使用了findElements()方法获取所有多选按钮并存储到list中,然后遍历并进行点击操作。最后,文章对整个过程进行了小结,并推荐了作者的其他自动化测试教程资源。
11 0
|
3天前
|
Java 关系型数据库 MySQL
Java基础教程(20)-Java连接mysql数据库CURD
【4月更文挑战第19天】MySQL是流行的关系型数据库管理系统,支持SQL语法。在IDEA中加载jar包到项目类路径:右击项目,选择“Open Module Settings”,添加库文件。使用JDBC连接MySQL,首先下载JDBC驱动,然后通过`Class.forName()`加载驱动,`DriverManager.getConnection()`建立连接。执行CRUD操作,例如创建表、插入数据和查询,使用`Statement`或`PreparedStatement`,并确保正确关闭数据库资源。
|
4天前
|
设计模式 算法 Java
Java基础教程(19)-设计模式简述
【4月更文挑战第19天】设计模式是软件设计中反复使用的代码设计经验,旨在提升代码的可重用性、可扩展性和可维护性。23种模式分为创建型、结构型和行为型三类。创建型模式如工厂方法、抽象工厂、建造者、原型和单例,关注对象创建与使用的分离。结构型模式涉及对象组合,如适配器、装饰器、外观等,增强结构灵活性。行为型模式专注于对象间职责分配和算法合作,包括责任链、命令、观察者等。设计模式提供标准化解决方案,促进代码交流和复用。
|
5天前
|
前端开发 测试技术 Python
《手把手教你》系列技巧篇(三十三)-java+ selenium自动化测试-单选和多选按钮操作-上篇(详解教程)
【4月更文挑战第25天】本文介绍了自动化测试中如何处理单选和多选按钮的操作,包括它们的定义、HTML代码示例以及如何判断和操作这些元素。文章通过一个简单的HTML页面展示了单选和多选框的示例,并提供了Java+Selenium实现的代码示例,演示了如何检查单选框是否选中以及如何进行全选操作。
11 0
|
5天前
|
网络协议 Java 网络架构
Java基础教程(18)-Java中的网络编程
【4月更文挑战第18天】Java网络编程简化了底层协议处理,利用Java标准库接口进行TCP/IP通信。TCP协议提供可靠传输,常用于HTTP、SMTP等协议;UDP协议则更高效但不保证可靠性。在TCP编程中,ServerSocket用于监听客户端连接,Socket实现双进程间通信。UDP编程中,DatagramSocket处理无连接的数据报文。HTTP编程可以通过HttpURLConnection发送请求并接收响应。
|
6天前
|
前端开发 Java 测试技术
《手把手教你》系列技巧篇(三十二)-java+ selenium自动化测试-select 下拉框(详解教程)
【4月更文挑战第24天】本文介绍了在自动化测试中处理HTML下拉选择(select)的方法。使用Selenium的Select类,可以通过index、value或visible text三种方式选择选项,并提供了相应的取消选择的方法。此外,文章还提供了一个示例HTML页面(select.html)和相关代码实战,演示了如何使用Selenium进行选择和取消选择操作。最后,文章提到了现代网页中类似下拉框的新设计,如12306网站的出发地选择,并给出了相应的代码示例,展示了如何定位并选择特定选项。
16 0