提交 5028e799 编写于 作者: W wizardforcel

2019-11-26 18:42:18

上级 0c9e6018
......@@ -8,11 +8,11 @@
![Memory visual representation](img/751d3b0a0d5f994387d9626692b0926c.jpg)
记忆视觉表现
内存视觉展示
在上面的表示中,4000、4004、4008 等表示内存插槽的地址,或者在与数组,数据的索引或内存位置进行的比较中。 这些“随机值”中的每一个代表 32 位。 由于每个内存插槽占用 32 位或 4 个字节,因此内存地址每次增加 4。
通常,在 Java 和一般的编程中,有两种类型的可变范围**全局****局部**。 全局变量是可以从程序中的任何地方访问的变量,而局部变量是只能在给定函数中创建它们的地方访问的变量。 因此,这两种不同类型的变量作用域存储在不同的存储区域中-**栈****数据。**
通常,在 Java 和一般的编程中,有两种类型的可变范围**全局****局部**。 全局变量是可以从程序中的任何地方访问的变量,而局部变量是只能在给定函数中创建它们的地方访问的变量。 因此,这两种不同类型的变量作用域存储在不同的存储区域中 - **栈****数据。**
![Memory regions](img/3977ced81858595e9eccbc2660b4817d.jpg)
......@@ -27,7 +27,7 @@ public void doSomething() {
}
```
在上面的简单 Java 示例中, v 存储在栈存储区域中。 这是因为 v 是局部变量。
在上面的简单 Java 示例中,`v`存储在栈存储区域中。 这是因为`v`是局部变量。
## 静态数据
......@@ -41,7 +41,7 @@ public class Example {
}
```
在上面的 Java 示例中,globalVar 位于静态数据存储区中。 因为如您所见,即使未在其中创建方法,也可以通过该方法进行访问。 此外,可以在整个程序中对其进行访问。
在上面的 Java 示例中,`globalVar`位于静态数据存储区中。 因为如您所见,即使未在其中创建方法,也可以通过该方法进行访问。 此外,可以在整个程序中对其进行访问。
## 堆
......@@ -68,12 +68,10 @@ public class Driver {
}
```
在上面的 Java 示例中,我们创建了 Person 类的新实例并将其存储在“ p”中,实际上,我们使用 Heap 区域为动态分配我们创建此类内存所需的内存。 使用 **新** 关键字的实例。 换句话说,它不固定为一定大小。 无论实例有多少字节,如果有足够的内存(可能),将创建该实例,并且该实例将仅保留创建该实例所需的字节数。
在上面的 Java 示例中,我们创建了`Person`类的新实例并将其存储在`p`中,实际上,我们使用堆区域为动态分配我们创建此类内存所需的内存。 使用`new`关键字的实例。 换句话说,它不固定为一定大小。 无论实例有多少字节,如果有足够的内存(可能),将创建该实例,并且该实例将仅保留创建该实例所需的字节数。
Java 在动态内存分配方面为我们省去了很多麻烦,因为在某些其他语言(例如 C)中,您必须手动分配内存并在不再需要该内存时手动“释放”相同的内存, 而在 Java 中,一切都在幕后发生,只需调用关键字 **new。**
Java 在动态内存分配方面为我们省去了很多麻烦,因为在某些其他语言(例如 C)中,您必须手动分配内存并在不再需要该内存时手动“释放”相同的内存, 而在 Java 中,一切都在幕后发生,只需调用关键字`new`
我将建议以下有关如何调整 JVM 以使用特定内存量的教程
> [Java 增加内存](https://javatutorial.net/java-increase-memory)
<iframe class="wp-embedded-content" data-secret="E5Uq3UeE34" frameborder="0" height="338" marginheight="0" marginwidth="0" sandbox="allow-scripts" scrolling="no" security="restricted" src="https://javatutorial.net/java-increase-memory/embed#?secret=E5Uq3UeE34" style="position: absolute; clip: rect(1px, 1px, 1px, 1px);" title="“Java Increase Memory” — Java Tutorial Network" width="600"></iframe>
\ No newline at end of file
......@@ -12,39 +12,36 @@ Java [堆转储](https://www.ibm.com/support/knowledgecenter/en/SS3KLZ/com.ibm.j
## 1\. `jmap –XX:+HEAPDUMPONOUTOFMEMORYERROR`
值得注意的是,jmap 会将堆转储打印到特定的文件位置。 jmap 工具通常打包在 [JDK](https://javatutorial.net/install-java-8-jdk-on-ubuntu) 中。 您可以在以下文件夹中找到它:< JAVA_HOME > \ bin
值得注意的是,`jmap`会将堆转储打印到特定的文件位置。`jmap`工具通常打包在 [JDK](https://javatutorial.net/install-java-8-jdk-on-ubuntu) 中。 您可以在以下文件夹中找到它:`<JAVA_HOME>\bin`
要调用 jmap,请执行以下过程。
要调用`jmap`,请执行以下过程。
`jmap -dump: live, file=&lt;file-path&gt; &lt;pid&gt;`
,其中`pid`是 Java 进程 ID,将为其捕获堆转储
。此外,`file-path`是其中打印堆转储的文件路径。
`jmap -dump: live, file=<file-path> <pid>`,其中`pid`是 Java 进程 ID,将为其捕获堆转储。此外,`file-path`是其中打印堆转储的文件路径。
**请注意,传递**“实时”选择至关重要。 如果选项“通过”,则只会将活动对象写入堆转储文件。 但是,如果您无法通过该选项,则所有对象(包括未设置为垃圾收集的对象)都将被打印到堆转储中。 这样的错误会过多且不必要地增加其堆转储的大小。 通过将您的移动开发需求与 [Java Development Company](https://www.nearshore-it.eu/java-development/) 签订合同,可以避免此类错误。
**请注意**,传递“实时”选择至关重要。 如果选项“通过”,则只会将活动对象写入堆转储文件。 但是,如果您无法通过该选项,则所有对象(包括未设置为垃圾收集的对象)都将被打印到堆转储中。 这样的错误会过多且不必要地增加其堆转储的大小。 通过将您的移动开发需求与 [Java Development Company](https://www.nearshore-it.eu/java-development/) 签订合同,可以避免此类错误。
## 2\. `HeapDumpOnOutOfMemoryError`
当应用程序遇到 java.lang.OutOfMemoryError 时,捕获瞬时堆转储至关重要。
当应用程序遇到`java.lang.OutOfMemoryError`时,捕获瞬时堆转储至关重要。
这样的过程将有助于确定内存中占用的对象以及它们在 java.lang.OutOfMemoryError 发生时的位置所占用的空间量(百分比)。
这样的过程将有助于确定内存中占用的对象以及它们在`java.lang.OutOfMemoryError`发生时的位置所占用的空间量(百分比)。
但是,由于操作和技术很多,操作组可能无法捕获堆转储。 此外,团队可能还重新启动了该应用程序。 因此,堆转储捕获已成为系统应用程序的关键方面,尤其是在涉及内存问题时。
幸运的是,XX:+ HeapDumpOnOutOfMemoryError 选项将在该过程中提供帮助。 您只需在应用程序启动时传递系统属性(XX:+ HeapDumpOnOutOfMemoryError)。 然后, [JVM](https://javatutorial.net/jvm-explained) 将通过在 JVM 面临 OutOfMemoryError 的确切时间捕获堆转储来完成其余工作。
幸运的是,`XX:+HeapDumpOnOutOfMemoryError`选项将在该过程中提供帮助。 您只需在应用程序启动时传递系统属性(`XX:+HeapDumpOnOutOfMemoryError`)。 然后, [JVM](https://javatutorial.net/jvm-explained) 将通过在 JVM 面临`OutOfMemoryError`的确切时间捕获堆转储来完成其余工作。
值得注意的是,在上述情况下将捕获的头转储将打印在名为“ -XX:HeapDumpPath”的系统属性概述的位置中
值得注意的是,在上述情况下将捕获的头转储将打印在名为`-XX:HeapDumpPath`的系统属性概述的位置中
## 3\. `jcmd`
jcmd 工具用于发送命令请求以诊断 Java [JVM](https://javatutorial.net/jvm-explained) 。 同样,jcmd 工具包含在 JDK 软件包中。 您可以在名为\ bin 的文件夹中获取它。
`jcmd`工具用于发送命令请求以诊断 Java [JVM](https://javatutorial.net/jvm-explained) 。 同样,`jcmd`工具包含在 JDK 软件包中。 您可以在名为`bin`的文件夹中获取它。
这是调用 jcmd 时需要使用的过程;
这是调用`jcmd`时需要使用的过程;
1. 转到`jcmd &lt;pid&gt; GC.heap_dump &lt;file-path&gt;`
2. 其中
3. pid:是一个 Java 进程 ID,将为其捕获堆转储
4. 另外,file-path:是在其中打印堆转储的文件路径。
1. 转到`jcmd <pid> GC.heap_dump <file-path>`
2. 其中`pid`:是一个 Java 进程 ID,将为其捕获堆转储
4. 另外,`file-path`是在其中打印堆转储的文件路径。
结论
在本文中,我讨论了可用于捕获 Java 堆转储的三个主要过程:(1)jmap – XX:+ HEAPDUMPONOUTOFMEMORYERROR(2)HeapDumpOnOutOfMemoryError 和(3)jcmd。
\ No newline at end of file
在本文中,我讨论了可用于捕获 Java 堆转储的三个主要过程:(1)`jmap –XX:+HEAPDUMPONOUTOFMEMORYERROR`(2)`HeapDumpOnOutOfMemoryError`和(3)`jcmd`
\ No newline at end of file
......@@ -14,7 +14,7 @@
因此,垃圾收集功能会找出这些对象并自动将其从内存中删除并删除它们,从而实现高效的内存使用和管理。
如果要用 C 语言进行相同的垃圾收集和优化,则可以使用 free()函数,而在 C++中,则可以使用 delete()方法。 因此,Java 中此过程实现了自动化,从而为用户减少了麻烦。
如果要用 C 语言进行相同的垃圾收集和优化,则可以使用`free()`函数,而在 C++ 中,则可以使用`delete()`方法。 因此,Java 中此过程实现了自动化,从而为用户减少了麻烦。
从技术上讲,Java 垃圾收集处理的是跟踪 [JVM(Java 虚拟机)](https://javatutorial.net/jvm-explained)堆空间中的每个对象,并删除(删除/取消分配)未使用的对象。
......
# Java Mutex 示例
# Java 互斥量示例
> 原文: [https://javatutorial.net/java-mutex-example](https://javatutorial.net/java-mutex-example)
......@@ -6,26 +6,26 @@
想想一个队列。 不管长短,都没关系。 现在想想一辆正在出售游乐园门票的卡车。 一次一个人可以买票。 该人买了票后,就该排队了。
这个小故事与理解 Mutex 有什么关系? 让我解释。
这个小故事与理解互斥量有什么关系? 让我解释。
![java-featured-image](img/e0db051dedc1179e7424b6d998a6a772.jpg)
**Mutex 允许每个线程具有 1 个许可。** 换句话说,一次只能有 1 个线程可以访问资源。 在上面的类比中,两个人不能同时购买门票。 Mutex 也是如此。 它不是线程,而是人员,而是票证。 同样的事情或多或少..
**互斥量允许每个线程具有 1 个许可**。换句话说,一次只能有 1 个线程可以访问资源。 在上面的类比中,两个人不能同时购买门票。互斥量也是如此。 它不是线程,而是人员,而是票证。 同样的事情或多或少..
Mutex 与 Semaphore 略有不同,因此 **Semaphore 允许多个线程访问资源。**意思是,多个人可以同时购买门票。
互斥量与`Semaphore`略有不同,因此`Semaphore`允许多个线程访问资源。意思是,多个人可以同时购买门票。
![Mutex java example thread](img/66ac23d82b34151f8831186826308565.jpg)
## 构造器
1. 公共[信号灯](https://javatutorial.net/java-semaphore-example)(国际许可)
2. 公共信号量(int 许可,布尔型公平);
1. `public Semaphore(int permits)`
2. `public Semaphore(int permits, boolean fair)`
第一个构造函数是我们实际上可以区分 Mutex 和 Semaphore 的地方。 如果那里有 1 作为参数,则意味着将只允许 1 个线程获取锁。 请记住,由于它没有第二个参数**布尔公平**,因此您正在使 Semaphore 类以任何顺序访问任何线程。
第一个构造函数是我们实际上可以区分互斥量和`Semaphore`的地方。 如果那里有 1 作为参数,则意味着将只允许 1 个线程获取锁。 请记住,由于它没有第二个参数`boolean fair`,因此您正在使`Semaphore`类以任何顺序访问任何线程。
第二个构造函数如果传递 true(公平),则确保按线程请求访问并在队列中等待的顺序给出访问。
第二个构造函数如果传递`true`(公平),则确保按线程请求访问并在队列中等待的顺序给出访问。
## Mutex 基本代码实现
## 互斥量基本代码实现
```java
import java.util.concurrent.Semaphore;
......@@ -100,10 +100,10 @@ Christie bought the ticket.
How many people can buy tickets after Christie has finished buying the ticket: 1
```
从输出中可以看到,**当有人买票时,没有其他人可以买。** 这是显示以下内容的行之一:
从输出中可以看到,**当有人买票时,没有其他人可以买**这是显示以下内容的行之一:
Bob 仍在买票。 还有多少人可以和他一起买票: **0**
但是,在他“购买”了机票之后,其他人立即购买了机票。
归根结底,它涉及 acquire()和 release()。 Acquire()是指该人开始“购买机票”的时间,release()是指该人“购买机票”的时间。
\ No newline at end of file
归根结底,它涉及`acquire()``release()``acquire()`是指该人开始“购买机票”的时间,`release()`是指该人“购买机票”的时间。
\ No newline at end of file
......@@ -4,7 +4,7 @@
信号量可用于限制并发线程的数量,并且实质上,此类维护一组许可。
Acquire()从信号量获取许可,然后 release()将许可返回信号量。 如果没有许可证,则 acuire()将阻塞直到可用
`acquire()`从信号量获取许可,然后`release()`将许可返回信号量。 如果没有许可证,则`acuire()`将阻塞直到可用
信号量用于控制对特定资源的访问。
......@@ -12,22 +12,22 @@ Acquire()从信号量获取许可,然后 release()将许可返回信
**工作流程**
信号量设置为一个计数值。 然后线程尝试获取许可,如果计数为 0 或小于 0,则线程将被阻塞,并且它将等待下一个许可(如果有)。 这将一直保持下去,直到 count 大于 0。如果是,则信号量将提供对线程资源的访问。 然后线程将释放许可,计数将增加 1。
信号量设置为一个计数值。 然后线程尝试获取许可,如果计数为 0 或小于 0,则线程将被阻塞,并且它将等待下一个许可(如果有)。 这将一直保持下去,直到数量大于 0。如果是,则信号量将提供对线程资源的访问。 然后线程将释放许可,计数将增加 1。
## 构造器
1. 信号量(int 许可):使用给定数量的许可和不公平的公平设置创建一个信号量
2. 信号量(int 许可,布尔值公平):创建具有给定许可数量和给定公平性设置的信号量
1. `Semaphore(int permits)`:使用给定数量的许可和不公平的公平设置创建一个信号量
2. `Semaphore(int permits, boolean fair)`:创建具有给定许可数量和给定公平性设置的信号量
## 主要方法
1. void acquisition():从当前信号量获取许可,阻塞直到可用,否则线程被中断。
2. void Acquisition(int permits):从当前信号量获取指定的许可数量,直到所有可用或线程中断为止一直阻塞。
3. int availablePermites():返回当前信号量中可用的当前许可数量
1. `void acquire()`:从当前信号量获取许可,阻塞直到可用,否则线程被中断。
2. `void acquire(int permits)`:从当前信号量获取指定的许可数量,直到所有可用或线程中断为止一直阻塞。
3. `int availablePermites()`:返回当前信号量中可用的当前许可数量
要查看所有方法,请在处单击[。 您将被重定向到 Oracle 官方文档。](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html)
要查看所有方法,[请单击此处,您将被重定向到 Oracle 官方文档](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html)
信号量可用于锁定对特定资源的访问。 每个线程都必须请求“权限”,因此需要在访问资源之前调用方法 acquire()。 当线程不再需要资源时,它必须调用 release()来释放锁。
信号量可用于锁定对特定资源的访问。 每个线程都必须请求“权限”,因此需要在访问资源之前调用方法`acquire()`。 当线程不再需要资源时,它必须调用`release()`来释放锁。
```java
import java.util.concurrent.Semaphore;
......@@ -66,9 +66,9 @@ Available permits: 1
Available permits: 0
```
从上面的示例中可以看到,当您调用 release()时,您正在向 Semaphore 实例添加许可。 当您调用 acquire()时,您正在删除 permit()。 如果没有许可证,而您打电话给获取,它将等待直到许可证被释放,因此在上例中将永远不会执行最后一个打印语句。
从上面的示例中可以看到,当您调用`release()`时,您正在向`Semaphore`实例添加许可。 当您调用`acquire()`时,您正在删除`permit()`。 如果没有许可证,而您打电话给获取,它将等待直到许可证被释放,因此在上例中将永远不会执行最后一个打印语句。
如果您有 1 个许可,然后又有一个 Acquisition()调用,紧接着是一个 release()调用,则称为**锁。**
如果您有 1 个许可,然后又有一个`acquire()`调用,紧接着是一个`release()`调用,则称为锁。
```java
import java.util.concurrent.Semaphore;
......
......@@ -8,16 +8,16 @@
有两种创建并行流的方法:
* 通过使用 parallelStream()方法
* 通过使用 parallel()方法
* 通过使用`parallelStream()`方法
* 通过使用`parallel()`方法
当您使用并行流时,它们实际上会使用更多的 CPU 能力,这将使整个输出或处理(如果您愿意)的整体速度更快。 更快! 另外,如果您不使用并行流进行大型计算,则您的程序将仅使用 16 个内核中的 1 个内核。 多么低效!
**订单**
## 顺序
并行流将问题分为多个小子集(或子问题),它们可以同时解决(不同于顺序排列的顺序,在这些子集中一个一个地执行每个小问题)。 在计算了子问题之后,将所有对它们的解决方案组合在一起。
**如何实现**
## 如何实现
Java 允许您通过使用诸如 map 之类的聚合操作来实现并行流。
......@@ -51,11 +51,11 @@ public class Main {
}
```
在此示例中,我们有一个示例方法,该方法仅循环 10000 次并将 i 加到 n。 最后,我们简单地返回 n。 在我们的主要函数中,我们创建一个包含整数的 [ArrayList](https://javatutorial.net/java-arraylist-example) 。 然后我们创建一个巨大的循环,循环 1000000 次,然后将每个增量 i * 6 加到 arraylist 中。 添加完之后,我们得到了这两个打印语句,它们指出这个大循环已经完成。
在此示例中,我们有一个示例方法,该方法仅循环 10000 次并将`i`加到`n`。 最后,我们简单地返回`n`。 在我们的主要函数中,我们创建一个包含整数的[`ArrayList`](https://javatutorial.net/java-arraylist-example)。 然后我们创建一个巨大的循环,循环 1000000 次,然后将每个增量`i * 6`加到`arraylist`中。 添加完之后,我们得到了这两个打印语句,它们指出这个大循环已经完成。
在这些打印语句下面是有趣的部分。 更具体地说,这是我们使用映射函数创建并行流的地方。 我们在 arraylist 上调用 parallelStream(),对于 arraylist 中的每个 i,我们都在调用计算函数,并将 arraylist 的元素作为参数传递。 最后,我们将所有结果结合在一起。
在这些打印语句下面是有趣的部分。 更具体地说,这是我们使用映射函数创建并行流的地方。 我们在`arraylist`上调用`parallelStream()`,对于`arraylist`中的每个`i`,我们都在调用计算函数,并将`arraylist`的元素作为参数传递。 最后,我们将所有结果结合在一起。
**何时应使用并行流**
## 何时应使用并行流
* 当有大量数据要处理时
* 如果循序渐进的方法使您付出了代价
......
......@@ -26,7 +26,7 @@
同步块用于线程同步。
Synchronized 关键字用于标识 Java 中的同步块,并基于某些对象进行同步。 其背后的主要概念是,在同一对象上同步的所有同步块一次只能在其中一个线程内执行,这会阻止多个线程同时运行和执行。 尝试进入同步块的所有其他线程将被阻止,直到同步块内的线程退出该块为止。
`synchronized`关键字用于标识 Java 中的同步块,并基于某些对象进行同步。 其背后的主要概念是,在同一对象上同步的所有同步块一次只能在其中一个线程内执行,这会阻止多个线程同时运行和执行。 尝试进入同步块的所有其他线程将被阻止,直到同步块内的线程退出该块为止。
共享资源保留在此同步块内,以便在给定时间点只有一个线程可以访问特定资源。
......@@ -110,7 +110,7 @@ public class Tests {
```
&lt;u&gt;输出:&lt;/u&gt;(每次运行都会产生不同的结果)
输出:(每次运行都会产生不同的结果)
```java
Starting Thread - 1
......@@ -129,7 +129,7 @@ Thread Thread - 1  exiting.
Thread Thread - 2  exiting.
```
&lt;u&gt;相同的示例,但是这次具有线程同步:&lt;/u&gt;
相同的示例,但是这次具有线程同步:
```java
class Countings {
......@@ -192,7 +192,7 @@ public class Testings{
```
&lt;u&gt;输出:&lt;/u&gt;(我们看到的输出是同步的,并且每次执行程序都是相同的)
输出:(我们看到的输出是同步的,并且每次执行程序都是相同的)
```java
Starting Thread - 1
......
......@@ -18,9 +18,9 @@
## `Executor`,`Runnable`和`ExecutorService`
Java 提供了 Executor 框架,这意味着您只需要实现 Runnable 对象并将其发送给 executor 即可执行。
Java 提供了`Executor`框架,这意味着您只需要实现`Runnable`对象并将其发送给执行器即可执行。
要使用线程池,首先我们需要创建一个 ExecutorService 对象并将任务传递给它。 ThreadPoolExecutor 类设置核心和最大池大小。 然后,可运行对象将顺序执行。
要使用线程池,首先我们需要创建一个`ExecutorService`对象并将任务传递给它。`ThreadPoolExecutor`类设置核心和最大池大小。 然后,可运行对象将顺序执行。
## 不同的`Executor`线程池方法
......@@ -32,7 +32,7 @@ newCachedThreadPool() - creates a thread pool that creates new threads if needed
newSingleThreadExecutor() - creates a single thread
```
ExecutorService 接口包含许多方法,这些方法用于控制任务的进度并管理服务的终止。 您可以使用 Future 实例控制任务的执行。 有关如何使用 Future 的示例:
`ExecutorService`接口包含许多方法,这些方法用于控制任务的进度并管理服务的终止。 您可以使用`Future`实例控制任务的执行。 有关如何使用`Future`的示例:
```java
ExecutorService execService = Executors.newFixedThreadPool(6);
......@@ -40,13 +40,13 @@ Future<String> future = execService.submit(() -> "Example");
String result = future.get();
```
ThreadPoolExecutor 使您可以实现具有许多参数的可扩展线程池,这些参数包括 corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,处理程序,threadFactor。 但是,corePoolSize,maximumPoolSize 和 keepAliveTime 是主要变量,因为它们在每个构造函数中都使用。
`ThreadPoolExecutor`使您可以实现具有许多参数的可扩展线程池,这些参数包括`corePoolSize``maximumPoolSize``keepAliveTime``unit``workQueue``handler``threadFactor`。 但是,`corePoolSize``maximumPoolSize``keepAliveTime`是主要变量,因为它们在每个构造函数中都使用。
corePoolSize 是要保留在池中的线​​程数,即使它们处于空闲状态,除非设置了 allowCoreThreadTimeOut
`corePoolSize`是要保留在池中的线​​程数,即使它们处于空闲状态,除非设置了`allowCoreThreadTimeOut`
maximumPoolSize 是池中允许的最大线程数。
`maximumPoolSize`是池中允许的最大线程数。
keepAliveTime 是当线程数大于内核数时,这是多余的空闲线程将在终止之前等待新任务的最长时间。
`keepAliveTime`是当线程数大于内核数时,这是多余的空闲线程将在终止之前等待新任务的最长时间。
有关其他参数的更多信息,请访问[原始 Oracle 文档](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html)
......@@ -59,7 +59,7 @@ keepAliveTime 是当线程数大于内核数时,这是多余的空闲线程将
3. 将任务传递给执行程序池
4. 关闭执行程序池
**Task.java**
`Task.java`
```java
import java.text.SimpleDateFormat;
......@@ -100,7 +100,7 @@ public class Task implements Runnable {
```
**Main.java**
`Main.java`
```java
import java.text.SimpleDateFormat;
......@@ -171,7 +171,7 @@ task 5 completed
**上面的代码实现的细分:**
Task.java 表示任务类。 每个任务都有一个名称实例变量,并且每个任务都使用构造函数实例化。 此类有 1 个方法,称为运行。 在 run 方法的主体内,有一个 for 循环,该循环根据存在的任务数进行迭代。 在我们的例子中,有 5 个任务,这意味着它将运行 5 次。 第一次迭代,显示当前任务初始化的时间。 其他迭代,打印执行时间。 打印完成后,有一个 Thread.sleep()方法调用,该方法用于以 1 秒的延迟显示每个迭代消息。 **注意**,像这样调用的方法名称“ run” 非常重要,因为它是来自 Task 类正在实现的 Runnable 的抽象方法。
`Task.java`表示任务类。 每个任务都有一个名称实例变量,并且每个任务都使用构造函数实例化。 此类有 1 个方法,称为`run`。 在`run`方法的主体内,有一个`for`循环,该循环根据存在的任务数进行迭代。 在我们的例子中,有 5 个任务,这意味着它将运行 5 次。 第一次迭代,显示当前任务初始化的时间。 其他迭代,打印执行时间。 打印完成后,有一个`Thread.sleep()`方法调用,该方法用于以 1 秒的延迟显示每个迭代消息。 **注意**,像这样调用的方法名称`run`非常重要,因为它是来自`Task`类正在实现的`Runnable`的抽象方法。
仅在池中的某个胎面变得空闲时才执行任务 4 和 5。 在此之前,额外的任务将放置在队列中。
......@@ -179,4 +179,4 @@ Task.java 表示任务类。 每个任务都有一个名称实例变量,并且
## 线程池何时有用
组织服务器应用程序时。 如本文开头所述,在组织服务器应用程序时非常有用,因为使用线程池非常有效,就像有许多任务一样,它会自动将它们放入队列中。 不仅如此,它还可以防止内存不足,或者至少可以显着减慢这样做的速度。 使用 ExecutorService 使其更易于实现。
\ No newline at end of file
组织服务器应用程序时。 如本文开头所述,在组织服务器应用程序时非常有用,因为使用线程池非常有效,就像有许多任务一样,它会自动将它们放入队列中。 不仅如此,它还可以防止内存不足,或者至少可以显着减慢这样做的速度。 使用`ExecutorService`使其更易于实现。
\ No newline at end of file
......@@ -2,11 +2,11 @@
> 原文: [https://javatutorial.net/threadlocal-java-example](https://javatutorial.net/threadlocal-java-example)
ThreadLocal 是提供线程局部变量的类,用于实现线程安全。 存储的数据只能由特定线程访问。
`ThreadLocal`是提供线程局部变量的类,用于实现线程安全。 存储的数据只能由特定线程访问。
![java-featured-image](img/e0db051dedc1179e7424b6d998a6a772.jpg)
ThreadLocal 扩展了 Object 类,并提供了线程限制,它是局部变量的“一部分”。
`ThreadLocal`扩展了`Object`类,并提供了线程限制,它是局部变量的“一部分”。
## 创建`ThreadLocal`变量
......@@ -14,18 +14,18 @@ ThreadLocal 扩展了 Object 类,并提供了线程限制,它是局部变量
ThreadLocal threadLocalExample = new ThreadLocal();
```
上面代码中 ThreadLocal 对象的实例化仅需要针对每个线程进行。
上面代码中`ThreadLocal`对象的实例化仅需要针对每个线程进行。
就像大多数类一样,一旦有了 ThreadLocal 的实例,就可以在其上调用方法。 一些方法是:
就像大多数类一样,一旦有了`ThreadLocal`的实例,就可以在其上调用方法。 一些方法是:
1. get():返回此线程局部变量的当前线程副本中的值
2. initialValue():返回当前线程局部变量的当前线程初始值
3. remove():从当前线程中删除当前线程局部变量的值
4. set(T value):将当前线程局部变量的当前线程副本设置为指定值
1. `get()`:返回此线程局部变量的当前线程副本中的值
2. `initialValue()`:返回当前线程局部变量的当前线程初始值
3. `remove()`:从当前线程中删除当前线程局部变量的值
4. `set(T value)`:将当前线程局部变量的当前线程副本设置为指定值
有关这些方法的更多详细信息,请访问原始 [Oracle 文档](https://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html)
有关这些方法的更多详细信息,请访问原始 [Oracle 文档](https://docs.oracle.com/javase/7/docs/api/java/lang/ThreadLocal.html)
ThreadLocal 实例是希望将状态与线程相关联的类中的私有静态字段(在大多数情况下)
`ThreadLocal`实例是希望将状态与线程相关联的类中的私有静态字段(在大多数情况下)
## 实现示例
......@@ -81,4 +81,4 @@ After remove: null
## 分解
上面的代码显示了两种使它起作用的方法:一种是通过拥有 Runnable 对象并将其传递给 Thread 实例,然后覆盖 run()方法,或者您可以简单地创建一个 ThreadLocal 实例并为其设置值,然后 可以获取或删除它。 从上面的示例中可以看到,即使它是相同的变量(局部变量),也可以包含不同的值。 在第一种情况下,它包含值“ First”。 在第二种情况下,它包含值“ Second”。 对于其他实现,我只显示了一个线程。 但是,每个线程都是独立的–意味着,如果您要创建另一个 Thread 实例(例如 thread2),并对其进行 start(),它将独立运行,并且与其他 Thread 实例变量无关。 要进行检查,可以在静态类中创建一个 ThreadLocal 实例,然后在重写的 run()方法中创建一个随机数,然后使用 set()方法将其传递给当前线程。 您将看到,如果您在两个或多个不同的线程上调用它,则它们都将具有不同的值。
\ No newline at end of file
上面的代码显示了两种使它起作用的方法:一种是通过拥有`Runnable`对象并将其传递给`Thread`实例,然后覆盖`run()`方法,或者您可以简单地创建一个`ThreadLocal`实例并为其设置值,然后 可以获取或删除它。 从上面的示例中可以看到,即使它是相同的变量(局部变量),也可以包含不同的值。 在第一种情况下,它包含值`"First"`。 在第二种情况下,它包含值`"Second"`。 对于其他实现,我只显示了一个线程。 但是,每个线程都是独立的–意味着,如果您要创建另一个`Thread`实例(例如`thread2`),并对其进行`start()`,它将独立运行,并且与其他`Thread`实例变量无关。 要进行检查,可以在静态类中创建一个`ThreadLocal`实例,然后在重写的`run()`方法中创建一个随机数,然后使用`set()`方法将其传递给当前线程。 您将看到,如果您在两个或多个不同的线程上调用它,则它们都将具有不同的值。
\ No newline at end of file
......@@ -6,23 +6,23 @@
## 活锁
Java 中的 Livelock 是一种递归条件,其中两个或多个线程不断重复一段特定的代码。
Java 中的活锁是一种递归条件,其中两个或多个线程不断重复一段特定的代码。
当一个线程不断响应另一个线程并且另一个线程也执行相同操作时,就会发生 Livelock
当一个线程不断响应另一个线程并且另一个线程也执行相同操作时,就会发生活锁
要对其进行分解,我们可以总结以下几点:
* 一个线程响应另一个线程的行为而运行,而另一个线程也响应先前的线程而运行,则可能发生活锁。
* Livelock 线程无法继续进行。
* 活锁线程无法继续进行。
* 线程没有被阻塞; 他们只是忙于互相回应。
Livelock 也被称为资源匮乏的特例
活锁也被称为资源匮乏的特例
让我们通过将其与现实世界联系起来来理解该概念。 考虑两辆车在狭窄桥梁的相对两侧。 一次只能乘一辆车通过桥。 这两辆车的驾驶员都很有礼貌,正在等待对方先通过桥。 他们互相鸣叫,让他们知道他们想让对方先通过。 但是,两者都没有越过桥梁并互相鸣喇叭。 这种情况类似于活锁。
现在,通过一些编码来尝试这种实际情况:
&lt;u&gt;第一辆等待过桥的汽车的等级:&lt;/u&gt;
第一辆等待过桥的汽车的等级:
```java
public class Car1 {
......@@ -50,7 +50,7 @@ public class Car1 {
```
&lt;u&gt;等待通过桥的第二辆车的类别:&lt;/u&gt;
等待通过桥的第二辆车的类别:
```java
public class Car2 {
......@@ -81,7 +81,7 @@ public class Car2 {
```
&lt;u&gt;主要测试类别:&lt;/u&gt;
主要测试类别:
```java
public class BridgeCheck {
......@@ -106,7 +106,7 @@ public class BridgeCheck {
```
&lt;u&gt;输出:&lt;/u&gt;
输出:
这导致了非终止循环。
......@@ -120,7 +120,7 @@ public class BridgeCheck {
图:死锁状态
&lt;u&gt;让我们看一下发生死锁的情况:&lt;/u&gt;
让我们看一下发生死锁的情况:
```java
public class DeadlockExample{
......@@ -149,9 +149,8 @@ private static String B = "Something B";
考虑两个线程 T1 和 T2,T1 获取 A 并等待 B 完成其功能。 但是,T2 获取 B 并等待 A 完成其自身的功能。 在这里,T1 和 T2 正在等待被其他线程锁定的资源。 因此,这是一个僵局方案。
**&lt;u&gt;,&lt;/u&gt;**
**&lt;u&gt;一起避免死锁:&lt;/u&gt;**
## 避免死锁:
* 第一个建议是避免同时使用多线程,但这在许多情况下可能不切实际。 因此,这种解决方案不是很明智。
* 分析并确保在事先访问资源时没有锁。
......
......@@ -35,7 +35,7 @@ Java 中有 4 种引用类型:
* 弱引用还广泛用于 [WeakHashMap](https://javatutorial.net/java-weakhashmap-example) 类中。 这是 Map 接口的实现,其中每个键值都存储为弱引用。 键值对扩展了 WeakReference 类。 因此,垃圾收集器删除此键也会导致实体也被删除。
&lt;u&gt;代码示例:&lt;/u&gt;
代码示例:
私有静态类 TryingOut &lt; K,V &gt;扩展了 WeakReference &lt;对象&gt;实现 Map.Entry &lt; K,V &gt;
......@@ -43,7 +43,7 @@ Java 中有 4 种引用类型:
## 实现弱引用:
&lt;u&gt;java.lang.ref.WeakReference&lt;/u&gt; 类在处理和创建弱引用时使用。
java.lang.ref.WeakReference 类在处理和创建弱引用时使用。
可以使用弱引用的一种实际的实际场景是在建立数据库连接时使用,当使用数据库的应用程序关闭时,垃圾收集器可能会清除该数据库连接。
......@@ -84,7 +84,7 @@ public class TryingOutWeak
```
&lt;u&gt;代码的输出:&lt;/u&gt;
代码的输出:
这被打印在屏幕上
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册