結論先行:
這是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可以實現精準通知喚醒。