前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >3分钟速读原著《Java并发编程的艺术》(二)

3分钟速读原著《Java并发编程的艺术》(二)

作者头像
cwl_java
发布2019-10-26 20:43:03
3920
发布2019-10-26 20:43:03
举报
文章被收录于专栏:cwl_Javacwl_Java

第四章 java并发编程的基础

Java从诞生开始就选择了内置多线程的支持,线程作为操作系统调度的最小单元,多个线程能够同时执行,浙江显著提升程序性能,故此本章将着重介绍Java并发编程的基础知识

1.main线程

main线程下先启动下面4个线程

  • 1.1 分发处理发送给JVM信号的线程
  • 1.2.调用对象finallize方法的线程
  • 1.3.清楚Reference的线程
  • 1.4.Main线程,用户程序的入口
2.多线程的优点
  • 2.1 更多的处理器核心,可以缩短响应时间,提升用户体验
  • 2.2 更好的编程模型
3.线程的优先级

现代的操作系统基本都采用时分的形式调度运行的线程,操作系统会分出多个时间片,每个线程占有一个时间片,当时间片用完的时候,就会发生线程的调度,等待着下次的分配

Java线程中通过priority来控制优先级,默认的优先级是5,优先级高的线程分配时间片的数量要比优先级低的线程多,针对于频繁阻塞的线程需要设置比较高的优先级,偏重计算的线程则需要设置比较低的优先级,确保处理器不会被独占.

虽然Java对线程的优先级做了对应的优先级,但是有时候操作系统未必会买账,部分系统会忽略Java对于线程优先级的设定,例如Mac OS X10.10 对应java1.7.0_71就会忽略Java的线程优先级设置

4.线程的状态
  • 4.1 New (新建)
  • 4.2 Runnable(可运行)
  • 4.3 Waiting(无限期等待,附带超时等待的状态,类似于计时等待)
  • 4.4 Timed waiting(计时等待)
  • 4.5 Terminated(终止,线程执行Runnable.run()方法的时候就会进入终止状态)
  • 4.6 Block(阻塞,线程在没有获取到锁的时候就会进入阻塞状态)

注意:Java会将就绪和运行两个状态合并为运行状态 阻塞状态是线程阻塞在进入synchrionized关键字修饰的方法或者代码块时的状态,但是阻塞在javaconcurrent包中的Lock接口的线程状态却是等待状态,因为Lock接口对于阻塞的实现均使用了LockSupport的相关方法

5.Daemon线程

Daemon线程是一种支持型线程,因为他主要被用作程序中后台调度以及支持性 工作Daemon属性的配置需要在启动线程之前进行设置,不能再启动线程之后设置,Daemon线程中的finally代码块并不会一定执行,==>所以在构建Daemon线程的时候,不能依靠finally代码块中的内容来确保执行关闭或者清理资源逻辑

6.线程的启动和终止
  • 6.1 构造线程:一个新构造的线程对象是由其parent线程来进行空间分配的,而child线程继承了parent是否为Daemon,优先级和加载资源的contextClassLoader以及可集成的ThreadLocal,同时还会分配一个唯一的ID来标示这个child线程.至此,一个能够运行的线程对象才算初始化完成并且在堆内存中等待着运行
  • 6.2 启动线程:线程对象初始化完成之后,就可以调用start()方法就可以启动这个线程.
    • ①线程start()方法的含义:当前线程同步告知Java虚拟机,只要线程规划期空闲,应立即调用start()方法的线程
    • ②Java内存区域中程序计数器,是一块比较小的内存空间,可以看做是当前线程所执行的字节码行号的指示器,字节码解释器工作时,通过改变计数器的值,选取下一条执行的字节码指令
    • ③在启动一个线程之前,最好要为这个线程设置线程名称,这是阿里规范中的强制要求也是我们每个开发人员应该提供的一些提示
7.线程的中断
  • 7.1 中断时一个标识位属性,它表示一个运行中的线程是否被其他线程进行了中断操作,中断就是其他线程调用了interrupt()方法对其进行中断操作
  • 7.2 线程通过检查自身是否被中断来进行相应,线程通过方法isInterrupted()判断是否被中断
    • InterruptedException就是属于线程中断而产生的异常
8.线程中过期的suspend(),resume()和stop()
  • 8.1 Suspend()表示暂停线程的方法
    • 过期原因:线程暂定的时候不会释放已经占有的资源(例如锁),这样容易引发死锁
  • 8.2 Resume()表示恢复线程的方法
  • 8.3 Stop()表示停止线程的方法
    • ①暂停和恢复操作已经使用等待/通知机制来进行代替了
    • ②Main线程通过中断操作和cancel()方法均可以使用CountThread得以终止
9.线程之间的通信
  • 9.1 线程开始运行,都会拥有自己的栈空间,就如同一个脚本一样,按照既定的代码一步步执行,直到终止
  • 9.2 Java支持多个线程同时访问一个对象或者对象的成员变量,由于每个线程都可以拥有这个变量的拷贝,所以执行过程中,一个线程看到的变量并不一定是最新的
  • 9.3 关键字volatile
    • ①可以用来修饰字段,就是告知程序任何对该变量的访问均需要从共享内存当中获取,而且对它的改变必须同步刷新回共享内存,它能保证所有线程对变量访问的可见性
  • 9.4 关键字synchronized
    • ①可以修饰方法或者以同步代码块的形式来进行使用,它主要确保多个线程可以在同一个时刻,只能有一个线程处于方法或者同步块当中,它保证了线程对变量访问的可见性和排他性.
  • 9.5 任意线程对Object的访问,首先要获得Object的监视器.如果获取失败就会进入同步队列
10.等待/通知机制
  • 10.1一个线程修改了一个对象的值,而另一个线程感知到了变化,然后进行相应的操作,整个过程开始于一个线程,而最终执行又是另一个线程.前者就是生产者,后者就是消费者==>这就是生产消费模式
    • ①简单的实现办法就是在while循环当中设置不满足消费条件则退出,满足则不断进行消费的情况,从而完成消费者的工作,生产消费模式存在一定问题:
      • a) 难以确保及时性
      • b) 难以降低开销
  • 10.2 使用wait()/notify()/notifyAll()时需要注意的细节
    • ①使用wait()/notify()/notifyAll()都需要对调用对象加锁
    • ②使用wait()方法后,线程状态有Running编程Waiting,并且将当前线程放置到对象的等待队列
    • ③Notify()或notifyAll()方法调用之后,等待线程依旧不会从wait()返回,而是要等到调用notify()或者notifyAll()的线程释放了锁之后,等待的线程才有机会从wait()方法当中返回
    • ④Notify()方法将等待队列中的一个等待线程从等待队列中移到同步队列当中,而notifyAll()方法则是将等待队列中所有的线程全部移到同步队列,被动的线程状态由waiting变成了blocked
  • 10.3 等待/通知的经典范式
    • ①等待方遵循如下原则:
      • a) 获取对象的锁
      • b) 如果条件不满足,那么调用对象的wait()方法,被通知后仍要检查条件.
      • c) 条件满足则执行对应的逻辑
	Synchronized(对象){
        While(条件不满足){
            对象.wait();
        }
        对应的处理逻辑;
    }
10.Thread.join()的使用

当一个线程A执行了thread.join()语句之后,其含义是:当线程A等待thread线程终止之后才从thread.join()返回.线程Thread还提供了定时的等待方法

11.ThreadLocal的使用
  • 11.1 ThreadLocal就是线程变量,以ThreadLocal对象为键,以任意对象为值的储存结构,即是说明一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值
  • 11.2 可以通过对应的setter和getter来设置和取值
12.超时等待模式
  • 12.1 当调用一个方法时等待一段时间,如果该方法能够在给定时间段之内得到结果,那么就将对应的结果进行返回,反之,就是返回默认结果

备注: 数据库的连接池对应的等待超时模式,可以对应的对连接池中进行获取使用和释放连接的过程,而客户端获取连接的过程被设定为等待超时的模式,可以设置超时等待时间,也可以设置连接池的大小,最多访问数,最大空闲数等等,连接池其实本质就是一个线程池

13.线程池技术以及其示例
  • 13.1 对于服务端的程序,经常需要面对的是客户端传入的比较短小的任务,需要服务端快速处理并且返回结果.如果服务端每次接受到一个任务,就去创建一个线程,那么当免催成千上万的任务递交进服务器当中,就会导致操作系统频繁的进行线程上下文切换,无故的增加系统的负载,而线程的创建和消亡都是需要耗费系统资源的
  • 13.2 线程池的技术就是用于解决这样的一个问题的,它预先创建了若干数量的线程,并且不能由用户直接对线程的创建进行控制,在这个前提下重复使用固定比较为固定的数目的线程来完成任务的执行.这样做不仅消除了频繁创建和消亡线程的系统资源开销,而且,面对过量任务的提交能够平缓的劣化
  • 13.3 线程的execute(job)方法将job提交进入线程池执行,而客户端自身不用等待Job的执行完成
  • 13.4 在线程池的实现当中可以看出,当客户端调用execute(job)方法时,会不断的向任务列表jobs中添加Job,而每个工作者线程会不断的从jobs上取出一个Job进行执行,当Jobs为空时,工作者线程进入等待状态
  • 13.5 添加一个Job后,对工作队列Jobs调用了其notify()方法,所以其实线程池在本质上是使用了Jobs队列的方式进行执行每个添加到这个队列当中的线程,并且这个队列在线程初始化的时候就已经确定了对应的长度,添加一个Job之后,对工作队列Jobs调用了其notify()方法,而不是notifyAll()方法,此时使用notify()开销相对会比使用notiifyAll()方法获得更小的开销
  • 13.6 线程池的本质就是使用了一个线程安全的工作队列连接工作者线程和客户端线程,客户端线程将任务放入工作队列后便返回,而工作者线程则不断的从工作队列上取出工作并且执行.当工作队列为空的时候,所有的工作者线程均等待在工作队列上,当有客户端啊提交了一个任务之后,会通知任意一个工作者线程,随着大量的任务被提交,更多的工作者线程就会被唤醒
14.基于线程池技术的简单web服务器
  • 14.1 目前的浏览器都是支持多线程访问,比如在请求一个HTML页面的时候,页面中包含的图片资源,样式资源会被浏览器发起并发的获取,这样用户就不会遇到一直等待一个图片完全下载完成才能继续查看文字内容的尴尬情况
  • 14.2 如果web服务器是单线程的,多线程的浏览器也没有任何勇武之地,因为单线程情况下服务端还是一个请求一个请求的顺序处理,因此大部分web服务器都是支持并发访问的.常用的java web服务器,例如Tomcat Jetty,在其处理请求的过程中都使用到了线程池的技术
    • ①简单来说,目前的java web服务器都是支持多线程并发的,并且也都使用了线程池的技术来进行请求的处理,ThreadLocal是一个线程的变量,可以绑定在线程当中
  • 14.3 在浏览器发起请求之后HttpServer不会马上处理客户端的请求,而是将其包装成了HttpRequestHandler并且交由线程池处理
  • 14.4 Web服务器中线程池数量并不是越多越好,具体的数量需要评估每个人物的处理时间,以及当前计算机的处理能力和数量来进行评定,如果线程过少,就无法发挥处理器的性能,如果线程过多,就会增加系统的无故开销,起到了相反的作用
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-05-15 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第四章 java并发编程的基础
    • 1.main线程
      • 2.多线程的优点
        • 3.线程的优先级
          • 4.线程的状态
            • 5.Daemon线程
              • 6.线程的启动和终止
                • 7.线程的中断
                  • 8.线程中过期的suspend(),resume()和stop()
                    • 9.线程之间的通信
                      • 10.等待/通知机制
                        • 10.Thread.join()的使用
                          • 11.ThreadLocal的使用
                            • 12.超时等待模式
                              • 13.线程池技术以及其示例
                                • 14.基于线程池技术的简单web服务器
                                领券
                                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档