JavaRush /Java 博客 /Random-ZH /喝咖啡休息#112。如何在 Java 中声明、初始化和迭代数组。如何在不使用 Thread.stop() 的情况下...

喝咖啡休息#112。如何在 Java 中声明、初始化和迭代数组。如何在不使用 Thread.stop() 的情况下停止 Java 线程?

已在 Random-ZH 群组中发布

如何在 Java 中声明、初始化和迭代数组

来源:FreeCodeCamp 在本文中,我们将讨论 Java 中的数组。我们将通过一些示例来帮助您了解数组是什么、如何声明它以及如何在 Java 代码中使用它。 喝咖啡休息#112。 如何在 Java 中声明、初始化和迭代数组。 如何在不使用 Thread.stop() 的情况下停止 Java 线程? - 1

什么是数组?

在Java中,我们使用数组在单个变量中存储相同数据类型的多个值。也可以将其视为相同数据类型的值的集合。这意味着,例如,如果您要在数组中存储字符串,那么数组中的所有值都必须是字符串。

如何在Java中声明数组

我们使用方括号[]来声明一个数组。像那样:
String[] names;
这里我们声明了一个名为名称的变量,它将保存一个字符串数组。如果我们需要声明一个整数(integers)变量,那么我们会这样做:
int[] myIntegers;
因此,要创建数组,我们指定将存储在数组中的数据类型,后跟方括号,然后是数组的名称。

Java中如何初始化数组

初始化数组就是给数组赋值。让我们初始化在上一节中声明的数组:
String[] names = {"John", "Jade", "Love", "Allen"};
int[] myIntegers = {10, 11, 12};
我们通过传递相同数据类型的值来初始化数组,并用逗号分隔每个值。如果我们想访问数组中的元素/值,我们将访问它们在数组中的索引号。第一个元素的索引是 0。下面是一个示例:
String[] names = {"John", "Jade", "Love", "Allen"};

System.out.println(names[0]);
// John

System.out.println(names[1]);
// Jade

System.out.println(names[2]);
// Love

System.out.println(names[3]);
// Allen
现在我们知道如何访问每个元素,让我们更改第三个元素的值。
String[] names = {"John", "Jade", "Love", "Allen"};
names[2] = "Victor";

System.out.println(names[2]);
// Victor
我们还可以使用length属性 检查数组的长度。这是一个例子:
String[] names = {"John", "Jade", "Love", "Allen"};
System.out.println(names.length);
// 4

如何在Java中迭代数组

我们可以使用for循环来迭代数组的元素。
String[] names = {"John", "Jade", "Love", "Allen"};
for (int i = 0; i < names.length; i++) {
  System.out.println(names[i]);
}

// John
// Jade
// Love
// Allen
上面的循环将打印数组的元素。我们使用length属性来指定循环应运行的次数。

结论

在本文中,我们学习了如何在 Java 代码中声明和初始化数组。我们还了解了如何访问数组的每个元素以及如何循环这些元素。快乐编码!

如何在不使用 Thread.stop() 的情况下停止 Java 线程?

来源:4compressive如果您正在阅读本文,那么您可能想知道为什么,最重要的是,如果您不能仅依赖Thread#stop() 方法(该方法自 Java 1.2 以来已被弃用) ,如何停止线程? 喝咖啡休息#112。 如何在 Java 中声明、初始化和迭代数组。 如何在不使用 Thread.stop() 的情况下停止 Java 线程? - 2简而言之,您应该使用中断,因为Thread#stop是不安全的。但如果您想了解令人讨厌的InterruptedException 的原因、方式和用途,请继续阅读。

Thread#stop() 的问题

无论听起来多么直观,中断已经运行的线程、启动和停止是两种完全不同的情况。为什么?当我们启动一个新线程时,我们是从头开始,这是一个明确定义的状态,但是当我们尝试停止一个现有线程时,它可能发生在一个无法立即中断的操作中间。想象一个线程对线程安全的数据结构进行更改。当它处于关键部分的中间时......然后线程神奇地消失 - 锁被释放,现在另一个线程可以观察处于不一致状态的数据结构。考虑以下具有某些内部状态的线程安全计数器的示例:
class ThreadSafeCounter {

    private volatile int counter = 0;
    private volatile boolean dirty = false;

    public synchronized int incrementAndGet() {
        if (dirty) {
            throw new IllegalStateException("this should never happen");
        }
        dirty = true;
        // ...
        Thread.sleep(5000); // boilerplate not included
        // ...
        counter = counter + 1;
        dirty = false;
        return counter;
    }
}
正如您所看到的,getAndIncrement()方法增加计数器并更改标志。锁确保每次线程进入getAndIncrement()时,标志都会设置为false。现在让我们创建一个线程并突然停止它:
var threadSafeCounter = new ThreadSafeCounter();

var t1 = new Thread(() -> {
    while (true) {
        threadSafeCounter.incrementAndGet();
    }
});

t1.start();
Thread.sleep(500);
t1.stop();

threadSafeCounter.incrementAndGet();

// Exception in thread "main" java.lang.IllegalStateException: this should never happen
现在,尽管正确实现了threadSafeCounter实例,但由于线程突然停止而永久损坏。如何修复它?

协作线程中断

我们不应该停止线程,而应该依靠协作线程中断。简而言之,我们必须使用Thread#interrupt要求线程在正确的时刻停止自身。然而,调用Thread#interrupt而不是Thread#stop还不够。线程的安全终止只能通过使用共享线程中断来实现,这意味着在线程内我们需要处理中断信号,中断信号有两种类型:
  • 布尔中断标志

  • 中断异常

可中断标志处理

在线程内部时,我们可以通过调用以下方法之一来检查它是否已被中断:
Thread.currentThread().isInterrupted(); // reads the interrupted flag
Thread.interrupted(); // reads and resets the interrupted flag
由于没有通用的方法来处理中断,因此我们需要应用适合我们上下文的操作。在上一节的示例中,线程在无限循环中递增计数器。我们可以简单地等待线程完成其上升工作,然后退出循环,而不是立即停止:
var threadSafeCounter = new ThreadSafeCounter();

var t1 = new Thread(() -> {
    while (!Thread.interrupted()) {
        threadSafeCounter.incrementAndGet();
    }
});

t1.start();
Thread.sleep(500);
t1.interrupt();

threadSafeCounter.incrementAndGet();
现在,线程在正确的时间安全终止,而不会造成混乱。

处理中断异常

如果我们尝试中断正在等待的线程会发生什么?当然,我们不能考虑中断标志,因为线程正忙于阻塞操作。这就是InterruptedException存在的原因。这不是标准异常,而是一种不同形式的中断信号,仅用于处理阻塞中断!与Thread#interrupted一样,它会重置中断标志。让我们再次更改上面的示例,并在每次增量之前引入暂停。现在我们需要处理Thread#sleep抛出的InterruptedException
var threadSafeCounter = new ThreadSafeCounter();

var t1 = new Thread(() -> {
    while (!Thread.interrupted()) {
        threadSafeCounter.incrementAndGet();
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            break;
        }
    }
});

t1.start();
Thread.sleep(500);
t1.interrupt();

assertThat(threadSafeCounter.incrementAndGet()).isGreaterThan(0);
在我们的例子中,只需退出循环就足够了。但是,如果您认为不应该由某个特定方法负责处理中断怎么办?如果方法签名无法更改怎么办?在这种情况下,我们需要恢复中断标志和/或重新打包异常,例如:
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
}
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    throw new RuntimeException(e);
}

结论:

  • Thread#stop并不是无缘无故被弃用的——它实际上非常不安全。

  • 线程通过中断(由线程本身解释的信号)停止。

  • InterruptedException不是一个普通的异常 - 它是 Java 发出线程已中断信号的内置方式。

  • Thread.currentThread().isInterrupted()Thread.interrupted()不是一回事。第一个只是读取中断标志,另一个随后将其重置。

  • 处理中断信号没有通用的方法——“这取决于具体情况。”

评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION