一、Synchronized的概念
是利用锁的机制来实现同步的。
锁机制有如下两种特性:
互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程中的协调机制,这样在同一时间只有一个线程对需同步的代码块(复合操作)进行访问。互斥性我们也往往称为操作的原子性。
可见性:必须确保在锁被释放之前,对共享变量所做的修改,对于随后获得该锁的另一个线程是可见的(即在获得锁时应获得最新共享变量的值),否则另一个线程可能是在本地缓存的某个副本上继续操作从而引起不一致。
二、Synchronized的使用
修饰静态方法
//修饰静态方法 public static synchronized void print() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"runing"); }
修饰非静态方法
//修饰非静态方法 public synchronized void print1() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"runing"); }
修饰代码块
//代码块的使用 public void prnit2() { synchronized (this) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"runing"); } } //代码块的使用 public void prnit3() { synchronized (SynchroTest.class) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"runing"); } }
疑问:修饰代码块的时候,Synchronized参数对象起到了什么作用?
答:当JVM是用ClassLoader加载字节码的时候,会在方法区创建一个对象,同时也会在堆去创建一个Class(注意是大写的)。使用Synchronized.xxx.class就是说明所有Class对应的对象都使用共同一个锁(比较抽象)。这个Class一定是一个final的类。详情请看原理分析JVM指令分析的 Monitorenter,Monitorexit。JVM会将对象与Monitor相关联。
java中每一个对象都会有一个monitor对象(监视器)。它的作用就是给对象加锁用的。
三、Synchronized的原理分析
线程堆栈分析(排他性)
public static void main(String[] args) throws InterruptedException { final SynchroTest tSynchroTest1 = new SynchroTest(); for (int i = 10; i < 15; i++) { new Thread(tSynchroTest1::print1).start(); } } // 修饰非静态方法 public synchronized void print1() { try { TimeUnit.MINUTES.sleep(2); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "runing"); }
2.方法加锁方式,是使用、配合使用
对象与monitor:一个实例对象包含了对象头,实例变量,填充数据。
对象头:加锁的一个基础。
实例变量:私有变量,属性变量信息
填充数据:对象的启示地址,8字节表示。
对象头
他们的区别是时间上的区别。
用户线程和核心线程相互转换(非常耗时)
由偏向锁转轻量级锁在转重量级锁的时候存在不可逆行为,只能低转高,不可反之。
自旋锁:再转换过程中还存在一个叫做自旋锁的情况,再轻量级装重量级的时候,并不是马上从用户线程转成核心线程,再互斥的情况下会执行几 次空循环,如果空循环完成后再没有拿到占有权,则才会进行转换。
消除锁:JIT(编译)的时候如果定义一个int i=0,并对这个定义进行加锁,那么由于上篇文章说道了,定义int i=0,是存在原子性的,所以这个这 个锁会在编译期间自动消除,防止JVM产生不必要的资源浪费
CAS算法:引用GA特大的Java VAS原理深度分析文章 https://blog.csdn.net/tanga842428/article/details/52742698
欢迎大家来提问,如果有不理解的可以再评论区提出,我会定时解惑。
同舟共济,新海同航
原文:https://www.cnblogs.com/gnwzj/p/10556028.html