学习Java多线程的一些总结

发布时间:2019-11-19 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了学习Java多线程的一些总结脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

入口

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;             }         }

脚本宝典总结

以上是脚本宝典为你收集整理的学习Java多线程的一些总结全部内容,希望文章能够帮你解决学习Java多线程的一些总结所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。