異常分析:java.lang.IllegalMonitorStateException

java.lang.IllegalMonitorStateException



JavaDoc上關於IllegalMonitorStateException的解釋是:

Thrown to indicate that a thread has attempted to wait on an object's monitor or to notify other threads waiting on an object's monitor without owning the specified monitor.

其實意思就是說,也就是當前的線程不是此對象監視器的所有者。也就是要在當前線程鎖定對象,才能用鎖定的對象此行這些方法,需要用到synchronized ,鎖定什麼對象就用什麼對象來執行  notify()notifyAll(),wait()wait(long)wait(long, int)操作,否則就會報IllegalMonitorStateException異常。


JavaDoc中說到:

A thread becomes the owner of the object's monitor in one of three ways:
1. By executing a synchronized instance method of that object.
2. By executing the body of a synchronized statement that synchronizes on the object.
3. For objects of type Class, by executing a synchronized static method of that class. 

通過以下三種方法之一,線程可以成爲此對象監視器的所有者:

  • 通過執行此對象的同步 (Sychronized) 實例方法。
  • 通過執行在此對象上進行同步的 synchronized 語句的正文。
  • 對於 Class 類型的對象,可以通過執行該類的同步靜態方法。

也就是在說,就是需要在調用wait()或者notify()之前,必須使用synchronized語義綁定住被wait/notify的對象。


下面介紹解決方法:

通過實現加鎖的方式實現線程同步時產生的併發問題.


exapmle 1,鎖定方法所屬的實例對象:
public synchronized void method(){
    //然後就可以調用:this.notify()...
    //或者直接調用notify()...
}



exapmle 2,鎖定方法所屬的實例的Class:
public Class Test{
 public static synchronized void method(){
    //然後調用:Test.class.notify()...
 }
}



exapmle 3,鎖定其他對象:
public Class Test{
public Object lock = new Object();
 public static void method(){
    synchronized (lock) {
     //需要調用 lock.notify();
    } 
 }
}


總結:

:“線程操作的wait()、notify()、notifyAll()方法只能在同步控制方法或同步控制塊內調用。如果在非同步控制方法或控制塊裏調用,程序能通過編譯,但運行的時候,將得到  IllegalMonitorStateException 異常,並伴隨着一些含糊信息,比如 ‘當前線程不是擁有者’。其實異常的含義是 調用wait()、notify()、notifyAll()的任務在調用這些方法前必須 ‘擁有’(獲取)對象的鎖。”


下面附錄線程的基本使用知識:



一、線程的理解


 1、同個應用中,多個任務同時進行。就像QQ聊天,打開一個聊天窗口就是一個線程。

 2、線程可以有多個,但cpu每時每刻只做一件事。由於cpu處理速度很快,我們就感覺是同時進行的。所以宏觀上,線程時併發進行的;從微觀角度看,線程是異步執行的。

 3、使用線程的目的是最大限度的利用cpu資源。想想QQ聊天的時候,如果沒有多線程,一個人的信息沒有發完另一個人的信息發不過來,會是什麼情況~!



二、java中使用線程

 

1、創建線程

eg1:繼承Thread

class MyThread extends Thread{
    @Override
    public void run() {
     //code
    }
}

啓動線程方法:new MyThread().start();

eg2:實現Runnable接口

class MyRunnable implements Runnable {
    @Override
    public void run() {
        //code
    }
}

啓動線程方法:new Thread(new MyRunnable()).start();

 


2、設置線程優先級

Thread t = new Thread(myRunnable);
t.setPriority(Thread.MAX_PRIORITY);//一共10個等級,Thread.MAX_PRIORITY表示最高級10
t.start();



3、join,sleep,yield的用法與區別

  join方法:假如你在A線程中調用了B線程的join方法B.join();,這時B線程繼續運行,A線程停止(進入阻塞狀態)。等B運行完畢A再繼續運行。

  sleep方法:線程中調用sleep方法後,本線程停止(進入阻塞狀態),運行權交給其他線程。

  yield方法:線程中調用yield方法後本線程並不停止,運行權由本線程和優先級不低於本線程的線程來搶。(不一定優先級高的能先搶到,只是優先級高的搶到的時間長)

 


4、結束線程(修改標示符flag爲false來終止線程的運行)

 
public class ThreadTest {
    public static void main(String[] args) {
        A aa = new A();
        Thread tt = new Thread(aa);
        tt.start();

        try {
            Thread.sleep(5000);
        } catch (Exception e) {
            e.printStackTrace();
        }

        aa.shutDown();
    }
}

class A implements Runnable {
    private boolean flag = true;

    public void run() {
        while (flag) {
            System.out.println("AAAA");
        }
    }

    public void shutDown() {
        this.flag = false;
    }
}
 

 


5、線程同步synchronized

  synchronized可以修飾方法,或者方法內部的代碼塊。被synchronized修飾的代碼塊表示:一個線程在操作該資源時,不允許其他線程操作該資源。

 
public class ThreadTest {

    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        new Thread(myRunnable).start();
        new Thread(myRunnable).start();
        new Thread(myRunnable).start();
        new Thread(myRunnable).start();
    }

}

class MyRunnable implements Runnable {
    private int i = 0;

    @Override
    public void run() {
        while (true) {
            sumNum();
        }
    }

    private synchronized void sumNum() {
        if (i < 100) {
            try {
                Thread.sleep(1000);
                i ++;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "--------"
                    + i);
        }
    }

}
複製代碼

 


6、wait、notify、notifyAll的用法

  wait方法:當前線程轉入阻塞狀態,讓出cpu的控制權,解除鎖定。

  notify方法:喚醒因爲wait()進入阻塞狀態的其中一個線程。

  notifyAll方法: 喚醒因爲wait()進入阻塞狀態的所有線程。

  這三個方法都必須用synchronized塊來包裝,而且必須是同一把鎖,不然會拋出java.lang.IllegalMonitorStateException異常。

 

下面是一個生產者、消費者例子:

 
public class ProductConsumer {
    public static int i = 0;

    public static void main(String[] args) {
        ProductStack ps = new ProductStack();
        Product product = new Product(ps);
        new Thread(product).start();

        Consumer consumer = new Consumer(ps);
        new Thread(consumer).start();
    }

}

class Product implements Runnable {
    ProductStack ps = null; 
    Product(ProductStack ps) {
        this.ps = ps;
    }
    @Override
    public void run() {
        while (true) {
            if (ProductConsumer.i < 100) {
                ps.push();
            }else{
                break;
            }
        }
    }

}

class Consumer implements Runnable {
    ProductStack ps = null;
        Consumer(ProductStack ps) {
            this.ps = ps;
        }

    @Override
    public void run() {
        while (true) {
            if (ProductConsumer.i < 100) {
                ps.pop();
            }else{
                break;
            }
        }
    }

}

class ProductStack {
    boolean isPro = true;
    public synchronized void push() {
        if (!isPro) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        notifyAll();
        ProductConsumer.i++;
        System.out.print("第" + ProductConsumer.i + "個產品,生產者:"
                + Thread.currentThread().getName());
        isPro = false;
        try {
            Thread.sleep((int) Math.random() * 200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
    }

    public synchronized void pop() {
        if (isPro) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        notifyAll();
        System.out.println(",消費者:" + Thread.currentThread().getName());
        isPro = true;
        try {
            Thread.sleep((int) Math.random() * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 

 

  



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章