对于Java中synchronized关键字的简单理解

Synchronized关键字

对象锁

作用于实例对应的内存位置。
一个实例一把锁,多个实例多把锁;
对象锁是针对一个实例(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()和Object.notify()

Object的wait和notify方法,必须在synchronized (Object)代码块内执行。
调用者为synchronized关键字修饰的对象。

Object.wait()

会使当前线程在该Object对象(监视器)上进入Waiting状态,并释放线程持有的作用于Object的锁(监视器锁)。

Object.notify()

调用Object.notify()方法之后,会使在该Object对象上处于Waiting状态的线程进入到Blocked(阻塞并正在等待获取监视器锁)的状态,
当监视器锁被释放之后,Blocked队列中获取到监视器锁的线程会从Waiting状态返回到Runnable状态。
注意:Object.notify()并不会立即释放监视器锁,而是在对应的synchronized代码块执行完毕之后。

三线程打印ABC

要求三个线程各打印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()详解

猜你喜欢

转载自blog.csdn.net/qq_35549286/article/details/107343964
今日推荐