作用于实例对应的内存位置。
一个实例一把锁,多个实例多把锁;
对象锁是针对一个实例(instance)的,它是在该实例对应的某个内存位置声明一个标识,记录该实例拥有锁,所以它只会锁住当前的实例,
而不会对当前对象的其它实例产生影响,当多个线程通过不同实例访问同一个对象锁的时候不会阻塞。
1、synchronized修饰成员方法
/**
* 对象锁-synchronized修饰成员方法
*/
public synchronized void m2 () {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
2、synchronized修饰this关键字
/**
* 对象锁-synchronized修饰this关键字
*/
public void m3 () {
synchronized (this) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
3、synchronized修饰成员变量
private Object mLock = new Object();
/**
* 对象锁-synchronized修饰成员变量
*/
public void m6 () {
synchronized (mLock) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
当多个线程访问同一个实例的对象锁时,会挨个执行。
访问不同实例的对象锁时则不会,因为不是同一把锁。
public static void main(String[] args) {
MyObject mo1 = new MyObject();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
mo1.m2();
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
mo1.m2();
}
}, "t2");
t1.start();
t2.start();
}
输出"t1"之后过了3秒输出"t2"。
作用于类
多个对象实例共享同一把锁。
1、synchronized修饰静态方法
/**
* 类锁-synchronized修饰静态方法
*/
public synchronized static void m4 () {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
2、synchronized修饰this.getClass()
/**
* 类锁-synchronized修饰this.getClass()
*/
public void m5 () {
synchronized (this.getClass()) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
3、synchronized修饰静态变量
private static Object sLock = new Object();
/**
* 类锁-synchronized修饰静态变量
*/
public void m7 () {
synchronized (sLock) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
当多个线程访问同一个对象的不同实例的类锁时,会共享同一把锁,挨个执行。
public static void main(String[] args) {
MyObject mo1 = new MyObject();
MyObject mo2 = new MyObject();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
mo1.m5();
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
mo2.m5();
}
}, "t2");
t1.start();
t2.start();
}
输出"t1"后过了3秒输出"t2"。
Object的wait和notify方法,必须在synchronized (Object)代码块内执行。
调用者为synchronized关键字修饰的对象。
会使当前线程在该Object对象(监视器)上进入Waiting状态,并释放线程持有的作用于Object的锁(监视器锁)。
调用Object.notify()方法之后,会使在该Object对象上处于Waiting状态的线程进入到Blocked(阻塞并正在等待获取监视器锁)的状态,
当监视器锁被释放之后,Blocked队列中获取到监视器锁的线程会从Waiting状态返回到Runnable状态。
注意:Object.notify()并不会立即释放监视器锁,而是在对应的synchronized代码块执行完毕之后。
要求三个线程各打印10次"A", “B”, “C”,输出结果为有顺序的"ABCABCABCABCABCABCABCABCABCABC"。
/**
* @author yangwei
* @describition TODO
*
*/
public class MyThreadPrinter2 implements Runnable {
private String name;
private Object prev;
private Object self;
public MyThreadPrinter2(String name, Object prev, Object self) {
super();
this.name = name;
this.prev = prev;
this.self = self;
}
@Override
public void run() {
// TODO Auto-generated method stub
int count = 10;
while (count > 0) {
synchronized (prev) {
synchronized (self) {
System.out.print(name);
self.notify();
count--;
}
try {
prev.wait();
} catch (Exception e) {
// TODO: handle exception
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Object a = new Object();
Object b = new Object();
Object c = new Object();
MyThreadPrinter2 A = new MyThreadPrinter2("A", c, a);
MyThreadPrinter2 B = new MyThreadPrinter2("B", a, b);
MyThreadPrinter2 C = new MyThreadPrinter2("C", b, c);
new Thread(A).start();
Thread.sleep(100); // 保证顺序为ABC
new Thread(B).start();
Thread.sleep(100);
new Thread(C).start();
Thread.sleep(100);
}
}
解析:
这里为了方便理解,我用大写的A、B、C标识线程,小写的a、b、c标识对象。
我们依次调用了线程A、B、C的start()方法,中间间隔100毫秒,执行过程是下面这样的。
1、首先是线程A打印"A"、唤醒在对象a上阻塞的线程、count–、使当前线程A在对象c上阻塞;
2、接下来线程B打印"B"、唤醒在对象b上阻塞的线程、count–、使当前线程B在对象a上阻塞;
3、接下来线程C打印"C"、唤醒在对象c上阻塞的线程、count–、是当前线程C在对象b上阻塞;
这个时候线程B和C都是阻塞状态了,线程A是就绪状态,线程A执行后阻塞A唤醒B、线程B执行后阻塞B唤醒C、线程C执行后阻塞C唤醒A,
就这样循环下去就输出了我们想要的结果。
以上见解如有不当之处,请予指正。
所参考内容:
Evankaka:Java多线程学习(吐血超详细总结)
DivineH:Java并发编程之Object.wait()/notify()详解