多线程之间如何通讯(什么是多线程之间通讯)?
多线程之间通讯,其实就是多个线程在操作同一个资源,但是操作的动作不同。
解决线程安全问题:在出现线程安全的代码块上加synchronized关键字.
对象锁和类锁
1. 对象锁
在 Java 中,每个对象都会有一个 monitor 对象,这个对象其实就是 Java 对象的锁,通常会被称为“内置锁”或“对象锁”。类的对象可以有多个,所以每个对象有其独立的对象锁,互不干扰。
2. 类锁
在 Java 中,针对每个类也有一个锁,可以称为“类锁”,类锁实际上是通过对象锁实现的,即类的 Class 对象锁。每个类只有一个 Class 对象,所以每个类只有一个类锁。
synchronized锁定的是调用这个同步方法的对象(this) 互斥锁,排它锁。
对于静态方法,由于此时对象还未生成,所以只能采用类锁;只要采用类锁,就会拦截所有线程,只能让一个线程访问。
同步方法与同步代码块
多线程中考虑,线程获取对象的锁。
1、当同一个对象P1在不同的线程中执行这个同步方法时,他们之间会形成互斥,达到同步的效果。
2、同时如果该对象中有多个同步方法,则当一个线程获执行对象中的一个synchronized方法,则该对象中其它同步方法也不允许别的线程执行;
那个拿到了P1对象锁的线程,才能够调用P1的同步方法;而对P2而言,P1这个锁和他毫不相干,程式也可能在这种情形下摆脱同步机制的控制,造成数据混乱。
每个对象对应一个唯一的锁,所以哪个线程拿到这个对象锁谁就能够运行他所控制的那段代码。
monitorEnter;monitorExit.
每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitor Enter指令时尝试获取monitor的所有权,过程如下:
1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。
2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.
3、如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。
Java对象头与Monitor
在JVM中,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充。
实例变量:存放类的属性数据信息,包括父类的属性信息,如果是数组的实例部分还包括数组的长度,这部分内存按4字节对齐。
填充数据:由于虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐.
监视器锁对象:
monitor对象存在于每个Java对象的对象头中(存储的指针的指向),synchronized锁便是通过这种方式获取锁的,也是为什么Java中任意对象可以作为锁的原因,同时也是notify/notifyAll/wait等方法存在于顶级对象Object中的原因。
Java对象头,它实现synchronized的锁对象的基础。
一般而言,synchronized使用的锁对象是存储在Java对象头里的,jvm中采用2个字来存储对象头(如果对象是数组则会分配3个字,多出来的1个字记录的是数组长度),其主要结构是由Mark Word 和 Class Metadata Address 组成。
Mark Word 存储对象的hashCode、锁信息或分代年龄或GC标志等信息;
Class Metadata Address 类型指针指向对象的类元数据,JVM通过这个指针确定该对象是哪个类的实例。其中Mark Word在默认情况下存储着对象的HashCode、分代年龄、锁标记位等以下是32位JVM的Mark Word默认存储结构。