java编程详解_《Java高并发编程详解》基础(一)

读《Java高并发编程详解》笔记,这本书笔者的收获很大,配合王文君汪老师的视频教学基本上可以将线程学的很透彻,很感谢前辈带给的这些智慧结晶。以下未我读本书和看视频对书的一些摘录和总结,以及一些补充。顺序按照书的思路走的。

本篇是最基础的部分,笔者花了很长时间去学习,看视频,敲实例,同时对照着java8的官方文档,再点进去学习源码,旨意是完全搞懂。笔者认为基础知识学习多久都是值得的。

1.线程

定义:

进程是操作系统动态执行的基本单元,每一个进程至少要有一个线程运行,线程是执行进程的基本单元。

线程生命周期:

b5efe8d33676?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

网图-线程生命周期

①. 创建状态(New):当线程对象创建后,即进入创建状态。

new关键字仅为创建一个线程类,在没有调用start()方法前,它就是一个普通的java类。

②. 就绪状态(Runnable):调用start()方法,线程即进入就绪状态。处于就绪状态的线程,随时等待cpu调度执行。

当执行start()方法后,才会在jvm中创建线程,此时线程等待cpu调度运行。除去意外失望,该状态只能进入Running状态。

③. 运行状态(Running):当CPU调度处于就绪状态的线程后,线程才得以执行。

此时线程根据不同的指令可转换转台为Runable,Blocked和Dead状态。①.cup轮询调度和yeild()进入Runable.②.sleep()/wait()/join()方法进入Blocked.③.执行完毕进入Dead.

④. 阻塞状态(Blocked):处于运行状态的线程,由于原因,暂时放弃对cpu的使用权,停止执行,此时进入阻塞状态,直到进入就绪状态。

此时线程可转换成Dead和Runable状态。①.线程意外死亡进入Dead。②.线程被其他线程notify/notifyall方法唤醒,完成休眠,interrupt打断等进入Runnable.

⑤. 死亡状态(Dead):线程执行结束,或异常退出,结束生命周期。

线程的设计模式

3.1. Thread中的模板设计模式

thread启动调用过程

1.当Thread处于Runable状态,此时调用start()方法启动线程。

2.start()方法调用本地方法栈(JNI)中方法start)()。

3.由JNI的start0()方法调用Run()方法,执行线程逻辑。

4.线程在运行状态和死亡状态再次调用start()方法,会抛IllegalThreadStateException异常

模板设计模式应用

1.由父类抽象出一个模板,而子类通过重写该方法而重写实现具体逻辑,其中父类控制逻辑调用。

2.模板模式主要由抽象模板(Abstract Template)角色和具体模板(Concrete Template)角色组成。父类实现模板方法以final修饰,子类实现模板方法所控制的具体的操作方法

3.Thread的run和start就是一个模板设计模式的设计。

3.2. 使用Runable()接口的策略模式

Runable接口

1.由于多个线程所实现的run方法控制具体逻辑,每次创建线程的时候都需要隐式创建该方法,故此java提供一个接口Runnable来将线程的控制和业务逻辑分离开。

2.创建线程只有一种方式,即构造Thread类,而实现现成的执行单元有两种方式,第一种是重写Thread的run方法,第二种是实现Runnable接口的run方法,并将Runable接口实例用作构造Thread的参数。

3.策略模式的目的是将线程的控制本身和业务逻辑的运行分离开,达到职责分明,功能单一的原则。

4.Thread类的run方法不能共享,而实现Runnable的实例则可以构造不同的Thread实例。

ps:

2.Thread构造函数

线程名

1.在构造Thread时,可以手动为该线程命名。

2.若创建线程没有命名,默认以"Thread-"作为前缀与一个以0开始自增的数字进行组合,这个自增函数在整个jvm进程中将不断自增。

3.若同时创建2个线程,第一个线程使用指定名,第二个线程使用默认名,则第二个线程以"Thread-1"命名,即在jvm中没有一个线程,命名数字都将自增,不论是否适用该线程默认名。

4.mian()线程的线程名为"mian"。

5.在线程启动前,可以通过方法setName()修改线程名,但是启动后,名字不再被修改。

6.静态方法:Thread.currentThread().getName() 获取线程名

线程父子关系

一个线程必然是由另一个线程创建出来的,而主线程mian是由jvm创建出来的。

线程与线程组关系

1.每一个线程创建的时候,都会分配线程组,若构造线程的时候,未显示的指定线程组,则该线程回默认与其父线程同一线程组。

2.mian线程的线程组名为mian。

3.若子线程与父线程同一个线程组,其与父线程的优先级一样。

线程与jvm虚拟机栈

守护线程

1.设置守护线程:setDaemon()

2.设置守护线程在线程的启动前才会起作用。可以使用isDaemon判断该线程是否为守护线程

3.线程死亡后,设置setDaemon会抛出异常

3.线程API

sleep方法

1.使用Thread.sleep()会使线程进入Blocked状态,直至休眠完成。

2.jdk1.5以后可以使用更加灵活的枚举类TimeUnit来替代该方法。

3.sleep与wait的区别

1.sleep是Thread的方法,休眠是并不释放资源,不需要定义同步(synchronized),不需要唤醒。

2.wait是Object方法,休眠会释放资源,允许其他线程访问,需要定义同步,需要使用notify()/notifyAll()唤醒。

yield方法

1.使用yield方法可以式当前线程从Running状态进入Runable.方法不常用。

2.CPU在资源不紧张的情况下会自动忽略该方法。

3.当CPU对该线程执行了yield方法,该线程将从新位于调度队列中,和其他线程一样拥有获得CPU调度权的机会。

4.yield与sleep的区别:

1.CPU收到sleep指令后,线程一定会进入Blocked状态,此时该线程仍然占据资源。

2.CPU接受到yield命令后,根据资源使用情况可以选择不执行,若执行,则线程释放资源,,进入可执行队列,且该线程可以从新竞争CPU使用权。

线程优先级

1.线程优先级范围(1

math?formula=%5Cleq value

math?formula=%5Cleq 10)。

2.mian线程优先级为5,线程不设置优先级,默认为5。

3.由于优先级设置也是一个提示操作(和yield类似),所以在CPU空闲时,不起作用,而对于非root权限用户,也会被忽略。

4.由于优先级的特性,所以在设置事务的先后以及重要性上,不要绑定优先级。

5.thread设置优先级:setPriority(num);线程组设计优先级:group.setMaXPriority(num)

6.若某线程组中的线程的优先级大于线程组的优先级,则该线程的优先级最大为该线程组优先级。

interrupt方法

1.调用interrupt方法会是陷入堵塞(Blocked)状态的线程被打断阻塞。

2.对于死亡的线程,该方法将被忽略。

3.每一个线程内部都有一个标志,interrupt flag,来标识当前线程是否被打断,该标识默认为flag=flase

4.当线程被打断时,会抛出InterruptedException异常,可以通过捕获异常来处理后续操作,是否继续执行该线程和结束该线程。

5.使用interrupt方法打断线程,可以使用isInterrupt()方法(对象方法),或使用interrupted方法打断线程(静态类方法)方法判断线程打断状况。

6.使用isInterrupt()方法(对象方法)判断线程是否被中断:

①:若打断正在执行(Running)的线程(未堵塞状态),该线程的中断信号会变为true,但是该方法并不起作用,线程也不会被打断Running状态。

②:若打断正在堵塞状态(Blocked)的线程,则会抛出InterruptedException异常,线程并不会死亡,是否结束和处理接下来的逻辑事务看程序的需要,此时中断信号会变成true,但在抛出异常后恢复为false,程序此时输出标识为false,避免影响接下来线程方法的调用。

7.使用interrupted方法打断线程(静态类方法)判断线程是否被中断。情况大致与上述相同,唯一不一样的是,第二种情况下的,中断信号在本次打断堵塞后一定会变为true,程序此时第一次输出标识为true,之后会恢复为false。仅此区别而已。

join方法

1.只有该线程启动后,才能调用join方法。

2.join方法前启动的线程,无法被join方法约束,只有在join后启动的线程,会在调用join方法的线程后执行。

3.某线程执行join方法,则该线程所在的主线程main线程会等待执行,知道主线程中所有的有join方法的线程执行完毕。

PS:join方法详解

关闭线程

1.若线程中有执行可中断方法(阻塞等)操作,可以通过捕获中断信号来决定是否退出。

2.由于线程标识可能被擦拭,或者没有可中断方法,使用volatile修饰的开关flag关闭线程

4.线程同步

synchronized关键字

①.作用:实现简单策略防止线程干扰和内存一致性错误,若对象对多个线程可见,则对该对象的读写将通过线程同步的方式进行。

②.表现:某线程获取与mutex(互斥)关联的monitor(监控)锁,确保共享变量的互斥访问,即同一时刻只有一个线程方法同步资源。从而避免数据不一致的问题。

③.synchronized可以对代码块或方法进行修饰,但是不能对class以及变量修饰。

synchronized本质

1.每一个对象都与一个monitor相关联,该monitor的锁只能被一个线程同一时间获取,其他线程进入阻塞状态。

2.对象的monitor锁都有一个计数器,初始为0,表示未被任何线程获取,若某线程获取该锁后,对计数器加一,此时该线程为该对象monitor的所有者。此时其他线程会陷入堵塞状态,知道monitor计数器变为0,参与从新竞争该锁。

3.若已经拥有该monitor的线程重入,会导致monitor计数器再次累加。

4.当monitor计数器减为0后,此时该线程释放锁的所有权,并和其他线程再次参数对锁的竞争。

This Monitor(对象锁)

1.即某个类中的对象方法或对象方法中的代码块被synchronized修饰

2.若同一个类中有多个对象方法或对象方法中的代码块被synchronized修饰,若多个线程同时访问一个该类的实例对象的不同方法,则只有一个线程能够访问,因为一个对象实例的对象方法是同一个monitor锁。

Class Monitor(类锁)

1.即某一个类中有多个静态方法或静态方法中的代码块被synichronized修饰.由于静态方法又称类方法,所以该锁称之为类锁,并不是关键字修饰在类上。

2.若同一个类中有多个静态方法或静态方法中的代码块被synichronized修饰,若多个线程同时访问该类不同静态方法,则只有一个线程能够访问,因为该类的静态方法使用同一个monitor锁。

死锁

1.死锁情况

①.交叉死锁,即A线程获取R1锁,等待获取R2锁,同时B线程获取R2锁,等待获取R1锁。

②.假死死锁,CPU占用率升高,难以检测。

②.内存不足,即A,B线程执行需要占用30内存,此时A占据10,B占据20,彼此等待释放内存。

③.一问一答式数据交互,客户端发送请求,服务端由于某种原因错过此时请求,之后客户端等待回复请求,服务端也等待请求。

④.数据库锁,文件锁,死循环引起假死的死锁

2.死锁检测

①.交叉死锁:一般交叉死锁会陷入Blocked状态,CPU资源占用不高,很容易借助工具实现。

5.线程通信

6.线程组

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值