提交 e8a0708b 编写于 作者: W wizardforcel

2020-05-23 11:42:50

上级 234ae965
......@@ -2,9 +2,9 @@
> 原文: [https://howtodoinjava.com/java/multi-threading/java-synchronized/](https://howtodoinjava.com/java/multi-threading/java-synchronized/)
**Java 同步关键字**将块或方法标记为关键部分。 关键部分是一次仅执行一个线程的线程,该线程持有同步部分的锁。
**Java 同步关键字**将块或方法标记为临界区。 临界区是一次仅执行一个线程的线程,该线程持有同步部分的锁。
**同步的**关键字有助于编写应用程序的[并发](https://howtodoinjava.com/java-concurrency-tutorial/)部分,以保护此块中的共享资源。
`synchronized`关键字有助于编写应用程序的[并发](https://howtodoinjava.com/java-concurrency-tutorial/)部分,以保护此块中的共享资源。
`synchronized`关键字可以与:
......@@ -15,7 +15,7 @@
#### 1.1 语法
编写同步块的一般语法如下。 此处 **lockObject** 是对对象的引用,该对象的锁与同步语句表示的监视器关联。
编写同步块的一般语法如下。 此处 `lockObject`是对对象的引用,该对象的锁与同步语句表示的监视器关联。
```java
synchronized( lockObject )
......@@ -29,17 +29,17 @@ synchronized( lockObject )
当线程要在同步块内执行同步语句时,必须获得`lockObject`监视器的锁定。 一次,只有一个线程可以获取锁对象的监视器。 因此,所有其他线程必须等到当前已获得锁的该线程完成执行。
通过这种方式,synchronized 关键字可确保一次仅一个线程将执行同步的块语句,从而防止多个线程破坏该块内部的共享数据。
通过这种方式,`synchronized`关键字可确保一次仅一个线程将执行同步的块语句,从而防止多个线程破坏该块内部的共享数据。
请记住,如果一个线程进入睡眠状态(使用`sleep()`方法),则它不会释放锁。 在此休眠时间,没有线程将执行同步的块语句。
如果`'synchronized (lock)'`中使用的锁定对象为`null`,则 Java 同步将抛出 **NullPointerException**
如果`'synchronized (lock)'`中使用的锁定对象为`null`,则 Java 同步将抛出`NullPointerException`
#### 1.3 Java 同步块示例
Java 程序演示同步块的用法。 在给定的示例中,我们具有使用方法`printNumbers()``MathClass`。 此方法将打印从 1 到参数 N 的数字。
请注意,printNumbers()方法中的代码在同步块内部。
请注意,`printNumbers()`方法中的代码在同步块内部。
```java
public class MathClass
......@@ -105,7 +105,7 @@ TWO :: 3
#### 2.1 语法
编写同步方法的一般语法如下。 此处 **lockObject** 是对对象的引用,该对象的锁与同步语句表示的监视器关联。
编写同步方法的一般语法如下。 此处`lockObject`是对对象的引用,该对象的锁与同步语句表示的监视器关联。
```java
<access modifier> synchronized method( parameters )
......@@ -119,8 +119,8 @@ TWO :: 3
与同步块类似,线程必须使用同步方法来获取关联的监视对象上的锁。 如果采用同步方法,则锁定对象为:
* **'.class'对象**-如果该方法是静态的。
* **'此'对象**-如果方法不是静态的。 “ this”是指在其中调用同步方法的当前对象的引用。
* `.class`对象 - 如果该方法是静态的。
* `this`对象 - 如果方法不是静态的。 `this`是指在其中调用同步方法的当前对象的引用。
> 阅读更多: [Java 中的对象级别锁与类级别锁](https://howtodoinjava.com/java/multi-threading/object-vs-class-level-locking/)
......@@ -128,7 +128,7 @@ Java 同步关键字本质上是**重入符**,这意味着如果同步方法
#### 2.3 Java 同步方法示例
与同步块示例类似,我们可以在`printNumber()`方法上应用 synced 关键字,这将使该方法成为同步方法。 现在,如果再次运行示例,我们将获得类似的输出。
与同步块示例类似,我们可以在`printNumber()`方法上应用`synchronized`关键字,这将使该方法成为同步方法。 现在,如果再次运行示例,我们将获得类似的输出。
```java
public class MathClass
......
......@@ -4,7 +4,7 @@
在 Java 中,**同步的**代码块一次只能由一个线程执行。 另外,java 支持同时执行多个线程。 这可能会导致两个或多个线程同时访问相同的字段或对象。
同步是使所有并发线程在执行中保持同步的过程。 同步避免了由于共享内存视图不一致而导致的内存一致性错误。 当方法声明为**同步**时,该线程持有该方法对象的监视器或 **[锁定](https://howtodoinjava.com/java/multi-threading/how-to-use-locks-in-java-java-util-concurrent-locks-lock-tutorial-and-example/)对象**。 如果另一个线程正在执行*同步的*方法,则该线程将被阻塞,直到该线程释放监视器。
同步是使所有并发线程在执行中保持同步的过程。 同步避免了由于共享内存视图不一致而导致的内存一致性错误。 当方法声明为**同步**时,该线程持有该方法对象的监视器或[**锁定**](https://howtodoinjava.com/java/multi-threading/how-to-use-locks-in-java-java-util-concurrent-locks-lock-tutorial-and-example/)对象。 如果另一个线程正在执行*同步的*方法,则该线程将被阻塞,直到该线程释放监视器。
请注意,我们可以在类中的已定义方法或块上使用`synchronized`关键字。 `synchronized`关键字不能与类定义中的变量或属性一起使用。
......@@ -105,7 +105,7 @@ public class DemoClass
7. 静态同步方法和非静态同步方法都可能同时或同时运行,因为它们锁定在不同的对象上。
8. 根据 Java 语言规范,不能将`synchronized`关键字与构造器一起使用。 这是非法的,并导致编译错误。
9. 不要在 Java 中的同步块上的非 final 字段上进行同步。 因为对非 final 字段的引用可能随时更改,然后不同的线程可能会在不同的对象上进行同步,即完全没有同步。
10. 不要使用 String 字面值,因为它们可能在应用程序中的其他地方被引用,并且可能导致死锁。 使用`new`关键字创建的字符串对象可以安全使用。 但是,作为最佳实践,请在我们要保护的共享变量本身上创建一个新的**私有**范围内的`Object`实例 OR 锁。 [感谢 Anu 在评论中指出这一点。]
10. 不要使用 String 字面值,因为它们可能在应用程序中的其他地方被引用,并且可能导致死锁。 使用`new`关键字创建的字符串对象可以安全使用。 但是,作为最佳实践,请在我们要保护的共享变量本身上创建一个新的**私有**范围内的`Object`实例 OR 锁。(感谢 Anu 在评论中指出这一点。)
让我知道有关 Java 中对象级别锁与类级别锁的想法和查询。
......
......@@ -2,15 +2,15 @@
> 原文: [https://howtodoinjava.com/java/multi-threading/java-runnable-vs-thread/](https://howtodoinjava.com/java/multi-threading/java-runnable-vs-thread/)
众所周知,在 Java 语言中,有两种创建线程的方法。 一种使用[可运行的](https://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html)接口,另一种通过扩展[线程](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html)类。 让我们确定两种方式之间的区别,即*扩展线程并实现可运行的*
众所周知,在 Java 语言中,有两种创建线程的方法。 一种使用[`Runnable`](https://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html)接口,另一种通过扩展[线程](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html)类。 让我们确定两种方式之间的区别,即*扩展线程和实现`Runnable`*
## 1\. 使用 Runnable Interface vs Thread 类创建线程
## 1\. 使用`Runnable`接口 vs `Thread`类创建线程
让我们快速检查一下这两种技术的 Java 代码。
#### 1.1 可运行的接口
#### 1.1 `Runnable`的接口
通过实现 **Runnable** 接口来创建线程的 Java 程序。
通过实现`Runnable`接口来创建线程的 Java 程序。
```java
public class DemoRunnable implements Runnable {
......@@ -40,7 +40,7 @@ public class DemoThread extends Thread {
```
## 2\. Runnable 与 Thread 之间的区别
## 2\. `Runnable`与`Thread`之间的区别
关于哪种更好的方法已经有很多争论。 好吧,我也试图找出答案,下面是我的学习内容。
......@@ -48,14 +48,14 @@ public class DemoThread extends Thread {
2. Java 仅支持单一继承,因此您只能扩展一个类。
3. 实例化一个接口可以使您的代码与线程实现更加清晰地分离。
4. 实现`Runnable`使您的类更加灵活。 如果您扩展`Thread`,那么您执行的操作将始终处于线程状态。 但是,如果您实现了`Runnable`,则不必这样做。 您可以在线程中运行它,或将其传递给某种执行服务,或仅作为任务在单个线程应用程序中传递。
5. If you are working on JDK 4 or lesser, then there is bug :
5. 如果您使用的是 JDK 4 或更低版本,则存在错误:
[http://bugs.java.com/bugdatabase/view_bug.do;jsessionid=5869e03fee226ffffffffc40d4fa881a86e3:WuuT?bug_id=4533087](http://bugs.java.com/bugdatabase/view_bug.do;jsessionid=5869e03fee226ffffffffc40d4fa881a86e3:WuuT?bug_id=4533087 terget=)
[http://bugs.java.com/bugdatabase/view_bug.do;jsessionid=5869e03fee226ffffffffc40d4fa881a86e3:WuuT?bug_id=4533087](http://bugs.java.com/bugdatabase/view_bug.do;jsessionid=5869e03fee226ffffffffc40d4fa881a86e3:WuuT?bug_id=4533087)
它已在 Java 1.5 中修复,但 Sun 并不打算在 1.4 中修复它。
问题在于,在构造时,`Thread`被添加到内部线程表中的引用列表中。 在`start()`方法完成之前,它不会从该列表中删除。 只要存在该引用,就不会收集垃圾。
这就是 Java 中`Runnable`接口和`Thread`类之间**区别的全部。 如果您知道更多信息,请在评论部分中添加,然后将其包含在帖子内容中。**
这就是 Java 中`Runnable`接口和`Thread`类之间**区别**的全部。 如果您知道更多信息,请在评论部分中添加,然后将其包含在帖子内容中。
学习愉快!
\ No newline at end of file
# 如何在 Java 中使用 wait(),notify()和 notifyAll()
# 如何在 Java 中使用`wait()`,`notify()`和`notifyAll()`
> 原文: [https://howtodoinjava.com/java/multi-threading/wait-notify-and-notifyall-methods/](https://howtodoinjava.com/java/multi-threading/wait-notify-and-notifyall-methods/)
[**Java 并发**](https://howtodoinjava.com/java-concurrency-tutorial/ "multithreading in java") 是一个非常复杂的主题,在编写处理在任何给定时间访问一个/多个共享资源的多个线程的应用程序代码时,需要引起很多关注。 Java 5 引入了诸如 [**BlockingQueue**](https://howtodoinjava.com/java/multi-threading/how-to-use-blockingqueue-and-threadpoolexecutor-in-java/ "How to use BlockingQueue and ThreadPoolExecutor in java")**Executors** 之类的类,它们通过提供易于使用的 API 消除了一些复杂性。
[**Java 并发**](https://howtodoinjava.com/java-concurrency-tutorial/ "multithreading in java")是一个非常复杂的主题,在编写处理在任何给定时间访问一个/多个共享资源的多个线程的应用程序代码时,需要引起很多关注。 Java 5 引入了诸如[**`BlockingQueue`**](https://howtodoinjava.com/java/multi-threading/how-to-use-blockingqueue-and-threadpoolexecutor-in-java/ "How to use BlockingQueue and ThreadPoolExecutor in java")`Executors`之类的类,它们通过提供易于使用的 API 消除了一些复杂性。
使用并发类的程序员会比使用 **wait()****notify()****notifyAll()**方法调用直接处理同步内容的程序员更有信心。 我还将建议您自己通过同步使用这些较新的 API,但是由于种种原因,我们经常需要这样做,例如维护旧版代码。 掌握这些方法的丰富知识将在您到达这种情况时为您提供帮助。
使用并发类的程序员会比使用`wait()``notify()``notifyAll()`方法调用直接处理同步内容的程序员更有信心。 我还将建议您自己通过同步使用这些较新的 API,但是由于种种原因,我们经常需要这样做,例如维护旧版代码。 掌握这些方法的丰富知识将在您到达这种情况时为您提供帮助。
在本教程中,我将讨论 Java 中 wait()notify()notifyall()的**用途。 我们将了解 wait 和 notify** 之间的**区别。**
在本教程中,我将讨论 Java 中`wait() notify() notifyall()`的用途。 我们将了解`wait``notify`之间的**区别**
> 阅读更多: [Java 中的 wait()和 sleep()之间的区别](https://howtodoinjava.com/java/multi-threading/difference-between-sleep-and-wait/)
> 阅读更多: [Java 中的`wait()`和`sleep()`之间的区别](https://howtodoinjava.com/java/multi-threading/difference-between-sleep-and-wait/)
## 1\. 什么是 wait(),notify()和 notifyAll()方法?
## 1\. 什么是`wait()`,`notify()`和`notifyAll()`方法?
Java 中的`Object`类具有三个最终方法,这些方法允许线程就资源的锁定状态进行通信。
1. #### 等待()
1. #### `wait()`
它告诉调用线程放弃锁定并进入睡眠状态,直到其他线程进入同一监视器并调用`notify()`为止。 `wait()`方法在等待之前释放锁,并在从`wait()`方法返回之前重新获取锁。 实际上,`wait()`方法与同步锁紧密集成在一起,使用的功能无法直接从同步机制获得。
......@@ -35,7 +35,7 @@ Java 中的`Object`类具有三个最终方法,这些方法允许线程就资
```
2. #### 通知()
2. #### `notify()`
它唤醒一个在同一对象上调用`wait()`的单个线程。 应该注意的是,调用`notify()`实际上并没有放弃对资源的锁定。 它告诉等待的线程该线程可以唤醒。 但是,直到通知者的同步块完成后才真正放弃锁定。
......@@ -55,11 +55,11 @@ Java 中的`Object`类具有三个最终方法,这些方法允许线程就资
```
3. #### notifyAll()
3. #### `notifyAll()`
它将唤醒在同一对象上调用`wait()`的所有线程。 尽管没有保证,但是在大多数情况下,优先级最高的线程将首先运行。 其他与上述`notify()`方法相同。
General syntax for calling `notify()` method is like this:
调用`notify()`方法的一般语法如下:
```java
synchronized(lockObject)
......@@ -71,19 +71,19 @@ Java 中的`Object`类具有三个最终方法,这些方法允许线程就资
```
In general, a thread that uses the `wait()` method confirms that a condition does not exist (typically by checking a variable) and then calls the `wait()` method. When another thread establishes the condition (typically by setting the same variable), it calls the `notify()` method. The wait-and-notify mechanism does not specify what the specific condition/ variable value is. It is on developer’s hand to specify the condition to be checked before calling `wait()` or `notify()`.
通常,使用`wait()`方法的线程会确认条件不存在(通常通过检查变量),然后调用`wait()`方法。 当另一个线程建立条件(通常通过设置相同的变量)时,它将调用`notify()`方法。 等待通知机制未指定特定条件/变量值是什么。 开发人员可以在调用`wait()``notify()`之前指定要检查的条件。
让我们写一个小程序来了解**应该如何使用**的 wait(),notify()和 notifyall()方法来获得理想的结果。
让我们写一个小程序来了解**应该如何使用**`wait()``notify()``notifyall()`方法来获得理想的结果。
## 2\. 如何与 wait(),notify()和 notifyAll()方法一起使用
## 2\. 如何一起使用`wait()`,`notify()`和`notifyAll()`方法
在本练习中,我们将使用`wait()``notify()`方法解决**生产者消费者问题**。 为了使程序简单并专注于`wait()``notify()`方法的使用,我们将只涉及一个生产者和一个消费者线程。
该程序的其他功能包括:
* 生产者线程每 1 秒钟产生一个新资源,并将其放入“ taskQueue”中。
* 使用者线程需要 1 秒钟来处理“ taskQueue”中消耗的资源。
* taskQueue 的最大容量为 5,即在任何给定时间,“ taskQueue”中最多可以存在 5 个资源。
* 生产者线程每 1 秒钟产生一个新资源,并将其放入`taskQueue`中。
* 使用者线程需要 1 秒钟来处理`taskQueue`中消耗的资源。
* taskQueue 的最大容量为 5,即在任何给定时间,`taskQueue`中最多可以存在 5 个资源。
* 两个线程都无限运行。
#### 2.1 生产者线程
......@@ -139,7 +139,7 @@ class Producer implements Runnable
```
* 此处,`produce(counter++)`代码已在无限循环内编写,以便生产者以规则的间隔保持生产元素。
* 此处,`produce(counter++)`代码已在无限循环内编写,以便生产者以规则的间隔保持生产元素。
* 我们已经按照通用准则编写了`produce()`方法代码,以编写第一部分中提到的`wait()`方法。
* `wait()`完成后,生产者在 taskQueue 中添加一个元素,并称为`notifyAll()`方法。 由于上次`wait()`方法是由使用者线程调用的(这就是生产者处于等待状态的原因),因此使用者可以获取通知。
* 收到通知后的使用者线程,如果准备按照书面逻辑使用该元素。
......@@ -193,8 +193,8 @@ class Consumer implements Runnable
```
* 此处,`consume()`”代码已在无限循环内编写,以便使用者在 taskQueue 中发现某些内容时便继续使用元素。
* 一旦`wait()`完成,使用者将删除 taskQueue 中的一个元素,并调用`notifyAll()`方法。 由于生产者线程调用了上次的 wait()方法(这就是为什么生产者处于等待状态的原因),所以生产者会收到通知。
* 此处,`consume()`代码已在无限循环内编写,以便使用者在`taskQueue`中发现某些内容时便继续使用元素。
* 一旦`wait()`完成,使用者将删除 taskQueue 中的一个元素,并调用`notifyAll()`方法。 由于生产者线程调用了上次的`wait()`方法(这就是为什么生产者处于等待状态的原因),所以生产者会收到通知。
* 获取通知后的生产者线程(如果准备按照书面逻辑生产元素)。
#### 2.3 测试生产者消费者示例
......@@ -245,15 +245,15 @@ Queue is empty Consumer is waiting , size: 0
我建议您将生产者线程和使用者线程花费的时间更改为不同的时间,并检查不同情况下的不同输出。
## 3\. 面试关于 wait(),notify()和 notifyAll()方法的问题
## 3\. 关于`wait()`,`notify()`和`notifyAll()`方法的面试问题
#### 3.1 调用 notify()并且没有线程正在等待时会发生什么?
#### 3.1 调用`notify()`并且没有线程正在等待时会发生什么?
通常,如果正确使用这些方法,则在大多数情况下并非如此。 尽管如果在没有其他线程等待时调用`notify()`方法,则`notify()`只会返回而通知将丢失。
由于*等待和通知机制*不知道它正在发送通知的条件,因此,假设没有线程正在等待,通知就不会被听到。 稍后执行 **wait()**方法的线程必须等待另一个通知发生。
由于*等待和通知机制*不知道它正在发送通知的条件,因此,假设没有线程正在等待,通知就不会被听到。 稍后执行`wait()`方法的线程必须等待另一个通知发生。
#### 3.2 在 wait()方法释放或重新获取锁的时间段内是否存在竞争条件?
#### 3.2 在`wait())`方法释放或重新获取锁的时间段内是否存在竞争条件?
`wait()`方法与锁定机制紧密集成。 在等待线程已经可以接收通知的状态之前,实际上不会释放对象锁。 这意味着仅当线程状态更改为能够接收通知时,才会保留锁定。 该系统可防止在此机制中发生任何竞赛情况。
......@@ -261,13 +261,13 @@ Queue is empty Consumer is waiting , size: 0
#### 3.3 如果线程收到通知,是否可以保证条件设置正确?
简单地说,不。 在调用`wait()`方法之前,线程应始终在保持同步锁的同时测试条件。 从`wait()`方法返回后,线程应始终重新测试条件以确定是否应该再次等待。 这是因为另一个线程也可以测试条件并确定不需要等待-处理通知线程设置的有效数据。
简单地说,不。 在调用`wait()`方法之前,线程应始终在保持同步锁的同时测试条件。 从`wait()`方法返回后,线程应始终重新测试条件以确定是否应该再次等待。 这是因为另一个线程也可以测试条件并确定不需要等待 - 处理通知线程设置的有效数据。
当通知中涉及多个线程时,这是一种常见情况。 更具体地说,正在将处理数据的线程视为使用者。 它们使用其他线程产生的数据。 无法保证当消费者收到通知时,该通知尚未被其他消费者处理。
这样,当消费者醒来时,它无法假定其等待的状态仍然有效。 它可能在过去是有效的,但是在调用`notify()`方法之后以及使用者线程唤醒之前,状态可能已经更改。 等待线程必须提供检查状态的选项,并在通知已被处理的情况下返回到等待状态。 这就是为什么我们总是在循环中放置对 wait()方法的调用的原因。
这样,当消费者醒来时,它无法假定其等待的状态仍然有效。 它可能在过去是有效的,但是在调用`notify()`方法之后以及使用者线程唤醒之前,状态可能已经更改。 等待线程必须提供检查状态的选项,并在通知已被处理的情况下返回到等待状态。 这就是为什么我们总是在循环中放置对`wait()`方法的调用的原因。
#### 3.4 当多个线程正在等待通知时会发生什么? 调用 notify()方法时,哪个线程实际获得通知?
#### 3.4 当多个线程正在等待通知时会发生什么? 调用`notify()`方法时,哪个线程实际获得通知?
这取决于许多因素。Java 规范没有定义要通知哪个线程。 在运行时中,哪个线程实际接收到通知取决于几个因素,包括 Java 虚拟机的实现以及程序执行期间的调度和计时问题。
......@@ -275,9 +275,9 @@ Queue is empty Consumer is waiting , size: 0
就像`notify()`方法一样,`notifyAll()`方法不允许我们决定哪个线程获取通知:它们都被通知了。 当所有线程都收到通知时,可以设计出一种机制,让线程在它们之间选择哪个线程应该继续,哪个线程应该再次调用`wait()`方法。
#### 3.5 notifyAll()方法是否真的唤醒所有线程?
#### 3.5 `notifyAll()`方法是否真的唤醒所有线程?
是的,没有。 所有等待的线程都被唤醒,但是它们仍然必须重新获取对象锁。 因此,线程不会并行运行:它们必须各自等待对象锁被释放。 因此,一次只能运行一个线程,并且只能在调用 notifyAll()方法的线程释放其锁之后运行。
是的,没有。 所有等待的线程都被唤醒,但是它们仍然必须重新获取对象锁。 因此,线程不会并行运行:它们必须各自等待对象锁被释放。 因此,一次只能运行一个线程,并且只能在调用`notifyAll()`方法的线程释放其锁之后运行。
#### 3.6 如果根本只执行一个线程,为什么要唤醒所有线程?
......
......@@ -2,36 +2,36 @@
> 原文: [https://howtodoinjava.com/java/multi-threading/difference-between-yield-and-join-in-threads-in-java/](https://howtodoinjava.com/java/multi-threading/difference-between-yield-and-join-in-threads-in-java/)
[**多线程**](//howtodoinjava.com/category/java/multi-threading/ "multi-threading") 从很长时间以来一直是 [**访问员**](//howtodoinjava.com/java-interview-questions/ "java interviews") 中非常受欢迎的话题。 尽管我个人觉得很少有人真正有机会在复杂的多线程应用程序上工作(*在过去的 7 年中只有一次机会*),但仍然可以帮助您方便地掌握这些概念来仅仅增强您的信心。 之前,我讨论过一个类似的问题,关于 [**wait()和 sleep()方法之间的区别**](//howtodoinjava.com/java/multi-threading/difference-between-sleep-and-wait/ "wait and sleep") ,这一次,我正在讨论 join()和 yield()方法之间的**区别[** 。 坦白地说,我在实践中并未同时使用这两种方法,因此,如果您在任何时候都感到不满意,请提出意见。
[**多线程**](//howtodoinjava.com/category/java/multi-threading/ "multi-threading")从很长时间以来一直是[**面试官**](//howtodoinjava.com/java-interview-questions/ "java interviews")中非常受欢迎的话题。 尽管我个人觉得很少有人真正有机会在复杂的多线程应用程序上工作(*在过去的 7 年中只有一次机会*),但仍然可以帮助您方便地掌握这些概念来仅仅增强您的信心。 之前,我讨论过一个类似的问题,关于[**`wait()`和`sleep()`方法之间的区别**](//howtodoinjava.com/java/multi-threading/difference-between-sleep-and-wait/ "wait and sleep"),这一次,我正在讨论`join()``yield()`方法之间的**区别** 。 坦白地说,我在实践中并未同时使用这两种方法,因此,如果您在任何时候都感到不满意,请提出意见。
## Java 线程调度的一些背景知识
需要 Java 虚拟机在其各个线程之间实现**可抢占,基于优先级的调度程序**。 这意味着为 Java 程序中的每个线程分配了一定的优先级,该优先级在明确定义的范围内。 开发人员可以更改此*优先级。 Java 虚拟机永远不会更改线程的优先级,即使该线程已经运行了一段时间。*
需要 Java 虚拟机在其各个线程之间实现**可抢占,基于优先级的调度**。 这意味着为 Java 程序中的每个线程分配了一定的优先级,该优先级在明确定义的范围内。 开发人员可以更改此*优先级。 Java 虚拟机永远不会更改线程的优先级,即使该线程已经运行了一段时间。*
优先级值很重要,因为 Java 虚拟机和底层操作系统之间的约定是 ***操作系统通常必须选择以最高优先级*** 运行 Java 线程。 这就是说 Java 实现基于优先级的 [**调度程序**](https://en.wikipedia.org/wiki/Fixed-priority_pre-emptive_scheduling "preemptive scheduling") 时的意思。 此调度程序以抢先方式实现,这意味着,当有一个优先级较低的线程正在运行时,当出现优先级较高的线程时,该线程中断( ***会抢占*** )。 但是,与操作系统的契约不是绝对的,这意味着操作系统有时可以选择运行优先级较低的线程。 [我讨厌有关多线程的问题。
优先级值很重要,因为 Java 虚拟机和底层操作系统之间的约定是**操作系统通常必须选择以最高优先级**运行 Java 线程。 这就是说 Java 实现基于优先级的[**调度器**](https://en.wikipedia.org/wiki/Fixed-priority_pre-emptive_scheduling "preemptive scheduling")时的意思。 此调度器以抢先方式实现,这意味着,当有一个优先级较低的线程正在运行时,当出现优先级较高的线程时,该线程中断(***会抢占***)。 但是,与操作系统的契约不是绝对的,这意味着操作系统有时可以选择运行优先级较低的线程。(我讨厌有关多线程的问题。)
> 还要注意, **java 并没有要求对其线程进行时间分段**,但大多数操作系统都这样做。 这里的术语经常会有一些混乱:抢占经常与时间片混淆。 实际上,**抢占仅意味着运行优先级更高的线程而不是优先级较低的线程**,但是当线程具有相同优先级时,它们不会相互抢占。 它们通常受时间限制,但这不是 Java 的要求。
## 了解线程优先级
了解线程优先级是学习多线程的下一个重要步骤,尤其是 **yield()的工作方式**。
了解线程优先级是学习多线程的下一个重要步骤,尤其是 **`yield()`的工作方式**
1. 请记住,未指定优先级时,所有线程均具有正常优先级。
2. 优先级可以在 1 到 10 之间指定。10 是最高优先级,1 是最低优先级,5 是正常优先级。
3. 请记住,优先级最高的线程将在执行时被赋予优先级。 但是不能保证它一开始就处于运行状态。
4. 与正在等待机会的池中的线程相比,当前正在执行的线程始终始终具有更高的优先级。
5. 线程调度程序决定应该执行哪个线程。
6. t.setPriority()可用于设置线程的优先级。
5. 线程调度决定应该执行哪个线程。
6. `t.setPriority()`可用于设置线程的优先级。
7. 请记住,应该在调用线程启动方法之前设置优先级。
8. 您可以使用常量 MIN_PRIORITY,MAX_PRIORITY 和 NORM_PRIORITY 设置优先级。
8. 您可以使用常量`MIN_PRIORITY``MAX_PRIORITY``NORM_PRIORITY`设置优先级。
现在,当我们对线程调度和线程优先级有了一些基本了解时,让我们进入主题。
## yield()方法
## `yield()`方法
从理论上讲,**表示“屈服”是指放弃,放弃,投降**。 一个让步线程告诉虚拟机,它愿意让其他线程在其位置进行调度。 这表明它并没有做太重要的事情。 请注意,虽然*只是提示*,但不能保证完全有效。
从理论上讲,**`yield()`是指放弃,屈服,投降**。 一个让步线程告诉虚拟机,它愿意让其他线程在其位置进行调度。 这表明它并没有做太重要的事情。 请注意,虽然*只是提示*,但不能保证完全有效。
yield()在 Thread.java 中定义如下。
`yield()``Thread.java`中定义如下。
```java
/**
......@@ -47,15 +47,15 @@ public static native void yield();
让我们从上面的定义中列出要点:
* 产量也是静态方法,也是本机方法。
* Yield 告诉当前正在执行的线程给 [**线程池**](//howtodoinjava.com/java-5/java-executor-framework-tutorial-and-best-practices/ "thread pool") 中具有相同优先级的线程一个机会。
* 无法保证 Yield 将使当前正在执行的线程立即变为可运行状态。
* `Yield`告诉当前正在执行的线程给[**线程池**](//howtodoinjava.com/java-5/java-executor-framework-tutorial-and-best-practices/ "thread pool")中具有相同优先级的线程一个机会。
* 无法保证`Yield`将使当前正在执行的线程立即变为可运行状态。
* 它只能使线程从运行状态变为可运行状态,而不能处于等待或阻塞状态。
## yield()方法示例用法
## `yield()`方法示例用法
在下面的示例程序中,我没有特定的原因创建了两个名为生产者和消费者的线程。 生产者设置为最小优先级,而消费者设置为最大优先级。 我将在有/无注释行 Thread.yield()的情况下运行以下代码。 如果没有 yield(),尽管输出有时会更改,但是通常首先打印所有使用者行,然后才打印所有生产者行。
在下面的示例程序中,我没有特定的原因创建了两个名为生产者和消费者的线程。 生产者设置为最小优先级,而消费者设置为最大优先级。 我将在有/无注释行`Thread.yield()`的情况下运行以下代码。 如果没有`yield()`,尽管输出有时会更改,但是通常首先打印所有使用者行,然后才打印所有生产者行。
使用 yield()方法,两者都一次打印一行,并且几乎总是将机会传递给另一线程。
使用`yield()`方法,两者都一次打印一行,并且几乎总是将机会传递给另一线程。
```java
package test.core.threads;
......@@ -101,7 +101,7 @@ class Consumer extends Thread
```
#### 上面程序的输出,“没有” yield()方法
#### 上面程序的输出,“没有”`yield()`方法
```java
I am Consumer : Consumed Item 0
......@@ -116,7 +116,7 @@ I am Consumer : Consumed Item 0
I am Producer : Produced Item 4
```
#### 添加了上述程序“ with” yield()方法的输出
#### 添加了`yield()`后上述程序方法的输出
```java
I am Producer : Produced Item 0
......@@ -131,9 +131,9 @@ I am Producer : Produced Item 0
I am Consumer : Consumed Item 4
```
## join()方法
## `join()`方法
可以将 Thread 实例的 join()方法用于**,以将一个线程的执行开始“联接”到另一个线程的执行**的结束,这样一个线程只有在另一个线程结束后才能开始运行。 如果在 Thread 实例上调用 join(),则当前正在运行的线程将阻塞,直到 Thread 实例完成执行为止。
可以将`Thread`实例的`join()`方法用于将一个线程的执行开始“连接”到另一个线程的执行的结束,这样一个线程只有在另一个线程结束后才能开始运行。 如果在`Thread`实例上调用`join()`,则当前正在运行的线程将阻塞,直到`Thread`实例完成执行为止。
```java
//Waits for this thread to die.
......@@ -142,11 +142,11 @@ public final void join() throws InterruptedException
```
***在 join()中设置超时,将使特定超时后的 join()效果无效。*** 当达到超时时,主线程和 taskThread 都是执行任务的同等可能性。 但是,与睡眠一样,join 的运行时间也取决于操作系统,因此,您不应假定 join 会完全按照您指定的时间等待。
***在`join()`中设置超时,将使特定超时后的`join()`效果无效***。当达到超时时,主线程和`taskThread`都是执行任务的同等可能性。 但是,与睡眠一样,`join`的运行时间也取决于操作系统,因此,您不应假定`join`会完全按照您指定的时间等待。
像睡眠一样,join 通过退出 InterruptedException 来响应中断。
像睡眠一样,`join`通过退出`InterruptedException`来响应中断。
## join()方法示例用法
## `join()`方法示例用法
```java
package test.core.threads;
......
......@@ -156,7 +156,7 @@ while(iterator.hasNext()) {
[同步 ArrayList](https://howtodoinjava.com/java/collections/arraylist/synchronize-arraylist/)
[交换 ArrayList](https://howtodoinjava.com/java/collections/arraylist/swap-two-elements-arraylist/)
[序列化 ArrayList](https://howtodoinjava.com/java/collections/arraylist/serialize-deserialize-arraylist/)
[接两个 ArrayList](https://howtodoinjava.com/java/collections/arraylist/merge-arraylists/)
[接两个 ArrayList](https://howtodoinjava.com/java/collections/arraylist/merge-arraylists/)
[清空 ArrayList](https://howtodoinjava.com/java/collections/arraylist/empty-clear-arraylist/)
[检查 ArrayList 是否为空](https://howtodoinjava.com/java/collections/arraylist/check-arraylist-empty/)
[替换 ArrayList 中现有元素的值[](https://howtodoinjava.com/java/collections/arraylist/replace-element-arraylist/)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册