多核机现在非常普遍。即使手机配备了强大的多核处理器。通过多进程和多线程表示,多个CPU可以同时工作以加快任务的实现。
多线程是编程中的一个更高级的主题。由于它涉及共享资源的操作,因此在编码时遇到问题非常容易。Java的并发软件包提供了许多工具来帮助我们简化这些变量的同步,但是学习应用道路仍然充满曲折。
本文将简要介绍Java中多线程线程的基本知识。残酷的多线错误的%。
在JVM中,线程实际上是一个轻量级的过程(LWP)。所谓的轻巧过程实际上是用户流程调用系统核心提供的一组接口。实际上,它还需要调用一个更基础的内核线程(KLT)。
实际上,JVM的线程创建破坏和调度都取决于操作系统。如果您查看线程类中的多个功能,您会发现其中许多是本机的,并且基础操作系统的功能是直接调用的。
下图是Linux上的简单线程模型。
可以看出,当切换不同的线程时,它们会经常转换状态和内核状态。此开关的成本相对较大,这是我们通常称之为上下文开关的成本。
在引入线程同步之前,我们需要引入一个新术语,即JVM的内存模型JMM。
JMM不是堆和Metaspace等内存的划分。这是一个完全不同的概念,它是指与线程相关的Java运行时线程内存模型。
执行Java代码时,许多说明不是原子。如果这些值的执行顺序未对准,将获得不同的结果。例如,I ++可以转换为以下字节码。
这只是代码级别。如果您在每个CPU上添加所有级别的缓存,则此执行过程将变得更加精致。如果我们要执行,然后执行,则无法完成主字节代码指令。我们需要一些同步方法。
上图是JMM的内存模型。它分为两种类型:主内存和工作内存。我们通常在线程中操作这些变量,这实际上是操作的主内存的副本。修改完成后,您需要将其重新旋转到主机上可以知道内存和其他线程知道这些变化。
为了完成JMM的操作并完成线程之间的变量,Java提供了许多同步方法。
从上面的描述中可以看出,有太多的东西可以学习多线程编程。不幸的是,尽管同步方法正在改变,但创建线程的方法很少。
第一个类别是线程类。每个人都知道有两种方法可以实现。第一个可以继承线程覆盖其运行方法。第二个是实现可运行的接口以实现其运行方法。创建线程的第三种方法是通过线程池。
实际上,最后,只有一种启动方法,即线程。线程池和可运行只是一个包装快捷方式。
多线程非常复杂且易于遇到问题。有哪些常见问题?我们如何避免?在下面,我将介绍10个看起来很高的坑并提供解决方案。
首先,我们谈到了一个非常非常低的水平,但是有多个线程错误,并带来了严重的后果。
通常,我们创建线程:线程,可运行和线程池。在Java1.8的受欢迎程度下,最常用的是线程池方法。
有一次,我们的在线服务器陷入僵局。即使遥控SSH也无法登录,我们只能无助地重新启动。每个人都发现,只要启动某个应用程序,这种情况就会在不到几分钟的时间内发生。引人发笑。
有一个同学不熟悉多线程,使用线程池来处理消息异步。通常,我们将使用线程池作为静态变量或成员变量。其他单词,每当有请求时,就会创建一个新的线程池。一旦请求增加,系统资源就会耗尽,最终会导致整个机器死亡。
如何避免这种问题?您只能使用代码进行检查。因此,即使是一个非常简单的同步关键字,多线程代码即使没有这种情况,也必须将其移交给有经验的人。,您需要非常仔细地查看这些代码。
与同步关键字Plus的独家锁相比,并发软件包中的锁可以提供更多的灵活性。您可以根据需要选择一个公平的锁,非fair锁,读取锁定和写作锁定。
但是,在用完锁后,有必要关闭它,即必须配对锁和解锁,否则锁定泄漏容易发生,导致其他线程始终获得此锁定。
例如,以下代码在调用锁后发生异常。尝试中的执行逻辑将被中断,解锁将永远无法执行。在这种情况下,线程获得的锁定资源将永远不会发布。
正确的方法是将解锁功能放入最终块中,以确保始终执行它。
因为锁定也是一个普通对象,所以它可以用作函数的参数。如果您通过函数之间的锁定,它也将被混淆为顺序。在通常的编码中,有必要避免这种锁定的情况作为参数。
作为Java的基类,对象提供了四种处理线程同步问题的方法。可以看出,等待工作等功能的状态有多高,在普通工作中,编写业务代码的学生的机会相对较小,因此一旦使用问题就很容易遇到问题。
但是,使用这些功能有一个很大的前提,也就是说,它必须与同步进行包裹,否则将抛出IllegalmonitorsStateException。例如,执行时,以下代码将报告错误。
使用时,类似的方法以及并发软件包中的条件对象也必须在使用时锁定和解锁功能之间出现。
为什么需要在等待之前同步此对象?因为JVM需要在执行等待时,线程需要保留此对象的监视器,显然同步关键字可以完成此功能。
但是,这还不够。等待函数通常放置在WALE循环中。JDK在代码中清楚注释。
重要的是:这是因为等待意味着当通知时,它可以向下执行逻辑。这是一种简单的方式,可以在while loop中编写。
等待并通知是否需要将条件包括在两层中,一层同步和一层。这是诸如等待之类的函数的正确用法。
使用同步关键字时,如果将其添加到普通方法中,则锁定为此对象。如果将其加载在静态方法上,则锁定为类。在该方法中,同步还可以直接指定要锁定的对象,锁定代码块以实现良好的锁定锁定控制。
如果涵盖了此锁的对象怎么办?例如以下内容。
上面的代码,因为在逻辑上强行分配锁定对象,将导致锁定或无效。
为了保险,我们通常将锁定对象声明为最终类型。
或直接声明专用的锁定对象,将其定义为普通对象。
这是一种要求在异步线程或长期执行时间中经常遇到的需求。我不再看到朋友的一部分不止一次。
这些暂停的基本原因是线的一条有问题,导致整个线程的死亡。
让我们看一下代码的模板。
在循环函数中,执行我们的真实业务逻辑。当执行某个任务时,会发生一个例外。这次,线程不会继续运行,但会直接抛出异常。当编写普通功能时,我们所有人都知道该程序的行为,但是一旦经过多线程,许多学生就会忘记这一部分。
值得注意的是,即使它是一种非捕捉类型,也会导致线程停止。因此,将逻辑执行在Try Catch中是一个很好的习惯。
Hashmap将在多线程环境中导致死亡周期。此问题非常受欢迎,因为它会产生非常严重的后果:CPU运行术,代码无法执行,在查看时,JSTACK在GET方法上被阻止。
至于如何提高哈希图的效率以及何时将红树和黑树转到列表中,这是扬丘恩baixue的八个摊位世界的主题。
互联网上有详细的文章来描述死周期问题的场景。通常,由于Hashmap是Rehash,它将形成一个环链。某些获取请求会出现在此铃声中。JDK认为这不是一个错误,尽管影响很差。
如果您判断您的收集类将由多线程使用,则可以使用线程-Safe ConcurRentHashMap替换它。
Hashmap还存在一个安全的删除问题,这与多线程无关,但它引发了ConturrentModificationException,看起来像一个多线程问题。
以上代码将引发异常,这是由于哈希玛普的失败速度机制所致。如果我们想安全地删除某些元素,则应使用迭代器。
对于线程安全类,编写的代码是否必须为线程-Safe?答案为负。
线程安全类仅负责其内部方法是线程安全性。如果我们将其包装在外部一层,无论它是否可以达到线程安全的效果,我们都需要重新解决它。
例如,在以下情况下,我们使用线程安全concurrenthashmap来存储计数。尽管contrenthashmap本身是安全的,但不会出现死亡循环的问题。但是,AddCounter函数显然是不正确的。它需要使用同步功能软件包。
这是开发人员经常踏上的位置之一。要实现线程安全性,您需要查看线程安全的范围。如果使用更大的维度的同步问题,即使使用了线程安全性集合,则将无法达到预期的效果。
挥发性关键字解决了变量的可见性问题,这可以使您修改并让其他线程立即读取。
尽管这件事在采访中询问了很多东西,包括优化confurnthashmap中队的挥发性。但在普通使用中,您实际上只能与布尔变量的价值接触。
请勿在计数或线程同步(例如以下)中使用它。
该代码在多线程环境中不准确。这是因为波动性仅保证可见性,不能保证原子质,并且多线程操作不能保证其正确性。
使用原子类或同步关键字有多好。您是否真的在乎这个纳米级级别之间的区别?
很多时候,日期处理也会发生。这是因为全局日历,SimpleDateFormat等。当多个线程同时执行格式函数时,将混淆数据。
为了改进,我们通常将SimpleDateFormat放在螺纹插座中,每个线程都可以避免一些问题。
void realjob(){
threadPoolExecutor exe = new ThreadPoolExecutor(...);
exe.submit(new runnable(){...})
} 6在构造函数中启动新线程,或在静态代码块中启动新线程,没有错误。但是,不建议您这样做。
因为java是继承的,如果您在构造函数中执行此操作,则子类的行为将变得非常神奇。此外,该对象可用于在结构完成之前将其用于另一个位置,从而导致一些不可预测的行为。
因此,将线程的启动置于常见方法(例如开始)是一个更好的选择。它可以减少错误的机会。
等待和通知很容易遇到问题,
编码格式的要求非常严格。相关的关键字相对简单,但是仍然有很多要注意同步代码块何时。这些体验在同时包装提供的各种API中仍然很实用。我们还需要处理在多线程逻辑中遇到的各种异常问题,避免中断,并避免死锁。避免使用这些坑,即使开始启动,基本上也编写了多线程代码。
许多Java开发刚刚暴露于多线程开发,并且在普通工作中的应用程序不多。如果您正在执行Crud的业务系统,那么编写一些多线程代码时会更少。但是总是有例外。您的程序变得非常慢,或者如果您检查某个问题,您将直接参与多线程的编码。
我们的各种工具和软件还使用大量的多线程。从tomcat,到各种中间件,缓存池连接池等,每个地方都充满了多线程代码。
即使经验丰富的开发也将陷入许多陷阱。由于异步混乱会导致时间安排,因此必须通过强制性均值将其与数据同步。Multi -Threaded操作必须首先确保准确性并使用线程安全集来存储数据存储;还要确保效率。毕竟,使用多线程的目标是这种情况。
我希望本文中的这些实际案例能够让您了解多线程级别。
作者:小姐姐妹口味链接:https://juejin.cn/post/700536939747106853