结论先行:
这是JDK对这异常的定义。就是说线程没有拿到对应对象的监视器,也就不能在监视器上完成wait或者notify等操作。
解决办法:
加上synchronized,线程就能拿到对象的监视器了。我的理解就是通过synchronized让线程拿到了对象锁,锁定了这个对象,那这个对象的监视器我耍耍问题不大吧。(手动狗头)
另:下面会继续讲解出错的整个过程,以及生产者消费者模式的两种实现方式。
生产者消费者-synchronized版
出错版本:
public class Producter {
public static void main(String[] args) {
PandA pandA = new PandA();
new Thread(()->{
pandA.increment();
},"A").start();
new Thread(()->{
pandA.increment();
},"B").start();
}
}
class PandA{
private int num = 0;
public void increment(){
if (num != 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num++;
this.notify();
System.out.println(Thread.currentThread().getName()+"生产了一个汉堡");
}
public void decrement(){
if (num == 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num--;
this.notify();
System.out.println(Thread.currentThread().getName()+"消费了一个汉堡");
}
}
运行报错的异常就是IllegalMonitorStateException。原因就是线程没有拿到对象的监视器。所以可以简单记忆,调用wait时请搭配上synchronized。
正确版本:
public class Producter {
public static void main(String[] args) {
PandA pandA = new PandA();
new Thread(()->{
for (int i=0;i<15;i++){
pandA.increment();
}
},"A").start();
new Thread(()->{
for (int i=0;i<15;i++){
pandA.decrement();
}
},"B").start();
}
}
class PandA{
private int num = 0;
public synchronized void increment(){
while (num != 0){
try {
//这个this就是这个对象,不是这个线程,我理解错了。wait(),notify()都是继承自Object的方法
//this.wait()让当前对象的调用者线程休眠去了
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num++;
this.notify();
System.out.println(Thread.currentThread().getName()+"生产了一个汉堡"+num);
}
public synchronized void decrement(){
while (num == 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num--;
this.notify();
System.out.println(Thread.currentThread().getName()+"消费了一个汉堡"+num);
}
}
生产者消费者-Lock版
同样先给个错误版本:
public class ProducterLock {
public static void main(String[] args) {
test t = new test();
new Thread(()->{
for (int i=0;i<15;i++){
t.increments();
}
},"A").start();
new Thread(()->{
for (int i=0;i<15;i++){
t.decrements();
}
},"B").start();
}
}
class test{
private int num = 0;
private Lock lock = new ReentrantLock();
public void increments(){
lock.lock();
while(num != 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num++;
System.out.println(Thread.currentThread().getName()+"生产了一个汉堡"+num);
lock.unlock();
}
public void decrements(){
{
lock.lock();
while(num == 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num++;
System.out.println(Thread.currentThread().getName()+"消费了一个汉堡"+num);
lock.unlock();
}
}
}
**运行结果:**同样会报IllegalMonitorStateException异常,但这次就有疑问了,我们加了Lock锁啊,没有锁住对象?继续去翻看JDK,找到ReentrantLock,看一下方法会发现一个方法:
Condition的定义,以及Condition提供的方法。我的疑问也到了解释,监视器方法wait等根本不适合Lock,和Lcok配套的是Condition,他们取代了synchronized和监视器方法的一整套实现方案。
正确代码:
public class ProducterLock {
public static void main(String[] args) {
test t = new test();
new Thread(()->{
for (int i=0;i<15;i++){
t.increments();
}
},"A").start();
new Thread(()->{
for (int i=0;i<15;i++){
t.decrements();
}
},"B").start();
}
}
class test{
private int num = 0;
private Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increments(){
lock.lock();
while(num != 0){
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num++;
condition.signal();
System.out.println(Thread.currentThread().getName()+"生产了一个汉堡"+num);
lock.unlock();
}
public void decrements(){
{
lock.lock();
while(num == 0){
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num--;
condition.signal();
System.out.println(Thread.currentThread().getName()+"消费了一个汉堡"+num);
lock.unlock();
}
}
}
上面代码正确的输出结果如图:
对了,两种方法Lock版本会好一点,因为Condition可以实现精准通知唤醒。
转载:https://blog.csdn.net/wd_888/article/details/105154184
查看评论