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();
}
}
}