Java多线程并发编程学习笔记【基础知识1】

该系列博文大多为阅读《java并发编程之美 翟陆续、薛宾田著 》的笔记

一、线程状态

线程总共有五种状态:新建(newThread)、就绪(runnable)、运行(running)、死亡(dead)、阻塞(blocked)。

0、线程状态的转换 

其中等待队列是阻塞状态,不过不是调用的是object类的方法,而不是Thread类的相关方法。

1、wait()方法,Object类的方法

当一个线程调用一个共享变量的wait()方法时,该调用线程会进入阻塞状态,直到发生下面两种情况才会返回:

  • 其他线程调用该共享对象的notify()或者notifyAll()方法;
  • 其他线程调用了该线程的interrupt()方法,抛出InterruptException异常

想要调用wait()方法,需要现要获取对象的监视器锁,否则会报IllegalMonitorStateException。

而获取监视器锁的方式有两种

 调用wait()方法后,会释放该共享变量上的锁,当前线程其他共享变量的锁不会被释放。然后会等待其他线程调用共享变量的notify()或者notifyAll()方法,重新竞争获取共享变量的锁之后,再去执行wait()方法后面的代码。

2、notify()和notifyAll()方法,Object类的方法

调用共享对象的notify()方法后,会唤醒一个在该共享变量上调用wait()方法被挂起的线程。而notifyAll()会唤醒所有在该共享变量上调用wait()方法被挂起的线程。

3、join()方法,Thread类的方法

调用对象线程的join()方法后,当前线程会进入阻塞状态直到对象线程死亡才会往下执行,比如用于等待多个线程资源加载完成。

比如线程A调用线程B的join()方法,线程A称为当前线程,B成为对象线程,线程A会进入阻塞状态(不是B线程进入等待状态),等待B线程的返回 。

4、sleep()方法,Thread类的方法

让当前线程从运行态转变为超时等待状态(timed waiting)(阻塞态一种),暂时让出指定时间的cpu调度权,但不会释放拥有的监视器资源,比如锁。时间结束后继续参与cpu的调度。

5、yield()方法,Thread类的方法

当前线程请求让出自己的cpu,然后重新排队获取cpu时间片,获取到时间片后再继续执行。与sleep()不同的是,线程会进入就绪状态,而不是阻塞状态。这也就造成了yield()方法可能是一厢情愿,cpu可能还是会优先执行该线程。

6、线程中断相关方法,Thread类的方法

  • void interrupt():设置对象线程的中断状态为true,对象线程并不会因此被中断,而是会继续执行下去。直到对象线程调用了wait系列函数、join方法或者sleep方法进入阻塞状态时,调用的地方将会抛出InterruptedException异常而返回。
  • PS:因为wait、join、sleep方法是会使当前线程进行相应的操作,所以在A线程是无法使B线程进行这些动作的,只能在B线程里面进行调用才有相应的效果。
  • boolean isInterrupted():返回对象线程的中断标志
  • boolean interrupted():返回当前线程的中断标志,并且重置标志为false。比如说当前线程中断标志为true,第一次调用返回true,第二次返回false。

二、死锁

死锁是指两个或者两个以上的线程在执行过程中,因争夺资源而造成的互相等待的现象。比如线程T1拥有资源R1,过程中需要等待R2资源;线程T2拥有R2资源,却等待R1资源的释放。

1、死锁产生条件

  • 互斥条件:指资源在同一时间内只能由一个线程占用,其他线程想要使用需要等待。
  • 请求并持有条件:指一个线程已经持有了至少一个资源,但又提出了新的资源请求,而新资源已经被其他线程占有,所以当前线程会被阻塞,但阻塞的同时并不释放自己已经获取到的资源。
  • 不可剥夺条件:指线程获取到的资源在自己使用完之前不被其他线程占有,只有自己使用完后才会被释放。
  • 环路等待:发生死锁时,必然会存在线程与资源的环形链。

2、如何避免死锁

  • 避免嵌套锁请求资源。
  • 注意加锁的资源,不要对不必要的资源加锁。
  • 注意请求资源的先后顺序,使其不能形成环路。比如已有线程T1拥有R1资源,请求R2资源,那么就不能存在线程T2拥有R2资源,请求R1资源。
  • 避免无限等待,比如获取ReentrantLock的lock(long time, TimeUnit unit)方法请求锁,一段时间内请求不到会返回false;又或者使用Thread的join(long millis)方法时,加上时间限制

三、守护线程

java线程分两种:daemon(守护线程)和user(用户线程)。daemon线程会伴随着JVM的退出而销毁,而JVM退出前需要确保没有运行的user线程才能退出。

也就是说,daemon线程用于你希望某些后台线程能随着JVM关闭而关闭的场景。

设置线程为守护线程只需要设置daemon为true即可:

Thread a=new Thread(()->{
//do something
});
a.setDaemon(true);
a.start();

四、线程本地变量

  • ThreadLocal:可以新建一个共享变量,让其他线程各自保存自己的本地变量

 

  • InheritableThreadLocal:可建立一个让子线程访问到父线程的对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值