理解Java中的synchronized [’sɪŋkrənaɪzd]
问题
有如下一个类a
class A {
public synchronized void a() {
}
public synchronized void b() {
}
}
然后创建两个对象
A a1 = new A();
A a2 = new A();
然后在两个线程中并发访问如下代码:
Thread1 a1.a();
Thread2 a2.a();
请问二者能否构成线程同步?
如果A的定义是下面这种呢?
class A {
public static synchronized void a() {
}
public static synchronized void b() {
}
}
要点
- synchronized采用锁同步的机制实现线程同步
- 线程在等待锁释放的过程中处于阻塞状态
- 一把锁可以同步所有对应的代码
使用:
synchronized修饰代码时会表明对应的锁,所有表明这把锁的代码都会同步。
Synchronized主修修饰对象为以下三种:
修饰普通方法 一个对象中的加锁方法只允许一个线程访问。但要注意这种情况下锁的是访问该方法的实例对象, 如果多个线程不同对象访问该方法,则无法保证同步。
修饰静态方法 由于静态方法是类方法, 所以这种情况下锁的是包含这个方法的类,也就是类对象;这样如果多个线程不同对象访问该静态方法,也是可以保证同步的。
修饰代码块 其中普通代码块 如Synchronized(obj) 这里的obj 可以为类中的一个属性、也可以是当前的对象,它的同步效果和修饰普通方法一样;Synchronized方法 (obj.class)静态代码块它的同步效果和修饰静态方法类似。
Class A{
//锁是this
public synchronized void a(){};
public synchronized void b(){};
//锁是this.class
public synchronized static void x(){};
public synchronized static void y(){};
public void z(){
//指定锁,该处指定为this
synchronized(this){
}
}
}
this 锁对应object,this.class 锁对应 clss
所以第一种a1,a2两个锁是不同的锁会并发,第二种是相同的锁A.class不会并发;
另外,修饰代码块时指定的锁就与所有这把锁对应的代码同步。
运行机制
简单的说是这样synchronized在实现同步功能时会声明一把锁,表明必须持有这把锁的线程才能访问执行,在一个线程需要执行这块代码时,会先尝试持有这把指定的锁,如果这个锁已被别的线程持有,那么该线程就会开始等待,直到这把锁被释放后才会继续执行,并且在执行时会一直持有这把锁,以防止其他线程进入这把锁控制的所有代码,直到该线程执行完才会释放这把锁。
上面说的很模糊,这个锁是怎么回事呢,看了这个synchronized底层语义原理之后大概意思就是说synchronized的对象锁,锁的指针指向的是monitor对象,每个对象有都存在着一个 monitor(管程) 与之关联,在 java 虚拟机 hotSpot 中monitor于对象的关联关系是由ObjectMonitor实现。
其中有_EntryList 和_WaitSet 两个对象列表,多线程访问synchronized修饰的代码段时会先进入_EntryList,线程获取到monitor之后进入 Owner 区域,monitor中的 count 会加一,线程调用 wait 方法之后会释放 monitor,count–,线程进入 waitset等待唤醒(被其他线程调用notify/notifyAll),线程执行完毕也要是释放monitor并复位。我注意到这个地方讲到wait和锁的关系,又想到以前面试被问过sleep() 和wait()的区别
sleep() 和wait()的区别
我当初提前看过一些面试题,就是这样答的:
sleep()方法属于Thread[θrɛd]类中的。而wait()方法是属于Object类中的。sleep()是线程安全的,wait()线程不安全。
真正理解之后的回答:
sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他一直在持有monitor(未进入 Waitset中),当指定的时间到了又会自动恢复运行状态。这就跟上面连到一起了
而当调用wait()方法的时候,线程会进入Waitset,释放 monitor,进入等待此对象的Waitset(等待锁定池),只有针对此对象调用notify/notifyAll方法后本线程才进入对象锁定池准备。
notify和notifyall的区别
notify只会通知一个在等待的对象,而notifyAll会通知所有在等待的对象,并且所有对象都会继续运行
wait此方法导致当前线程(称之为 T)将其自身放置在对象的等待集中Waitset(等待锁定池),然后放弃此对象上的所有同步要求。
出于线程调度目的,在发生以下四种情况之一前,线程 T 被禁用,且处于休眠状态:
- 其他某个线程调用此对象的 notify 方法,并且线程 T 碰巧被任选为被唤醒的线程。
- 其他某个线程调用此对象的 notifyAll 方法。
- 其他某个线程中断线程 T。大约已经到达指定的实际时间。但是,如果 timeout 为零,则不考虑实际时间,在获得通知前该线程将一直等待。
然后,从对象的等待集Waitset中删除线程T,并重新进行线程调度(进入_EntryList)。然后,该线程以常规方式与其他线程竞争,以获得在该对象上同步的权利;一旦获得对该对象的控制权,该对象上的所有其同步声明都将被恢复到以前的状态。
这就是调用 wait方法时的情况。然后,线程 T 从 wait 方法的调用中返回。所以,从 wait 方法返回时,该对象和线程 T 的同步状态与调用 wait 方法时的情况完全相同。
就是说多个线程等待的时候notifyall所有的等待线程都被通知了,这些线程进行竞争之后其中一个会得到锁(也是要等待正在执行的线程先释放锁才行)
/**
*
*/
package com.b510.test;
/**
* java中的sleep()和wait()的区别
* @author Hongten
* @date 2013-12-10
*/
public class TestD {
public static void main(String[] args) {
new Thread(new Thread1()).start();
try {
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
new Thread(new Thread2()).start();
}
private static class Thread1 implements Runnable{
@Override
public void run(){
synchronized (TestD.class) {
System.out.println("enter thread1...");
System.out.println("thread1 is waiting...");
try {
//调用wait()方法,线程会放弃对象锁,进入等待此对象的等待锁定池
TestD.class.wait();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("thread1 is going on ....");
System.out.println("thread1 is over!!!");
}
}
}
private static class Thread2 implements Runnable{
@Override
public void run(){
synchronized (TestD.class) {
System.out.println("enter thread2....");
System.out.println("thread2 is sleep....");
//只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
TestD.class.notify();
//==================
//区别
//如果我们把代码:TestD.class.notify();给注释掉,即TestD.class调用了wait()方法,但是没有调用notify()
//方法,则线程永远处于挂起状态。
try {
//sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,
//但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
//在调用sleep()方法的过程中,线程不会释放对象锁。
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("thread2 is going on....");
System.out.println("thread2 is over!!!");
}
}
}
}
enter thread1...
thread1 is waiting...
enter thread2....
thread2 is sleep....
thread2 is going on....
thread2 is over!!!
thread1 is going on ....
thread1 is over!!!
注释TestD.class.notify();
enter thread1...
thread1 is waiting...
enter thread2....
thread2 is sleep....
thread2 is going on....
thread2 is over!!!
本文深入解析Java中的synchronized关键字,探讨其在对象级和类级上的使用差异,以及如何通过锁机制实现线程间的同步,同时对比了sleep()与wait()的不同之处。
1778

被折叠的 条评论
为什么被折叠?



