写在前面:尽量不要使用 synchronized(String a) 因为在JVM中,字符串常量池具有缓冲功能!而这个会导致线程一直循环,因为String的引用只有一个,会导致只有一个线程不断循环执行。
1.synchronized
synchronized:可以在任意对象及方法上加锁,而加锁的这段代码称为互斥区或临界区。
synchronized可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性。
采用synchronized修饰符实现的同步机制叫做互斥锁机制,它所获得的锁叫做互斥锁。每个对象都有一个monitor(锁标记),当线程拥有这个锁标记时才能访问这个资源,没有锁标记便进入锁池。任何一个对象系统都会为其创建一个互斥锁,这个锁是为了分配给线程的,防止打断原子操作。每个对象的锁只能分配给一个线程,因此叫做互斥锁。
synchronized用的锁是存在Java对象头里。如果对象是数组类型,则虚拟机用3个字宽(Word)存储对象头,如果对象是非数组类型,则用2字宽存储对象头。在32位虚拟机中,1字宽等于4字节,即32bit,如下表所示。
Java对象头里的Mark Word里默认存储对象的HashCode、分代年龄和锁标记位。32位JVM的Mark Word的默认存储结构如表所示。
在运行期间,Mark Word里存储的数据会随着锁标志位的变化而变化。Mark Word可能变化为存储以下4种数据,如表2-4所示。
在64位虚拟机下,Mark Word是64bit大小的,其存储结构如表2-5所示。
Java中每一个对象都可以作为锁,这是synchronized实现同步的基础:
-
普通同步方法,锁是当前实例对象
-
静态同步方法,锁是当前类的class对象
-
同步方法块,锁是括号里面的对象
其实synchronized相当于是一个“对象监控器”锁,这个对象监控器由不同的对象充当。
当一个线程访问同步代码块时,它首先是需要得到锁才能执行同步代码,当退出或者抛出异常时必须要释放锁。
public class MyThread extends Thread {
private int count = 5;
@Override
public synchronized void run() {
count‐‐;
System.out.println(this.currentThread().getName() + " count:" + count);
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread1 = new Thread(myThread, "thread1");
Thread thread2 = new Thread(myThread, "thread2");
Thread thread3 = new Thread(myThread, "thread3");
Thread thread4 = new Thread(myThread, "thread4");
Thread thread5 = new Thread(myThread, "thread5");
thread1.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
}
}
输出结果:
<