import java.util.LinkedList; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; //條件:Condition 為Lock增加條件,當滿足條件時,做什麼事情,如枷鎖或解鎖。等待喚醒 public class TestContaniner<T> { private final LinkedList<T> list = new LinkedList<>(); private final int MAX = 10; private int count = 0; private Lock lock = new ReentrantLock(); private Condition product = lock.newCondition(); private Condition consumer = lock.newCondition(); public int getCount() { return count; } public void put(T t) { lock.lock(); try { while (list.size() == MAX) { System.out.println(Thread.currentThread().getName() + "等待中。。。"); // 進入等待隊列,釋放鎖標記 // 藉助條件,進入的等待隊列 product.await(); } System.out.println(Thread.currentThread().getName() + "put..."); list.add(t); count++; // 藉助條件,喚醒所有的消費者 consumer.signalAll(); } catch (InterruptedException e1) { e1.printStackTrace(); } finally { lock.unlock(); } } public T get() { T t = null; lock.lock(); try { while (list.size() == 0) { System.out.println(Thread.currentThread().getName() + "等待..."); consumer.await(); } System.out.println(Thread.currentThread().getName() + "get..."); t = list.removeFirst(); count--; } catch (InterruptedException e1) { e1.printStackTrace(); } finally { lock.unlock(); } return t; } public static void main(String[] args) { final TestContaniner<String> c=new TestContaniner<>(); for(int i=0;i<10;i++){ new Thread(new Runnable(){ @Override public void run(){ for(int j=0;j<5;j++){ System.out.println(c.get()); } } },"consumer"+i).start(); } try{ TimeUnit.SECONDS.sleep(2); }catch (InterruptedException e1) { e1.printStackTrace(); } for(int i=0;i<2;i++){ new Thread(new Runnable(){ @Override public void run(){ for(int j=0;j<25;j++){ c.put("container value"+j); } } },"producer"+i).start(); } } }
Java 虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现。同步方 法 并不是由 monitor enter 和 monitor exit 指令来实现同步的,而是由方法调用指令读取运 行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的
对象头:存储对象的 hashCode、锁信息或分代年龄或 GC 标志,类型指针指向对象的类 元数据,JVM 通过这个指针确定该对象是哪个类的实例等信息。 实例变量:存放类的属性数据信息,包括父类的属性信息 填充数据:由于虚拟机要求对象起始地址必须是 8 字节的整数倍。填充数据不是必须存 在的,仅仅是为了字节对齐 当在对象上加锁时,数据是记录在对象头中。当执行 synchronized 同步方法或同步代码 块时,会在对象头中记录锁标记,锁标记指向的是 monitor 对象(也称为管程或监视器锁) 的起始地址。每个对象都存在着一个 monitor 与之关联,对象与其 monitor 之间的关系有 存在多种实现方式,如 monitor 可以与对象一起创建销毁或当线程试图获取对象锁时自动生 成,但当一个 monitor 被某个线程持有后,它便处于锁定状态。 在 Java 虚拟机(HotSpot)中,monitor 是由 ObjectMonitor 实现的。 ObjectMonitor 中有两个队列,_WaitSet 和 _EntryList,以及_Owner 标记。其中_WaitSet 是用于管理等待队列(wait)线程的,_EntryList 是用于管理锁池阻塞线程的,_Owner 标记用于 记录当前执行线程。线程状态图如下:
当多线程并发访问 同一个同步代码 时,首先会进入_EntryList,当线程获取锁标记后, monitor 中的_Owner 记录此线程,并在 monitor 中的计数器执行递增计算(+1),代表锁定, 其他线程在_EntryList 中继续阻塞。若执行线程调用 wait 方法,则 monitor 中的计数器执行 赋值为 0 计算,并将_Owner 标记赋值为 null,代表放弃锁,执行线程进如_WaitSet 中阻塞。 若执行线程调用 notify/notifyAll 方法,_WaitSet 中的线程被唤醒,进入_EntryList 中阻塞,等 待获取锁标记。若执行线程的同步代码执行结束,同样会释放锁标记,monitor 中的_Owner 标记赋值为 null,且计数器赋值为 0 计算