Java多线程

线程的各种状态

NEW 新建状态,线程创建且没有执行start方法时的状态

RUNNABLE 可运行状态,线程已经启动,但是等待相应的资源(比如IO或者时间片切换)才能开始执行BLOCKED阻塞状态,当遇到synchronized或者lock且没有取得相应的锁,就会进入这个状态

WAITING 等待状态,当调用Object wait或者Thread joinO且没有设置时间,在或者着Locksupport.park时,都会进入自待状态。

TIMED WATING 计时等待,当调用Thread sleep或者Object wait(x)或者Thread join(x)或者都Locksupport,parkNanos或者Locksupport.partUntil时,进入该状态

TERMINATED终止状态,线程中断或者运行结束的状态

  1. wait方法
    wait方法是Object的方法,当线程获得该对象的锁以后,可调用该对象的wait方法,此线程会主动释放锁,陷入阻塞。如果线程没有获得该对象的锁而调用wait方法,会抛出illegalMonitorstate Exce ption异常。

2.notify方法
notify方法是Objecct的方法,调用后不会立即释放锁,仅仅只是随机唤醒一个线程开始竞争锁。只有sychrorized方法或方法块结束以后,才会真正释放锁,而其他线程才有可能获得锁。如果线程没有获得该对象的锁调用notify方法,会抛出illegalMonitorstateException异常。

  1. notifyAll方法
    notifyAll方法和notify方法的区别是:
    notifyAl会唤醒所有在等待该对象的锁的线程,而notify只会随机唤醒等待一个线程。

  2. join方法
    join方法是Thread方法。当前线程A调用线程B的join方法后,A线程会被阻塞,等待B线程执行结束后,A线程再重新执行。原理:实际上调用join方法时, A线程进入synchronized代码块,获取B线程对象的锁,紧接着调用B线程对象wait方法,此时A线程被挂起,等到B线程执行结束时,调用B对象的notifyAll方法唤醒A经程

5 sleep方法
sleep是Thread的静态方法。调用该方法会暂时让出指定时间的执行权,进入阻塞状态,也就是不参与CPU调度。sleep方法并不要求持有锁,并且sleep时并不释放锁。

6.yield方法
yield方法是Thread的静态方法。调用该方法会让出剩余时间片,立马进入就绪(runnable)状态,下一次仍然可能调度到该线程。

7.线程中断
Java 中断模型非常的简单:每个线程对象里都有一个 boolean类型的标识(当然jdk源码中是看不到该标识位,它位于虚拟机层面),代表着是否有中断请求(该请求可来自所有线程,包括被中断的线程本身)。例如,当线程t1想中断线程t2,只需要在线程t1中将线程 t2对象的中断标识置为 true,然后线程t2可以选择在合适的时候处理该中断请求,甚至可以不理会该请求,就像这个线程没有被中断一样。

interrupt()方法是实例方法,它仅做通知设置中断标志位。可以通过判断标志位手动终止线程。

islnterrupted()方法是实例方法,主要是检查线程是否被中断(通过检查中断标志位),返回值是boolean类型

interrupted()方法是静态方法。用来判断当前运行的线程是否被中断,但同时清除当前线程的中断标志位状态。

8.死锁的四个条件互斥条件

指线程对己经获取到的资源进行排它性使用,即该资源同时只由一个线程占用。如果此时还有其他线程请求取该资源,则请求者只能等待,直至占有资源的线程释放该资源。

请求并持有条件:指一个线程己经持有了至少一个资源,但又提出了新的资源请求,而新资源己被其他线程占有,所以当前线程会被阻塞,但阻塞的同时并不释放已经获取的资源。

不可剥夺条件:
目线程获取到的资源在自己使用完之前不能被其他线程抢占,只有在自己使用完毕后才由自己释放该资源。

环路等待条件:指在发生死锁时,必然存在一个线程→资源的环形链,即线程集合(T0,TL T2.…. Tn)中的T0正在等待一个T1占用的资源,TI正在等待T2占用的资源,……Tn正在等待己被T0占用的资源。

9.如何避免死锁

目前只有请求并持有和环路等待条件是可以被破坏的。造成死锁的原因其实和申请资源的顺序有很大关系,使用资源申请的有序性原则就可以避免死锁。

10.守护线程和用户线程
当最后一个用户线程结束以后, JVM就会退出.而守护线程是否存在不会影响JVM是否退出。父线程结束以后,子线程可以继续存在.

  1. ThreadLocal变量

创建一个全局的ThreadLocal变量,访问这个变量的每个线程都会有这个变量的一个本地副本,因此每个线程访问到的值都只属于原理:实际上变量存储在Thread中,当调用ThreadLocal的set方法时,是在往Thread的成员变量ThreadLocals赋值.Thread 类中有一个threadLocals和一个inheritableThreadLocals ,它们都是ThreadLocal Map类型的变量,而ThreadLocall是一个定制化的Hashmap。在默认情况下,每个线程中的这两个变量都为null,只有当前线程第一次调用ThreadLocal 的set或者get方法时才会创建它们。其实每个线程的本地变量不是存放在ThreadLocal实例里面,而是存放在调用线程的threadLocals 变量里面。也就是说, ThreadLocal类型的本地变量存放在具体的线程内存空间中。 ThreadLocal 就是一个工具壳,它通过set 方法把v值放入调用线程的threadLocals 里面并存放起来,当调用线程调用它的get 方法时,再从当前线程的threadLocals变量里面将其拿出来使用。

  1. volatile关键字

当一个变量被声明为volatile时,线程在写入变量时不会把值缓存在奇存器或者其他地方,而是会把值刷新回主内存。当其他线程读取该共享变量时-,会从主内存重新获取最新值,而不是使用当前线程的工作内存中的值。注意volatile并不能保证原子性所以,如果一个变量被volatile修饰了,那么肯定可以保证每次读取这个:量值的时候得到的值是最新的,但是一旦需要对变量进行自增这样的非原子操作,就不会保证这个变量的原子性了.

  1. CAS机制
    什么是CAS机制
    CAS是英文单词Compare And Swap的缩写,翻译过来就是比较并替换。
    CAS机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。
    更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的改为B。
    CAS是英文单词Compare And Swap的缩写,翻译过来就是比较并替换。
    这样说或许有些抽象,我们来看一个例子:

1.在内存地址V当中,存储着值为10的变量。
2.此时线程1想要把变量的值增加1。对线程1来说,I旧的预期值A=10,要修改的新值B=11。
3.在线程1要提交更新之前,另一个线程2抢先一步,把内存地址V中的变量值率先更新成了11. 线程1开始提交更新,首先进行A和地址V的实际值比较(Compare ),发现A不等于V的实际值,提交失败.线程1重新获取内存地址V的当前值,并重新计算想要修改的新值。此时对线程1来说,A=11,B=12。这个重新尝试的过程被称为自旋。
6.这一次比较幸运,没有其他线程改变地址V的值。线程1进行Compare,发现A和地址V的实际值是相等的。
7线程进行SWAP,把地址V的值替换为B.也就是12

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值