Object的同步方法
@IntrinsicCandidate
public final native void notify();
@IntrinsicCandidate
public final native void notifyAll();
public final void wait() throws InterruptedException {
wait(0L);
}
public final native void wait(long timeoutMillis) throws InterruptedException;
需要注意:wait(),notify(),notifyAll()这些方法必须使用在同步代码块或者同步方法中。如果在非同步控制方法或控制块里调用,程序能通过编译,但运行的时候,将得到 IllegalMonitorStateException 异常。
同步实例(两人交替买票)
class Ticket implements Runnable{
//当前拥有的票数
private int i = 100;
private Object ticketLock= new Object();
@Override
public void run() {
while(true){
synchronized (ticketLock){
ticketLock.notify();
if(i>0){
System.out.println(Thread.currentThread().getName()+".....sale...."+i--);
}else break;
try {
ticketLock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
Ticket t =new Ticket();
new Thread(t).start();
new Thread(t).start();
}
等效于使用lock和condition的代码如下
class Ticket implements Runnable{
//当前拥有的票数
private int i = 100;
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
@Override
public void run(){
while(true){
lock.lock();
try {
condition.signal();
if(i>0){
System.out.println(Thread.currentThread().getName()+".....sale...."+i--);
}else break;
condition.await();
}catch (InterruptedException e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
}
10、管程和信号量
1、信号量
- P() 操作:将 count - 1,若 count < 0 时,线程进入等待队列
- V() 操作:将 count + 1,若 count <= 0 时,唤醒一个等待队列中的线程
用信号量实现阻塞队列
用一个二进制信号量mutex实现互斥访问;用两个资源信号量notFul,notEmptyl实现条件同步
public class BlockingQueueUseSemaphore2、管程法{ private final Object[] items; private Semaphore notFull; private Semaphore notEmpty; private Semaphore mutex; private int putIndex; private int takeIndex; public BlockingQueueUseSemaphore(int capacity) { this.items = new Object[capacity]; notFull = new Semaphore(capacity); notEmpty = new Semaphore(0); mutex = new Semaphore(1); } public void enq(T x) throws InterruptedException { notFull.acquire(); mutex.acquire(); items[putIndex] = x; if (++putIndex == items.length) { putIndex = 0; } mutex.release(); notEmpty.release(); } public T deq() throws InterruptedException { notEmpty.acquire(); mutex.acquire(); T x = (T) items[takeIndex]; if (++takeIndex == items.length) { takeIndex = 0; } mutex.release(); notFull.release(); return x; } }
在信号量中,当 P 操作无法获取到锁时,将当前线程添加到同步队列(syncQueue)中。当其余线程 V 释放锁时,从同步队列中唤醒等待线程。但当有多个线程通过信号量 PV 配对时会异常复杂,所以管程中引入了等待队列(waitQueue)的概念,进一步封装这些复杂的操作.
管程中条件变量(condition variable)的概念,每一个条件变量对应一个等待队列
synchronized就是基于管程实现的,只包含一个同步队列,一个等待队列
AQS也是基于管程实现的,只包含一个同步队列,但是可以包含多个等待队列
用管程实现阻塞队列
public class BlockingQueueUseMonitor{ final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100]; int putptr, takeptr, count; public void put(E x) throws InterruptedException { lock.lock(); try { while (count == items.length) notFull.await(); items[putptr] = x; if (++putptr == items.length) putptr = 0; ++count; notEmpty.signal(); } finally { lock.unlock(); } } public E take() throws InterruptedException { lock.lock(); try { while (count == 0) notEmpty.await(); E x = (E) items[takeptr]; if (++takeptr == items.length) takeptr = 0; --count; notFull.signal(); return x; } finally { lock.unlock(); } } }
也可以使用基于管程实现的synchronized,以生产者消费者问题为例
class produce extends Thread{
sycnContainer container;
public produce(sycnContainer container) {
this.container = container;
}
@Override
public void run() {
for(int i=1;i<100;i++){
container.push(new product(i));
System.out.println("生产第"+i+"个产品");
}
}
}
class consume extends Thread{
sycnContainer container;
public consume(sycnContainer container) {
this.container = container;
}
@Override
public void run() {
for(int i=1;i<100;i++){
container.pop();
System.out.println("消费第"+i+"个产品");
}
}
}
class product{
int id;
public product(int id) {
this.id = id;
}
}
class sycnContainer{
private product[] ps = new product[10];
private int count =0;
public synchronized void push(product p){
while(count==ps.length){
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
ps[count++] =p;
this.notifyAll();
}
public synchronized product pop(){
while(count==0){
try{
this.wait();
} catch (InterruptedException e){
throw new RuntimeException();
}
}
count--;
this.notifyAll();
return ps[count];
}
}
public static void main(String[] args) {
sycnContainer container =new sycnContainer();
new produce(container).start();
new consume(container).start();
}
11、wait、sleep、await
1、wait VS sleep
*相同点:*
- 都可以让线程休眠;
- 都可以响应 interrupt 的响应。
*不同点:*
- wait 必须在 synchronized 中使⽤,而 sleep 却不⽤;
- sleep 是 Thread 的⽅法,而 wait 是 Object 的⽅法;
- sleep 不释放锁,wait 释放锁;
- sleep 一定有明确的终止等待时间,而 wait 有可能无限期的等待下去;
- sleep 和 wait 产生的线程状态是不同的,sleep 是 TIMED_WAITING 状态,而 wait 是 WAITING 状态。
*不同点:*
- lock不再用synchronize把同步代码包装起,但是需要Condition对应的lock上锁;
- 一个是基于对象监听器的同步方式,一个是基于 AQS同步机制的同步方式
- 使用lock和condition更灵活。在实际应用时可能需要多个等待队列,比如读和写。为了这个灵活性,lock将同步互斥控制和等待队列分离开来,互斥保证在某个时刻只有一个线程访问临界区(lock自己完成),等待队列负责保存被阻塞的线程(condition完成)。



