java多线程和synchronize锁

一、线程的5个状态

1.创建状态

线程对象被创建后,就进入了新建状态。此时它和其他Java对象一样,仅仅由Java虚拟机分配了内存,并初始化其成员变量值。
2.就绪状态 
也被称为“可执行状态”。线程对象被调用了该对象的start()方法,该线程处于就绪状态。Java虚拟机会
为其创建方法调用栈和程序计数器。处于就绪状态的线程,随时可能被CPU调度执行,取决于JVM中线程调度器的调度。
3.运行状态
线程获取CPU权限进行执行。需要注意的是,线程只能从就绪状态进入到运行状态。
4.阻塞状态
阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
阻塞的情况分三种:
(01) 等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。
(02) 同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
(03) 其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。
当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5.死亡状态
线程执行完了、因异常退出了run()方法或者直接调用该线程的stop()方法(容易导致死锁,现在已经不推荐使用)
,该线程结束生命周期。
二、让线程由运行状态直接进入就绪状态的方法
1.yield()
该方法会直接使线程由运行状态进入就绪状态,不会阻塞该线程。
该方法是Thread类的静态方法,该方法不会放弃synchronized锁
三、对线程造成阻塞的相关方法
1.wait(),notif(),notifAll()
wait()会是当前线程进入挂起状态,直到有notify()或notifyAll()通知,才会回到就绪状态,
等待过得CPU使用权。
此三个方法是object类的实例方法,该方法会放弃synchronized锁。
notify()随机通知一个正在等待wait()线程,notifyAll()通知所有正在等待的线程。
2.sleep()
该方法会使当前线程等待固定时间,进入阻塞状态并在固定时间后进入就绪状态。
此方法是Thread类的静态方法,不会放弃synchronized锁。
 有2种重载方式:


——static void sleep(long millis)  :  
让当前正在执行的线程暂停millis毫秒,并进入阻塞状态,
该方法受到系统计时器和线程调度器的精度和准度的影响。


——static void sleep(long millis , int nanos)  :  
让当前正在执行的线程暂停millis毫秒加nanos微秒,并进入阻塞状态,
该方法受到系统计时器和线程调度器的精度和准度的影响。
3.join()
当某个正在运行的线程调用join()方法时,该线程会被阻塞,知道join()所属的线程运行完毕。
该方法是Thread的实例方法,不会释放对象锁。
有3种重载的形式:


——join()  :  
等待被join的线程执行完成


——join(long millis)  :  
等待被join的线程的时间最长为millis毫秒,若在millis毫秒内,被join的线程还未执行结束,则不等待。


——join(long millis , int nanos)  :  
等待被join的线程的时间最长为millis毫秒加nanos微秒,若在此时间内,被join的线程还未执行结束,则不等待。
四、不改变线程状态的方法
1.interrupt(),interrupted(),isinterrupt(),配合InterruptedException异常使用
interrupt()是线程中断方法,该方法调用时不会立即中断线程,而是将中断标记更改为true,之后会有两种情况。
1.当线程状态变为阻塞时,该线程会抛出InterruptedException异常,线程借助异常中断。
2.若线程标记更改为中断后没有遇到阻塞的情况,不会对线程造成影响。
该方法是Thread类的实例方法。
interrupted()该方法会返回线程中断标记,并将中断标记为true的设置为false。
该方法是Thread类的静态方法
isInterrupted(),该方法会返回中断标记。
该方法是Thread的实例方法。
五、两个已经被淘汰的方法
1.shop(),现使用interrupt()方法
该方法用于终止线程,被调用之后会立即终止该线程。并同时释放对象锁。
弊端:
1.当shop()不是本线程调用时,其他线程调用该方法,可能导致原有代码未得到执行,从而导致会业务逻辑不完整。
2.破坏原子逻辑,既破坏了加锁的初衷。
该方法是Thread的实例方法,会释放对象锁。
2.suspend(),resume(),先使用wait(),notify(),notityAll()方法
该方法用于挂起,恢复线程。
弊端:由于该方法不会释放对象锁,所以很容易造成死锁
该方法是Thread的实例方法,不会释放对象锁。
六、synchronized关键字
1.多个线程并发读写同一个临界资源时候会发生"线程并发安全问题“,
常见的临界资源:
多线程共享实例变量
多线程共享静态公共变量
synchronized关键字是同步锁,为了使线程达到同步的效果,每个对象只有一个同步锁,当线程访问加同步锁的代码是,就获得了同步锁,
不同线程对同步锁的访问时互斥的,同一时间点只有一个线程可以访问该同步锁中的代码。
2.加锁的方式
实例锁和全局锁
实例锁:锁在实例对象上,
例如:在synchronized(this),public synchronized void test(){}
全局锁:锁在类对象上,每给类都有唯一的类对象,如:String.class是String类的类对象
例如:synchronized(Test.class),public synchronized static void test(){}
由此可知,实例方法加锁和静态方法加锁不是同一个锁对象,所有当一个线程访问一个对象的实例方法时,其他线程可以访问该对象的静态方法。
3.互斥规则
a.当一个线程访问某对象的synchronized实例方法或加了this的synchronized代码块时,其他线程访问该对象的任何synchronized实例方法或任何加了
this的synchronized代码块时,该线程会被阻塞。如果其他线程访问的不是synchronized实例方法或者不是加了this的synchronized代码块,则
不会被该线程阻塞。
b.当一个线程访问某对象的synchronized方法或加了this的synchronized代码块时,其他线程访问该对象的非synchronized方法和非synchronized代码块
时,不会被阻塞。
c.实例方法和静态方法加的锁的不同,所以当一个线程访问某对象的synchronized实例方法时,其他线程访问该对象的synchronized静态方法时,不会
该线程被阻塞。
4.死锁
多个线程之间抢占资源,相互等待,互不相让,形成死循环,线程无法继续运行,同时无法结束,形成死锁。
死锁形成条件:
1.互斥条件:进程对于所分配到的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放 
2.请求和保持条件:一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放。 
3.不剥夺条件:任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用 
4.循环等待条件:当发生死锁时,所等待的进程必定会形成一个环路(类似于死循环),造成永久阻塞。
阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

java多线程和synchronize锁

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭