最近又学到了很多新知识,感谢优锐课老师细致地讲解,这篇博客记录下自己所学所想,也和大家分享、了解有关Java中的并发问题和线程限制的更多信息。

在此文中,我们将探讨线程限制,它的含义以及如何实现。因此,让我们直接研究它。

线程限制

大多数并发问题仅在我们希望在线程之间共享可变变量或可变状态时才会发生。如果在多个线程之间共享了可变状态,则所有线程都将能够读取和修改状态的值,从而导致错误或意外的行为。避免此问题的一种方法是根本不共享线程之间的数据。该技术被称为线程限制,是在我们的应用程序中实现线程安全的最简单方法之一。

Java语言本身没有任何执行线程限制的方法。线程限制是通过设计程序的方式实现的,该程序不允许状态被多个线程使用,因此由实现来强制执行。线程限制有几种类型,如下所述。

临时线程限制

临时线程限制描述了一种线程限制方式,由开发人员或负责该程序的一组开发人员共同负责,以确保对象的使用仅限于单个线程。这种方法非常脆弱,在大多数情况下应避免使用。

Ad-hoc线程限制下的一种特殊情况适用于volatile变量。只要确保只从单个线程写入volatile变量,就可以对共享的volatile变量执行读-修改-写操作。在这种情况下,你将修改限制在单个线程中,以防止出现竞争情况,并且易失变量的可见性保证可确保其他线程看到最新的值。

堆栈限制

堆栈限制是将变量或对象限制在线程的堆栈中。这比临时线程约束要强得多,因为通过定义堆栈本身中变量的状态,它甚至进一步限制了对象的范围。例如,考虑以下代码:

 private long numberOfPeopleNamedJohn(List<Person> people) {
List<Person> localPeople = new ArrayList<>();
localPeople.addAll(people);
return localPeople.stream().filter(person -> person.getFirstName().equals("John")).count();
}

在上面的代码中,我们传递了一个人员列表,但没有直接使用它。 相反,我们创建自己的列表,该列表位于当前正在执行的线程的本地,并将所有人员添加到localPeople中。由于我们仅在numberOfPeopleNamedJohn方法中定义列表,因此这使变量localPeople堆栈受到限制,因为它存在于一个线程的堆栈中,因此无法被其他任何线程访问。这使localPeople线程安全。我们唯一需要注意的是,我们不应该允许localPeople逃脱此方法的范围,以使其保持在堆栈限制内。定义此变量时,也应记录或注释该变量,通常来说,只有当前的开发人员才能避免使用此变量,将来,另一位开发人员可能会搞砸了。

线程本地

ThreadLocal允许你将每个线程的值与值持有对象相关联。它允许你为不同的线程存储不同的对象,并维护哪个对象对应于哪个线程。它具有set和get访问器方法,该方法为使用它的每个线程维护该值的单独副本。get()方法始终返回从当前正在执行的线程传递给set()的最新值。让我们看一个例子:

 public class ThreadConfinementUsingThreadLocal {
public static void main(String[] args) {
ThreadLocal<String> stringHolder = new ThreadLocal<>();
Runnable runnable1 = () -> {
stringHolder.set("Thread in runnable1");
try {
Thread.sleep(5000);
System.out.println(stringHolder.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Runnable runnable2 = () -> {
stringHolder.set("Thread in runnable2");
try {
Thread.sleep(2000);
stringHolder.set("string in runnable2 changed");
Thread.sleep(2000);
System.out.println(stringHolder.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Runnable runnable3 = () -> {
stringHolder.set("Thread in runnable3");
try {
Thread.sleep(5000);
System.out.println(stringHolder.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread1 = new Thread(runnable1);
Thread thread2 = new Thread(runnable2);
Thread thread3 = new Thread(runnable3);
thread1.start();
thread2.start();
thread3.start();
}
}

在上面的示例中,我们使用相同的ThreadLocal对象stringHolder执行了三个线程。如你在这里看到的,首先,我们在stringHolder对象的每个线程中设置一个字符串,使其包含三个字符串。然后,经过一些暂停后,我们仅从第二个线程更改了该值。下面是程序的输出:

 string in runnable2 changed
Thread in runnable1
Thread in runnable3

如你在上面的输出中看到的,线程2的字符串已更改,但线程1和线程3的字符串未受影响。如果在从ThreadLocal获取特定线程上的值之前未设置任何值,则它将返回null。终止线程后,ThreadLocal中特定于线程的对象将准备进行垃圾回收。

这就是关于线程限制的全部内容。我希望这篇博客能对你有所帮助,并且你必须学习新的知识。

谢谢,祝你愉快!

Java并发:线程限制的更多相关文章

  1. Java 并发 线程同步

    Java 并发 线程同步 @author ixenos 同步 1.异步线程本身包含了执行时需要的数据和方法,不需要外部提供的资源和方法,在执行时也不关心与其并发执行的其他线程的状态和行为 2.然而,大 ...

  2. Java 并发 线程的优先级

    Java 并发 线程的优先级 @author ixenos 低优先级线程的执行时刻 1.在任意时刻,当有多个线程处于可运行状态时,运行系统总是挑选一个优先级最高的线程执行,只有当线程停止.退出或者由于 ...

  3. Java 并发 线程属性

    Java 并发 线程属性 @author ixenos 线程优先级 1.每当线程调度器有机会选择新线程时,首先选择具有较高优先级的线程 2.默认情况下,一个线程继承它的父线程的优先级 当在一个运行的线 ...

  4. Java 并发 线程的生命周期

    Java 并发 线程的生命周期 @author ixenos 线程的生命周期 线程状态: a)     New 新建 b)     Runnable 可运行 c)     Running 运行 (调用 ...

  5. Java并发——线程安全、线程同步、线程通信

    线程安全 进程间"共享"对象 多个“写”线程同时访问对象. 例:Timer实例的num成员,即add()方法是用的次数.即Timer实例是资源对象. class TestSync ...

  6. 从JDK源码角度看java并发线程的中断

    线程的定义给我们提供了并发执行多个任务的方式,大多数情况下我们会让每个任务都自行执行结束,这样能保证事务的一致性,但是有时我们希望在任务执行中取消任务,使线程停止.在java中要让线程安全.快速.可靠 ...

  7. Java并发——线程介绍

    前言: 互联网时代已经发展到了现在.从以前只考虑小流量到现在不得不去考虑高并发的问题.扯到了高并发的问题就要扯到线程的问题.你是否问过自己,你真正了解线程吗?还是你只知道一些其他博客里写的使用方法.下 ...

  8. java并发线程池---了解ThreadPoolExecutor就够了

    总结:线程池的特点是,在线程的数量=corePoolSize后,仅任务队列满了之后,才会从任务队列中取出一个任务,然后构造一个新的线程,循环往复直到线程数量达到maximumPoolSize执行拒绝策 ...

  9. java 并发——线程

    一.前言 前一篇文章总结了对 java 并发中的内置锁的理解,这篇文章来说说线程 ,并发与线程总有剪不断理还乱的关系.关于 java 线程的基本概念.线程与进程的关系以及如何创建线程,想必大家都很清楚 ...

  10. Java并发--线程池的使用

    在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统 ...

随机推荐

  1. codeblocks 调试

    codeblocks 调试工具使用的注意事项: 1.codebloccks 调试,必须要在一个项目下才可以,也就是说“单独的文件是不能运行debug工具的” 2.项目的目录文件名必须是全英文,同时文件 ...

  2. java中hashmap容量的初始化

    HashMap使用HashMap(int initialCapacity)对集合进行初始化. 在默认的情况下,HashMap的容量是16.但是如果用户通过构造函数指定了一个数字作为容量,那么Hash会 ...

  3. Apache服务安装及一些基本操作

    注意:安装apache服务之前记得搭建yum仓库 1.安装apache服务,输入命令“yum install httpd” 安装成功后,会这样显示 2.需要对Apache服务进行启动,输入命令“sys ...

  4. Python标准类型的分类

    Python有3种不同的模型可以帮助对基本类型进行分类,这些类型更好的理解类型之间的相互关系以及他们的工作原理. 1 存储模型    能保存单个字面对象的类型,称为原子或标量存储:    能保存多个对 ...

  5. MAC系统下,Jmeter5.1.1 无法录制问题

    问题一: 点击[start]先出现一个检查证书信息的弹窗,确保删除旧的安装新的,并且需要信任证书 (一般证书只需要信任一下即可,每次启动都会有这个弹窗提醒) 问题二: MAC OS系统使用Jmeter ...

  6. 023.掌握Pod-Pod扩容和缩容

    一 Pod的扩容和缩容 Kubernetes对Pod的扩缩容操作提供了手动和自动两种模式,手动模式通过执行kubectl scale命令或通过RESTful API对一个Deployment/RC进行 ...

  7. Amazon Lightsail部署LAMP应用程序之部署实验室基础架构

    一.在Lightsail中创建LAMP堆栈实例 1.在AWS管理控制台的"服务"下拉选项中单击"Lightsail". 2.在语言方面选择 "英语&q ...

  8. Spring Cloud - 切换Ribbon的负载均衡模式

    Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现.通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模 ...

  9. 管道符和作业控制、shell变量、环境变量配置文件 使用介绍

    第6周第1次课(4月23日) 课程内容: 8.6 管道符和作业控制 8.7/8.8 shell变量8.9 环境变量配置文件扩展bashrc和bash_profile的区别 http://ask.ape ...

  10. Server MyEclipse Tomcat v7.0 was unable to start within 45 seconds. If the server requires more time

    启动Tomcat服务器时经常遇到这个错误, Server MyEclipse Tomcat v7.0 was unable to start within 45 seconds. If the ser ...