《java并发编程之美》第一章(主打一个陪伴学习)
看书总结记录,《java并发编程之美》,第一章。
1.1、什么是线程?
进程是代码在数据集合上的一次运行活动,是系统进行资源分配的基本单位。线程则是进程的一个执行路径,一个进程至少有一个线程,线程共享进程的堆、方法区等资源;但是每个线程又有自己独占的栈和程序计数器。
1.2、线程的创建
线程的创建有三种方法:
1、方法一:继承Thread类,重写run()方法。缺点是,java的单继承和任务(run())与代码没有分离,同一个任务逻辑要执行多个的时候,每一个都要创建一份任务代码。
2、方法二:实现Runnable接口(任务类),并重写run()方法,然后new Thread(Runnable)。java的接口可以实现多个,缺点是(方法一也存在),任务执行完成后无返回值。
3、方法三:通过FutrueTask来创建线程。先是创建任务类,具体为实现Callable接口,并重写call()方法,然后new Thread(Callable),任务结束之后有返回值。
1.3、线程的一系列方法
1、wait():想要一个线程去调用一个共享变量的wait()方式时,必须先获取到这个共享变量的监视器锁,不然会抛IllegaMonitorStateException。获取监视器锁的方法有两种:
a、执行synchronized静态代码块时,使用该共享变量做参数。
b、调用该共享变量的方法,并且该方法使用了synchronized修饰。
调用这个wait()方法后,线程会阻塞挂起,直到发生以下事件才会返回:
a、其它线程调用了该共享变量的notify()方法或者notifyAll()方法。(notify:通知)
b、其它线程调用了该线程的interrupt()方法中断,抛出中断异常(InterruptException)。
wait(long timeout):指定等待时间。
wait(long timeout,int nanos):当nacos>0时,timeout++。
2、notify():一个线程调用共享对象的notify()方法之后,会唤醒一个在该共享变量上调用的一个线程。和wait系列方法一样,它也需要这个线程先获取到该共享变量的监视器锁。
notifyAll():唤醒全部在该共享变量上调用的线程。
3、join():等待线程终止的方法,使用场景:比如多线程去获取资源,要全部加载完毕后,再汇总处理。
4、sleep():睡眠期间让出指定时间的执行权,不参与CPU调度,但是不会让出其所拥有的监视器资源,比如锁。睡眠期间,如果其它线程调用该线程的interrupt()方法,会抛出中断异常InterruptedException。
5、yield():线程调用这个方法,让出cpu执行权,当前线程回到就绪状态。
6、interrupt():其它线程调用当前线程的interrupt方法的话,会给当前线程设置一个中断标志,但是也仅仅是设置中断的标志,当前线程还是会继续执行下去。但是如果当前线程是出于一个wait()阻塞挂起,或者sleep()阻塞睡眠,或者join()阻塞的时候,当前线程会抛出中断异常InterruptException。
boolean IsInterrupted():检测当前线程是否被中断。
boolean interrupted():检测当前线程是否被中断,如果是中断的话,并会清理中断标志,并且它获取的是当前线程的中断标志,而不是调用这个方法的线程的中断标志。
1.4、线程死锁
死锁的产生必须具备的四个条件:
1、互斥条件:指线程对已经获取到的资源进行排它性使用,即该资源同时只由一个线程占用。如果此时有别的线程请求获取该资源,则请求者只能等待,直至占有资源的线程释放该资源。
2、请求并持有条件:指一个线程已经持有了至少一个资源,但又提出了新的资源请求,而且新资源已经其它线程占有,所以当前线程会阻塞,但是阻塞的同时并不释放自己已经获取了的资源。
3、不可剥夺条件:指线程获取到的资源在自己使用完之前不能被其它线程抢占,只能在自己使用完毕后,由自己释放。
4、环路等待条件:指发生死锁的时候,必定存在一个线程——资源的环形链。
避免死锁的方法:目前只有请求并持有和环路等待条件是可以被破坏的。比如可以让线程的资源请求顺序保持一致。
1.5、守护线程与用户线程
thread.setDaemon(true),可以将线程设置为守护线程。守护线程和用户线程的区别在于,当最后一个非守护线程结束后,JVM会正常退出,而不管当前的守护线程是否还没结束。
1.6、ThreadLocal
线程本地变量,如果创建了一个ThreadLocal变量后,那么在访问这个变量的时候,每个线程会复制一个变量到自己的本地内存。 所谓ThreadLocal变量就是保存在每一个线程的ThreadLocalMap中的。而这个map是也特殊的map,因为它的entry的key是一个弱引用。如果这个变量不再被其他对象使用时,可以自动回收这个ThreadLocal对象,避免可能的内存泄露。但是它的value是强引用,只有在线程thread回收时,它在会被回收。为了解决这个问题,就需要在这个变量使用完之后,调用remove()方法删除这个本地变量。
InheritableThreadLocal:继承自ThreadLocal,其提供一个特性,就是让子线程可以访问在父线程中设置的本地变量。比如子线程想要获取到父线程设置的用户登录信息等。
本专栏是本人看书——《java并发编程之美》的总结,主要是为自己做一个记录,也欢迎大家评论区留言讨论交流呀~