并发系列1 Java并发编程基础

参考:
《Java并发编程的艺术》第四章
《Java多线程编程核心技术》
博客 https://www.jianshu.com/p/8a04b5ec786c Java多线程基础
博客 https://www.jianshu.com/p/12af2d966c13 Java并发编程1

一.线程简介

1.线程和进程
  • 进程是系统进行资源分配和调度的一个独立单位,现代操作系统运行程序时会创建进程
  • 线程也叫轻量级进程,是现代操作系统调度的最小单元,一个进程中可以有多个线程,每个线程都拥有各自的计数器、堆栈和局部变量等属性,并能够访问共享的内存变量
2.为什么使用多线程
  • 更多的计算核心:充分利用多核处理器的硬件优势,将计算逻辑分配给多个处理器同时执行
  • 更快的响应时间:在业务逻辑复杂的场景中,将数据一致性不强的操作派发给其他线程处理
  • 更好的编程逻辑:Java提供了良好并且一致的多线程编程模型,方便开发者完成多线程开发
3.上下文切换
  • CPU切换运行的线程时存储和恢复CPU的过程,使得线程能够从中断点恢复执行
  • 线程上下文切换过程中会记录程序计数器、CPU寄存器状态等
  • 多线程环境中上下文切换会带来一定的性能开销
4.线程优先级
  • 现代操作系统采取分时的形式调度执行的线程
  • 线程在获取操作系统分出的时间片后开始执行,时间片用完后停止执行,等待再次获得时间片
  • 线程优先级决定线程获取CPU时间片的优先级
  • 注意:Java线程优先级在某些操作系统中无效
5.线程的状态
状态名称 说明
NEW 初始状态,线程被构建,但还没有调用start()方法
RUNNABLE 运行状态,包括线程在操作系统中就绪和运行两种情况
BLOCKED 阻塞状态,表示线程阻塞于锁
WAITING 等待状态,进入等待状态的线程需要等待其他线程的特定动作(通知或中断)
TIME_WAITING 超时等待状态,进入超时等待状态的线程可以在指定的时间自行返回
TERMINATED 终止状态,表示当前线程已经执行完毕
线程状态切换
6. Daemon线程
  • 支持型线程,用作程序中后台调度以及支持型工作
  • JVM不存在非Daemon线程时,Daemon线程将自动结束,JVM退出
  • 注意,在JVM退出时,Daemon线程中的finally块可能不会执行

二.线程启动和终止

1.构造线程
  • void init(ThreadGroup g, Runnable target, String name,long stackSize,AccessControlContext acc)方法完成线程构造
  • 新构造的线程对象由其parent线程进行空间分配,继承了parent线程的Daemon、优先级和加载资源的contextClassLoader以及可继承的ThreadLocal,同时获得唯一的线程ID
2.线程的实现
  • 继承Thread类,重写run()方法,Thread类本身实现了Runnable接口
  • 实现Runnable接口,重写run()方法
  • 使用ExecutorService、Callable、Future实现有返回结果的多线程
3.线程中断
  • 一个线程应该自行停止,而非由其他线程强制中断或停止
  • Thread.stop()不保证资源的正确释放、Thread.suspend()暂停时不释放锁容易导致死锁、Thread.resume()等三个方法都已废弃
  • 每个线程均有一个中断标志位,表示是否有其他线程对该线程进行了中断操作
  • 当对一个线程调用interrupt()方法时
    1)如果线程处于等待状态(如sleep、wait、join)时,线程将立即退出等待状态,并抛出一个interruptedException异常,仅此而已
    2)如果线程处于正常活动状态,会将该线程的中断标志设置为true,仅此而已。被设置中断标志的线程将继续正常运行,不受影响
  • 所以interrupt()并不能真正的中断线程,需要被调用的线程进行配合。如果一个线程有被中断的需求,可以这样做
    1)在正常运行任务时,使用isInterrupted()方法经常检查本线程的中断标志位,如果被设置了中断标志就自行停止
    2)线程处于等待状态时,catch到InterruptedException异常后退出线程
  • Thread.interrupted()方法将清除中断标志位,但并不代表线程又恢复,仅代表线程已响应该中断信号然后重置为可再次接收信号的状态
  • 处于等待的线程在调用interrupt()方法后抛出InterruptedException前,JVM将先清除线程的中断标志位
Modifier and Type Method Description
void interrupt() Interrupts this thread
static boolean interrupted() Tests whether the current thread has been interrupted
boolean isInterrupted() Tests whether this thread has been interrupted

三.线程间通信

1.volatile和synchronized
  • Java支持多个线程同时访问共享对象,现代多核处理器为了加速程序运行,每个线程会拥有共享对象的一份拷贝,由此引出内存可见性问题——一个线程看到的变量并一定是最新的
  • volatile:修饰的字段(成员变量),要求程序对该变量的访问必须从共享内存获取,修改必须同步刷新回共享内存,从而保证所有线程对变量访问的可见性
  • synchronized:修饰方法或同步块,确保同一时刻,只有一个线程处于方法或同步块中,保证了线程对变量访问的可见性和排他性
  • 对象、对象的监视器、同步队列和执行线程之间的关系
    1)任意线程对由synchronized保护的object的访问,首先要获得object的监视器Monitor
    2)如果Monitor获取失败,线程进入同步队列SynchronizedQueue,线程为BLOCKED状态
    3)当其他获得锁访问object的线程释放锁,该释放操作唤醒阻塞在同步队列中的线程,使其重新尝试获取object的Monitor
2.等待/通知机制
  • 生产者消费者模式
    1)线程A修改了一个对象的值,线程B在感知变化后进行相应的操作
    2)整个过程开始于线程A(生产者),最终执行于线程B(消费者)
    3)该模式隔离了“做什么”(What)和“怎么做”(How),功能层面实现了解耦
  • 原始办法
    消费者线程不断循环检查信号变量是否变化,伪代码如下,存在程序及时性和资源消耗量的两难
while ( value != desire )  {
    Thread.sleep ( 1000 ) ;
}
doSometing();
  • 等待/通知机制 notify/wait
    1)线程A调用对象O的wait()方法进入等待状态
    2)线程B调用对象O的notify()notifyAll()方法通知线程A
    3)线程A收到通知后从对象O的wait()方法返回,继续执行后续操作

  • 等待/通知机制流程
    1)使用wait()notify()notifyAll()时需要先对调用对象加锁
    2)调用wait()后,线程状态由RUNNING变为WAITING,并将当前线程放置在等待队列
    3)notify()notifyAll()方法调用后,等待线程依旧不会从wait()返回,需要调用notify()notifyAll()的线程释放锁之后,等待线程才会从wait()返回
    4)notify()/notifyAll()将等待队列中的一个/全部等待线程从等待队列中移到同步队列,被移动的线程状态由WAITING变为BLOCKED
    5)从wait()方法返回(离开同步队列开始运行)的前提是获得了调用对象的锁

    Wait/Notify

  • 等待/通知的经典范式

等待方伪代码
1.获取对象的锁
2.如果条件不满足,则调用对象的wait()方法,被通知后仍要检查条件
3.条件满足则执行对应逻辑
synchronized(对象)  {
      while(条件不满足)  {
            对象.wait();
      }
      对应的处理逻辑
}

通知方伪代码
1.获取对象的锁
2.改变条件
3.通知所有等待在对象上的线程
synchronized(对象)  {
      改变条件
      对象.notifyAll();
}
3.管道输入/输出流
  • 管道IO主要用于线程间数据传输,传输媒介为内存,与文件IO或网络IO不同
  • 主要实现类
    1)管道字节流:PipedOutputStreamPipedInputStream
    2)管道字符流:PipedReaderPipedWriter
PipedReader in = new PipedReader();
PipedWriter out = new PipedWriter();
//必须连接输入流和输出流,否则抛出异常
out.connect(in);
4.Thread.join()
  • 线程A使用thread.join()表示当前线程A等待thread线程终止后才从thread.join()返回
  • join()方法的源代码逻辑结构与等待/通知经典范式一致,即加锁、循环和处理逻辑三步
5.线程变量ThreadLocal
  • 以ThreadLocal对象为键、任意对象为值得存储结构,依附于线程
  • 线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值

那年离别日,只道住桐庐。桐庐人不见,今得广州书

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,825评论 4 377
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,887评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,425评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,801评论 0 224
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,252评论 3 299
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,089评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,216评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 31,005评论 0 215
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,747评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,883评论 2 255
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,354评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,694评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,406评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,222评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,242评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,017评论 2 281

推荐阅读更多精彩内容