Java的并发编程:从synchronized保证线程安全的原理

[

Java的并发编程:线程的安全性问题的分析这篇文章中说到了synchronized可以保证线程的安全性,那么,这篇文章主要是说synchronized保证线程安全的原理,为什么加上synchronized就能保证线程的安全呢?我们可以从两个角度出发去看这个问题,一个是从理论的层面,一个是从JVM的层面。

从理论的层面看synchronized保证线程安全的原理

synchronized的原理有两个:

  • 内置锁
  • 互斥锁

Java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。而Java的内置锁又是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,知道线程B释放这个锁,如果B线程不释放这个锁,那么A线程将永远等待下去。

synchronized保证线程的安全访问,那么,这个synchronized的关键字可以修饰什么呢?synchronized主要是用来修饰下面三个内容:

  • 修饰普通方法
  • 修饰静态方法
  • 修饰代码块

synchronized修饰这个三个方法的Java示例:


package com.breakyizhan.thread.t3;

public class Sequence {
	
	private int value;
	
	/**
	 * synchronized 放在普通方法上,内置锁就是当前类的实例
	 * @return
	 */
	public synchronized int getNext() {
		return value ++;
	}
	
	/**
	 * 修饰静态方法,内置锁是当前的Class字节码对象
	 * Sequence.class
	 * @return
	 */
	public static synchronized int getPrevious() {
//		return value --;
		return 0;
	}
	
	public int xx () {
		
		// monitorenter
		synchronized (Sequence.class) {
			
			if(value > 0) {
				return value;
			} else {
				return -1;
			}
			
		}
		// monitorexit
		
	}
	
	public static void main(String[] args) {
		
		Sequence s = new Sequence();
//		while(true) {
//			System.out.println(s.getNext());
//		}
		
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true) {
					System.out.println(Thread.currentThread().getName() + " " + s.getNext());
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true) {
					System.out.println(Thread.currentThread().getName() + " " + s.getNext());
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
		
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(true) {
					System.out.println(Thread.currentThread().getName() + " " + s.getNext());
					try {
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();		
	}
}

从JVM的层面看synchronized保证线程安全的原理

我们已经知道,在Java的并发编程:线程的安全性问题的分析这篇文章中说到,用java -verbose可以查看JVM虚拟机的二进制代码,那么,我们就来看一下synchronized修饰同步代码块的时候运行的机制,(修饰普通方法和修饰静态方法暂时看不到锁的获取和释放),在synchronized修饰同步代码块的时候 monitorenter和monitorexit代表了锁的获取和释放,如下图,代表着方法int xx():

Java的并发编程:从synchronized保证线程安全的原理

简单说一下上面那个图,这个 monitorenter和monitorexit代表了锁的获取和释放,而方法int xx()并不是按顺序执行的,如果遇到第9步ifle也会跳到第19步,执行完之后再执行monitorexit释放锁,所以从java的字节码指令可以看到也是有很多monitorexit这样的命令,也是不奇怪的。

]
转载请保留页面地址:https://www.breakyizhan.com/java/6628.html
原文地址:https://www.cnblogs.com/breakyizhan/p/13281257.html