资讯专栏INFORMATION COLUMN

学习Java多线程的一些总结

coolpail / 2875人阅读

摘要:多线程环境下的一些问题安全性问题在没有正确同步的情况下,多线程环境下程序可能得出错误的结果。一些相关概念竞争条件多线程的环境下,程序执行的结果取决于线程交替执行的方式。而线程的交替操作顺序是不可预测的,如此程序执行的结果也是不可预测的。

入口
Java多线程的应用复杂性之如jvm有限的几个内存方面的操作和规范,就像无数纷繁复杂的应用逻辑建立在有限的指令集上。

如何写出线程安全的程序,有各种各样需要遵循的规则,如果硬是去记忆这些写法或者规则,就事倍功半了,最好是先学习原理,抓住问题的主干,再拓展细节,这也是大家公认的学习某种技术的方式。对于多线程的问题,java使用java内存模型 JMM来保证多个线程可以有效地,正确地工作。

学习的步骤可以分为:

关注大师的言行,跟随大师的举动——JUC包已经足够丰富,按照API规范正确使用。

和大师一起修行——理解多线程问题的由来,以及jvm给出的解决方案,需要理解java的内存模型JMM,以及JMM给出的线程工作内存与主内存交互的规则如何形成JMM的happens-before原则等。参考书有知名的《深入理解Java虚拟机》第12章,最重要的《JSR-133 Java内存模型与线程规范》以及《并发编程实战》第16章。

领悟大师的意境——JUC包的实现原理,volatile和CAS构筑了JUC包的基础类,AQS,非阻塞数据结构,原子变量,这些基础类又构建了JUC包的高层类,Lock,同步器,阻塞队列,并发容器,Executor等。理解了高层类的原理,能够心里有底地使用这些类,构建健壮的应用。

成为真正的大师——学习JUC包的实现,说不定哪天也能写出一样优秀的类。

一些背景知识:
1.操作系统中线程的实现

现代操作系统的线程主要有三种实现:内核线程实现,用户线程实现,混合实现

内核线程(KLT):线程表由内核维护,由内核完成线程的切换,内核通过调度器对线程进行调度,并将线程的任务映射到处理器上,每个内核线程可以视为内核的一个分身。程序一般不会直接使用内核线程,而是使用内核线程的一种高级接口——轻量级进程(LWP)(广义上来说,轻量级进程也是在用户空间的进程中的,所以也是一种用户线程)。LWP和KLT是一一对应的,是1:1的关系,因此也叫作一对一线程模型(1:1)。内核线程最大的特点就是,如果有轻量级进程发生了阻塞,不会影响整个进程的工作,内核会运行其他可运行的线程。缺点也是明显的:各种线程操作都需要系统调用,需要在用户态和内核态进行来回切换,代价高昂,而且因为占用内核空间,所以内核能支持的数量是有限的。

用户级线程(UT):狭义上的用户线程是指,完全建立在用户空间的线程库上,由所在进程实现管理的线程。最大的亮点在于可以在不支持多线程的操作系统之上实现多线程,如DOS,同时因为不需要切换到内核态,所以快速且低消耗,也能支持更大规模的线程数量。这种模型中,一个轻量级进程对应多个线程,因此叫做一对多线程模型(1:N),用户进程的优势在于不需要内核的支援,而因为没有内核的支援,所有的线程操作都需要用户自己处理,导致复杂性是其劣势。线程的创建,切换,调度都是需要考虑的问题,现在使用用户线程的程序已经越来越少了。java线程在JDK1.2之前,使用用户线程。

用户线程+轻量级进程的混合实现:使用轻量级进程作为用户线程和内核线程的桥梁的一种实现,用户线程和轻量级进程的比例不定,因此也叫多对多线程模型(N:M),UNIX家族中的Solaris提供了N:M的线程模型实现。

更详细的说明可以查看介绍操作系统的书籍。

2.java虚拟机中线程的实现

jdk1.2 之前,java使用的是称为“绿色线程”的用户线程,而在1.2中,线程模型替换为基于操作系统原生线程模型来实现

操作系统支持什么样的线程模型,很大程度上影响java的线程模型,windows和linux系统提供的线程模型是1:1的,所有这两个平台上的JDK使用的是1:1的线程模型,一个java线程映射到一个轻量级进程中,Solaris系统同时支持1:1和N:M,该平台中的JDK可以指定参数选择线程模型。

操作系统的线程特性会对线程的并发规模和操作成本产生影响,但是对java程序的编写和运行来说是透明的

3.多线程环境下的一些问题:

安全性问题:在没有正确同步的情况下,多线程环境下程序可能得出错误的结果。

活跃性问题:在多线程环境下,当某个操作应该继续执行却无法继续执行下去,就造成了活跃性问题,如:死锁,饥饿,活锁。

死锁:

活锁:

饥饿:

性能问题:线程的频繁切换将带来极大的开销,如:

保存和恢复执行上下文,丢失局部性

使用同步机制的时候,这些机制会抑制某些编译器优化以保证执行顺序,

如使用volatile保证可见性的情况下,使内存缓冲区中的数据无效,其他线程需要重新从主内存中加载

所有这些因素会带来额外的性能开销。

4.一些相关概念

竞争条件:多线程的环境下,程序执行的结果取决于线程交替执行的方式。而线程的交替操作顺序是不可预测的,如此程序执行的结果也是不可预测的。

状态:状态在多线程编程中是一个很核心的概念,因为线程安全性的核心就在于:对可变的共享状态的访问操作进行正确的管理。

非正式的定义:状态可以简单理解为存储在对象的域中的数据,下面的Counter类中的count就是一个Counter对象的状态。如果不能正确地访问和修改count,那么count的值就不具备正确性。状态也包括一个对象依赖的对象的域。

多线程的环境下,主要是可变的,共享的状态会导致安全性问题,可变意味着状态可以被修改,共享意味着可以被多个线程改变,自然而然的,有三种方法来解决这个问题:1.让对象不可改变 2. 让状态不可共享 3. 必须共享和改变的,使用某种机制来保证顺序——同步。

或者更加直接可靠的,不要给对象状态,一个没有状态的对象一定是线程安全的。

线程安全性:当多个线程访问某个类的时候,这个类始终能表现正确的行为,那么这个类是线程安全的。

        public class Counter {
            private long count = 0;
            public long getCount(){
                return count;
            }
        }

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69353.html

相关文章

  • Java开发

    摘要:大多数待遇丰厚的开发职位都要求开发者精通多线程技术并且有丰富的程序开发调试优化经验,所以线程相关的问题在面试中经常会被提到。将对象编码为字节流称之为序列化,反之将字节流重建成对象称之为反序列化。 JVM 内存溢出实例 - 实战 JVM(二) 介绍 JVM 内存溢出产生情况分析 Java - 注解详解 详细介绍 Java 注解的使用,有利于学习编译时注解 Java 程序员快速上手 Kot...

    LuDongWei 评论0 收藏0
  • 初学者福音!可能是最适合你Java学习路线和方法推荐。

    摘要:学习完多线程之后可以通过下面这些问题检测自己是否掌握,下面这些问题的答案以及常见多线程知识点的总结在这里。可选数据结构与算法如果你想进入大厂的话,我推荐你在学习完基础或者多线程之后,就开始每天抽出一点时间来学习算法和数据结构。 我自己总结的Java学习的系统知识点以及面试问题,已经开源,目前已经 35k+ Star。会一直完善下去,欢迎建议和指导,同时也欢迎Star: https://...

    yanest 评论0 收藏0
  • java

    摘要:多线程编程这篇文章分析了多线程的优缺点,如何创建多线程,分享了线程安全和线程通信线程池等等一些知识。 中间件技术入门教程 中间件技术入门教程,本博客介绍了 ESB、MQ、JMS 的一些知识... SpringBoot 多数据源 SpringBoot 使用主从数据源 简易的后台管理权限设计 从零开始搭建自己权限管理框架 Docker 多步构建更小的 Java 镜像 Docker Jav...

    honhon 评论0 收藏0
  • 超详细Java面试题总结(二)之Java基础知识篇

    摘要:超详细的面试题总结一之基本知识多线程和虚拟机创建线程有几种不同的方式你喜欢哪一种为什么继承类实现接口应用程序可以使用框架来创建线程池实现接口。死亡线程方法执行结束,或者因异常退出了方法,则该线程结束生命周期。死亡的线程不可再次复生。 超详细的Java面试题总结(一)之Java基本知识 多线程和Java虚拟机 创建线程有几种不同的方式?你喜欢哪一种?为什么? 继承Thread类 实现R...

    wangjuntytl 评论0 收藏0

发表评论

0条评论

coolpail

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<