加在静态方法上,锁为当前类的Class
同步代码块 public void draw(int money) { synchronized (total) { if (total >= money) { this.total = this.total - money; System.out.println(Thread.currentThread().getName() + "剩下" + this.total); } else { System.out.println(Thread.currentThread().getName() + "不够了"); } } }synchronized同步块,锁为()里边的对象
锁 Lock lock = new ReentrantLock(); public void draw(int money) { lock.lock(); try { if (total >= money) { this.total = this.total - money; System.out.println(Thread.currentThread().getName() + "剩下" + this.total); } else { System.out.println(Thread.currentThread().getName() + "不够了"); } } finally { lock.unlock(); } }使用比较简单,进方法加锁,执行完释放,后面会专门发一篇文章介绍锁,包括AQS之类的东西,敬请关注。
线程间的通信线程之间协调工作的方式
基于等待通知模型的通信等待/通知的相关方法是任意Java对象都具备的,因为这些方法被定义在java.lang.Object上。
相关APInotify: 通知一个对象上等待的线程,使其从wait方法返回,而返回的前提是该线程获取到了对象的锁
notifyAll: 通知对象上所有等待的线程,使其从wait方法返回
wait: 使线程进入WAITING(后面线程的生命周期里边有)状态,只有等待另一个线程通知或者被中断才返回,需要注意的是,调用wait方法后需要释放对象的锁
wait(long): 和wait类似,加入了超时时间,超时了还没被通知就直接返回
wait(long, int): 纳秒级,不常用
一些需要注意的点:
使用wait()、notify()和notifyAll()时需要先对调用对象加锁。
调用wait()方法后,线程状态由RUNNING变为WAITING,并将当前线程放置到对象的等待队列,释放锁。
notify()或notifyAll()方法调用后,等待线程不会立即从wait()返回,需要调用notify()或notifAll()的线程释放锁之后,等待线程才有机会从wait()返回。
notify()方法将等待队列中的一个等待线程从等待队列中移到同步队列中,而notifyAll()方法则是将等待队列中所有的线程全部移到同步队列,被移动的线程状态由WAITING变为BLOCKED。
从wait()方法返回的前提是获得了调用对象的锁。
关于等待队列和同步队列
同步队列(锁池):假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的同步队列(锁池)中,这些线程状态为Blocked。
等待队列(等待池):假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁(因为wait()方法必须出现在synchronized中,这样自然在执行wait()方法之前线程A就已经拥有了该对象的锁),同时 线程A就进入到了该对象的等待队列(等待池)中,此时线程A状态为Waiting。如果另外的一个线程调用了相同对象的notifyAll()方法,那么 处于该对象的等待池中的线程就会全部进入该对象的同步队列(锁池)中,准备争夺锁的拥有权。如果另外的一个线程调用了相同对象的notify()方法,那么 仅仅有一个处于该对象的等待池中的线程(随机)会进入该对象的同步队列(锁池)。
以上来自啃碎并发(二):Java线程的生命周期
等待通知模型的示例 class WaitNotifyModel { Object lock = new Object(); boolean flag = false; public void start() { Thread A = new Thread(() -> { synchronized (lock) { while (!flag) { try { System.out.println(Thread.currentThread().getName()+":等待通知"); lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+ ":收到通知,处理业务逻辑"); } }); A.setName("我是等待者"); Thread B = new Thread(() -> { synchronized (lock) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } flag = true; System.out.println(Thread.currentThread().getName()+":发出通知"); lock.notify(); } }); B.setName("通知者"); A.start(); B.start(); } } 模型归纳等待者
synchronized (对象) { while (不满足条件) { 对象.wait() } 处理业务逻辑 }通知者
synchronized (对象) { 改变条件 对象.notify(); } 基于Condition的通信上述的这种等待通知需要使用synchronized, 如果使用Lock的话就要用Condition了
Condition接口也提供了类似Object的监视器方法,与Lock配合可以实现等待/通知模式