深入浅出java多线程(一)

昨晚看到Guide哥推送的《深入浅出Java多线程》,今天一鼓作气看了9节,虽然看得很疲倦,但是对多线程的基本操作和一些原理还是有不少了解;
进程与线程的区别开始,进程是由操作系统分配内存与其他资源(如io),线程是操作系统能够进行运算调度的最小单位。
线程创建的2种方式,继承Thread和实现Runnable,其中Thread使用到了装饰者模式,装饰Runnable对象,扩展Runnable的功能,又使用到了策略模式,Thread中的Runnable的各种实现就是一种策略;
具体使用哪个可以从2个方面考虑:
1、继承方式还是实现方式;
2、是否需要使用Thread类的诸多方法;
这两种线程的执行都是没有返回值的,如果需要返回值可以使用JDK提供的Callable接口和Future接口,需要配合ExecutorService#submit(Callable c) : Future 使用,返回值通过Future#get() : T 获取,get()方法会阻塞当前线程,直到得到返回结果;Future接口中定义了取消线程的cancel()方法,所以如果想取消线程的话可以使用Future的实现类FutureTask,cancel()操作内部实际上是执行Thread#interrupt()方法,所以调用此方法取消并不一定能取消成功。
昨天总结写的比较晚,写到12点多硬是撑不住了,今天将昨天未总结完的补上。
线程的优先级可以调用Thread#setPriority(int i)来设置优先级,取值范围1~10,然而并不是优先级高的线程一定比优先级低得先执行,具体还是由操作系统决定。
线程必须存在于线程组ThreadGroup当中,线程的优先级不能大于所在线程组的最大优先级,如果超过将会被线程组的最大优先级取而代之;
线程中还有一个守护线程(Deamon),可以通过Thread#setDaemon(boolean on)来设置,只有当所有非守护进程结束后,守护线程才会自动结束。

接下来是线程的6种状态以及它们之间的转换:NEW、RUNNABLE、BLOCKED、WATTING、TIMED_WATTING、TERMINATED
NEWRUNNABLE调用start(),如果有其他线程拿到锁了,则进入BLOCKED状态;
RUNNABLEBLOCKED 等待锁
RUNNABLEWAITING调用当前锁调用wait()、其他线程调用join()
RUNNABLETIMED_WAITING 调用wait(time)、join(time)、sleep(time)
WAITING/TIMED_WAITINGRUNNABLE调用Object.notify()、notifyAll()、LockSupport.unpark;
需要注意的是线程调用start()方法必定会进入RUNNABLE状态,遇到锁后才有可能进入BLOCKED状态;

接下来是线程间的通信,java线程机制主要采用锁+同步的方式来进行线程间的通信
Synchronized:同步标识,需要一个对象做为锁,且只能是Class对象或者Object对象;如果是静态方法中使用这个默认为该类.class,如果是普通方法标记则默认为this,同步代码块需要通过参数传入锁,如果两段同步的锁相同,那么线程可以从第一段同步执行完后,直接进入第二段同步代码,不需要竞争锁,因为锁只能被一个对象持有;
join():当一个线程调用join后,那么其他线程必须进入WAITING等待这个线程执行完毕后才会执行,主线程也不例外;
Object#wait()Thread#Sleep(long t)区别
1、wait()会释放锁,而sleep()不会;
2、wait()只有在拿到锁的情况才能执行,而sleep()在线程中随时可以执行;

接着作者提到了ThreadLocal类主要是为每个线程创建一个副本,用来存储线程自己的私有变量;

呼啦啦,这才总结了一半,不总结还真不知道昨天学了多少知识!总结写的好心累啊,一方面想着学习进度,一方面手头还有些工作要弄,不管了,加油吧!少年!

下面是原理篇,主要是java内存模型、volatile关键字、锁的几种类别,先从java内存模型讲起吧,
java运行时内存主要由方法区、堆、虚拟机栈、本地方法栈、程序计数器这无大部分组成,对于每个线程来说,堆是共有的,而栈是私有的,每个线程都含有一个本地内存,保存着该线程使用的堆空间中共享变量的副本,线程只与本地内存交互,而本地内存什么时候更新到共享内存由虚拟机控制。线程间的通信必须通过共享内存来进行通信
讲到共享内存通信,那不得不提volatile关键字,其作用主要有2个:
1、内存共享:当一个线程对volatile修饰的变量进行写操作时,java运行模型会立即把该线程对应的本地内存中的值刷新到主内存中,当一个线程对volatile修饰的变量进行读操作时,java运行模型会将本地内存中的值设置成无效,并从主内存中读取;
2、防止指令重排;
讲到volatile关键字不得不提单例模式双重检查+锁模式,其中单例变量就使用了volatile关键字标记,而它的作用主要就是防止指令重排,因为new Object()操作在编译器内有3条指令:分配内存、初始化、赋值给变量,而如果还没有实例化就复制给了变量,这是另一个线程刚好进入到第一层检查,那么就会直接返回一个未初始化完成的对象;

最后,了解到了同步锁还存在升级机制,而锁的状态也分为4种:无锁状态、偏向锁、轻量级锁、重量级锁
既然讲到锁,那得弄清楚锁存在哪个地方,锁都是基于对象的,锁信息存放在java对象头中,
java对象头占用2个字宽,如果是数组则占用3个字宽,多一个字宽用来存储数组的长度,一个字宽对应操作系统的位数,如果是64位操作系统中,那java对象头就占了128位,这128位,主要存储2种数据,hashCode+锁信息、类型数据指针;
对象头中就保存着锁的状态,线程在第一次进入同步代码块时,会将线程ID保存到锁的对象头中,并且把锁设置为偏向锁的信息也保存进去;
线程再次进入同步代码块中时,会去检查锁对象头里的Mark Word信息,如果锁的对象头中Mark Word里已经有线程ID了,且线程ID等于自己时,那说明该线程已经获得了锁,并且不需要花费CAS(compare and swap)操作来进行加锁和解锁,那么这个锁还是偏向锁;
如果线程ID不等于自己的线程ID,那说明有另一个线程来竞争这把锁了,这个时候会尝试CAS操作来替换Mark Word里的线程ID和锁类型,CAS操作返回成功和失败,如果成功,则表示之前线程已经不存在了,那么替换ID,不升级锁;如果失败,说明之前的线程还存在,这时需要暂停之前的线程,将锁升级为轻量级锁(这个过程有一定开销);
还是刚刚那个线程,在讲偏向锁升级为轻量级锁后,会自旋竞争锁,如果超过一定时间获取不到,就会进入阻塞状态,CAS会将锁升级为重量级锁,未竞争到锁的线程将会进入阻塞状态,阻塞状态不消耗CPU资源,但是线程间状态的转换需要相对较长的较长的时间,所以重量级锁效率较低

呼!终于完了,最后再来总结下这三种锁的区别和使用场景吧。

1、偏向锁:加锁和解锁操作不需要额外的消耗,速度接近非同步方法,但是线程竞争锁导致锁升级会造成额外的消耗,适用于多线程中同一时间段内只有一个线程访问同步代码块的情况;
2、轻量级锁:竞争锁不会阻塞,提高了程序的响应速度,但是始终得不到锁的线程会自旋消耗CPU资源,适用于追求响应速度的情况;
3、重量级锁:线程竞争不使用自旋,不会消耗CPU,但是线程阻塞,响应时间缓慢,同步块执行速度较长,适用于追求吞吐量的情况。

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

推荐阅读更多精彩内容

  • 第6章类文件结构 6.1 概述 6.2 无关性基石 6.3 Class类文件的结构 java虚拟机不和包括java...
    kennethan阅读 913评论 0 2
  • 除了充分利用计算机处理器的能力外,一个服务端同时对多个客户端提供服务则是另一个更具体的并发应用场景。衡量一个服务性...
    胡二囧阅读 1,327评论 0 12
  • 一、线程状态转换新建(New)可运行(Runnable)阻塞(Blocking)无限期等待(Waiting)限期等...
    达微阅读 570评论 1 2
  • 九种基本数据类型的大小,以及他们的封装类。(1)九种基本数据类型和封装类 (2)自动装箱和自动拆箱 什么是自动装箱...
    关玮琳linSir阅读 1,882评论 0 47
  • Java SE 基础: 封装、继承、多态 封装: 概念:就是把对象的属性和操作(或服务)结合为一个独立的整体,并尽...
    Jayden_Cao阅读 2,103评论 0 8