java

位置:IT落伍者 >> java >> 浏览文章

java多线程notify和wait


发布日期:2022年09月12日
 
java多线程notify和wait
java多线程之 wait()notify()notifyAll()

wait()notify()notifyAll()不属于Thread类而是属于Object基础类也就是说每个对像都有 wait()notify()notifyAll()的功能因为都个对像都有锁锁是每个对像的基础当然操作锁的方法也是最基础了

先看java doc怎么说

wait导致当前的线程等待使当前线程进入阻塞状态(对象等待队列)直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法当前的线程必须拥有此对象锁该线程发布对此对象锁的所有权并等待直到其他线程通过调用 notify 方法或 notifyAll 方法通知在此对象的锁上等待的线程醒来然后该线程将等到重新获得对对象锁的所有权后才能在wait阻塞处开始继续执行

例子如下

import javautilArrayList;

import javautilList;

class Consume implements Runnable {

private List container = null;

private int count;

public Consume(List lst) {

ntainer = lst;

}

public void run() {

while(true){

synchronized (container) {

if (containersize()==) {

try {

Systemoutprintln(++++++++++++++wait之前

containerwait()// 放弃锁

Systemoutprintln(++++++++++++++wait之后

} catch (InterruptedException e) {

eprintStackTrace()

}

}

try {

Threadsleep(

} catch (InterruptedException e) {

// TODO Autogenerated catch block

eprintStackTrace()

}

containerremove(

containernotify()

Systemoutprintln(我吃了 + containersize() +

}

}

}

}

class Product implements Runnable {

private List container = null;

private int count;

public Product(List lst) {

ntainer = lst;

}

public void run() {

while(true) {

synchronized (container) {

if (containersize()>= MultiThreadMAX) {

try {

containerwait()

} catch (InterruptedException e) {

eprintStackTrace()

}

}

try {

Threadsleep(

} catch (InterruptedException e) {

eprintStackTrace()

}

containeradd(new Object())

containernotify()

Systemoutprintln(我生产了 +containersize() +

}

}

}

}

public class MultiThread {

private List container = new ArrayList()

public final static int MAX = ;

public static void main(String args[]) {

MultiThread m = new MultiThread()

new Thread(new Consume(mgetContainer()))start()

new Thread(new Product(mgetContainer()))start()

// new Thread(new Consume(mgetContainer()))start()

// new Thread(new Product(mgetContainer()))start()

}

public List getContainer() {

return container;

}

public void setContainer(List container) {

ntainer = container;

}

}

++++++++++++++wait之前 //进入消费者线程同时wait 当前线程为阻塞状态

我生产了个 //进入生产者线程同时notify告知消费者退出阻塞状态

//退出同步块之后生产者和消费者竞争处理器

我生产了个 // 生产者再次获得处理器同时notify此时已经没有线程//在contain上等待则notify不起作用

++++++++++++++wait之后 // 再次竞争消费者获得处理器从wait阻塞的地方开始继续//执行

我吃了

我吃了

++++++++++++++wait之前

我生产了

++++++++++++++wait之后

我吃了

++++++++++++++wait之前

我生产了

我生产了

我生产了

++++++++++++++wait之后

我吃了

我吃了

我生产了

我生产了

我吃了

我吃了

我吃了

我生产了

我吃了

我生产了

我生产了

我吃了

我吃了

++++++++++++++wait之前

我生产了

我生产了

++++++++++++++wait之后

我吃了

我生产了

我生产了

我吃了

我生产了

我生产了

我吃了

我吃了

我生产了

我生产了

我吃了

我生产了

我生产了

我吃了

我吃了

我生产了

我生产了

我吃了

我吃了

我吃了

我生产了

我吃了

我吃了

我生产了

我吃了

我吃了

我生产了

我吃了

++++++++++++++wait之前

我生产了

我生产了

++++++++++++++wait之后

我吃了

我吃了

我生产了

我吃了

++++++++++++++wait之前

我生产了

……

可能的一种输出结果如上所示

++++++++++++++wait之前

我生产了

我生产了

++++++++++++++wait之后

我吃了

我吃了

++++++++++++++wait之前

我生产了

++++++++++++++wait之后

notify唤醒在此对象等待队列上等待的单个线程使线程进入锁标记等待队列此时锁标记等待队列重新进行锁的竞争获得锁的线程运行同步块如果所有线程都在此对象上等待则会选择唤醒其中一个线程直到当前的线程放弃此对象上的锁定才能继续执行被唤醒的线程此方法只应由作为此对象锁的所有者的线程来调用

当前的线程必须拥有此对象锁此方法只应由作为此对象锁的所有者的线程来调用说明wait方法与notify方法必须在同步块内 执行即synchronized(obj之内)

调用对像wait方法后当前线程释放对像锁进入对象等待队列直到其他线程(也只能是其他线程)通过notify 方法或 notifyAll该线程重新进入锁标记等待队列接下来进行锁的竞争若获得锁的所有权则继续执行

记得线程必须重新获得对像锁才能继续执行因为synchronized代码块内没有锁是寸步不能走的看一个很经典的例子

Code

package ProductAndConsume;

import javautilList;

public class Consume implements Runnable{

private List container = null;

private int count;

public Consume(List lst){

ntainer = lst;

}

public void run() {

while(true){

synchronized (container) {

if(containersize()== ){

try {

containerwait()//放弃锁

} catch (InterruptedException e) {

eprintStackTrace()

}

}

try {

Threadsleep(

} catch (InterruptedException e) {

// TODO Autogenerated catch block

eprintStackTrace()

}

containerremove(

containernotify()

Systemoutprintln(我吃了+(++count)+

}

}

}

}

package ProductAndConsume;

import javautilList;

public class Product implements Runnable {

private List container = null;

private int count;

public Product(List lst) {

ntainer = lst;

}

public void run() {

while (true) {

synchronized (container) {

if (containersize() > MultiThreadMAX) {

try {

containerwait()

} catch (InterruptedException e) {

eprintStackTrace()

}

}

try {

Threadsleep(

} catch (InterruptedException e) {

eprintStackTrace()

}

containeradd(new Object())

containernotify()

Systemoutprintln(我生产了+(++count)+

}

}

}

}

package ProductAndConsume;

import javautilArrayList;

import javautilList;

public class MultiThread {

private List container = new ArrayList()

public final static int MAX = ;

public static void main(String args[]){

MultiThread m = new MultiThread()

new Thread(new Consume(mgetContainer()))start()

new Thread(new Product(mgetContainer()))start()

new Thread(new Consume(mgetContainer()))start()

new Thread(new Product(mgetContainer()))start()

}

public List getContainer() {

return container;

}

public void setContainer(List container) {

ntainer = container;

}

run()和start()

这两个方法应该都比较熟悉把需要并行处理的代码放在run()方法中start()方法启动线程将自动调用 run()方法这是由Java的内存机制规定的并且run()方法必须是public访问权限返回值类型为void

关键字Synchronized

这个关键字用于保护共享数据当然前提是要分清哪些数据是共享数据每个对象都有一个锁标志当一个线程访问该对象时被 Synchronized 修饰的数据将被上锁阻止其他线程访问当前线程访问完这部分数据后释放锁标志其他线程就可以访问了

以下是引用片段

public ThreadTest implements Runnable

{

public synchronized void run(){

for(int i=;i<;i++)

{

Systemoutprintln( + i)

}

}

public static void main(String[] args)

{

Runnable r = new ThreadTest()

Runnable r = new ThreadTest()

Thread t = new Thread(r

Thread t = new Thread(r

tstart()

tstart()

}

}

以上这段程序中的 i 变量并不是共享数据也就是这里的Synchronized关键字并未起作用因为tt两个线程是两个对象(rr)的线程不同的对象其数据 是不同的所以r和r两个对象的i变量是并不是共享数据

当把代码改成如下Synchronized关键字才会起作用

以下是引用片段

Runnable r = new ThreadTest()

Thread t = new Thread(r)

Thread t = new Thread(r)

tstart()

tstart()

sleep()

使当前线程(即调用该方法的线程)暂停执行一段时间让其他线程有机会继续执行但它并不释放对象锁也就是如果有Synchronized同步 块其他线程仍然不同访问共享数据注意该方法要捕获异常

比如有两个线程同时执行(没有Synchronized)一个线程优先级为MAX_PRIORITY另一个为MIN_PRIORITY如果 没有Sleep()方法只有高优先级的线程执行完成后低优先级的线程才能执行但当高优先级的线程sleep()后低优先级就有机会执行 了

总之sleep()可以使低优先级的线程得到执行的机会当然也可以让同优先级高优先级的线程有执行的机会

join()

join()方法使调用该方法的线程在此之前执行完毕也就是等待调用该方法的线程执行完毕后再往下继续执行注意该方法也要捕获异常

yield()

它与sleep()类似只是不能由用户指定暂停多长时间并且yield()方法只能让同优先级的线程有执行的机会

wait()和notify()notifyAll()

这三个方法用于协调多个线程对共享数据的存取所以必须在Synchronized语句块内使用这三个方法前面说过Synchronized 这个关键字用于保护共享数据阻止其他线程对共享数据的存取但是这样程序的流程就很不灵活了如何才能在当前线程还没退出Synchronized数据 块时让其他线程也有机会访问共享数据呢?此时就用这三个方法来灵活控制

wait()方法使当前线程暂停执行并释放对象锁标志让其他线程可以进入 Synchronized数据块当前线程被放入对象等待池中当调用 notify()方法后将从对象的等待池中移走一个任意的线程并放到锁标志等待池中只有

锁标志等待池中的线程能够获取锁标志如果锁标志等待池中没有线程则notify()不起作用

notifyAll()则从对象等待池中移走所有等待那个对象的线程并放到锁标志等待池中

               

上一篇:Java多线程程序如何掌握基本语法

下一篇:深入研究Servlet线程安全性问题