线程通信简单实例
发布日期:2021-09-25 11:48:25 浏览次数:4 分类:技术文章

本文共 5145 字,大约阅读时间需要 17 分钟。

1.Object类的wait()、notify()和notifyAll(),必需由同步监视器对象来调用

  • wait(),导致当前线程等待,直到其他线程调用该同步监视器的notify()或notifyAll()来唤醒该线程,调用该方法的当前线程会释放对该同步监视器的锁定
  • notify(),唤醒在此同步监视器上等待的单个线程,如果所有线程都在该同步监视器上等待,则会选择唤醒其中一个,选择是任意性的,只有当前线程放弃对该同步监视器的锁定(使用wait方法)后,才可以执行被唤醒的线程
  • notifyAll(),唤醒在此同步监视器上等待的所有线程

eg:建立三个线程,A线程打印10次A,B线程打印10次B,C线程打印10次C,要求线程同时运行,交替打印10次ABC

package jichu;/*建立三个线程,A线程打印10次A,B线程打印10次B,C线程打印10次C要求线程同时运行,交替打印10次ABC思路:Thread A->Thread B->Thread C->Thread A循环为了控制顺序,必须确定唤醒、等待的顺序,每个线程必须同时有两个对象锁,prev前一个;self自身先持有prev,也就是前一个线程要释放自身对象锁,再去申请自身对象锁,两者兼备打印之后先调用self.notify释放自身,唤醒下一个等待线程,再调用prev.wait释放prev对象锁,终止当前线程,等待循环结束后再次被唤醒A先运行,持有CA,后释放AC,唤醒B;B等待A,再申请B,后打印B,再释放BA,唤醒C;C等待B,再申请C,打印后释放CB,唤醒A */public class MyThreadPrinter implements Runnable {    private String name;    private Object prev;    private Object self;    private MyThreadPrinter(String name,Object prev,Object self){        this.name=name;        this.prev=prev;        this.self=self;    }    public void run(){        int count=10;        while(count>0){            synchronized (prev){                synchronized (self){                    System.out.print(name);                    count--;//                    调用self.notify()释放自身对象锁,唤醒下一个等待线程                    self.notify();                }                try{//                	调用prev.wait()释放prev锁,终止当前等待线程,等待循环结束后再次被唤醒                    prev.wait();                }catch (InterruptedException e){                    e.printStackTrace();                }            }        }    }    public static void main (String [] args)throws Exception{        Object a=new Object();        Object b=new Object();        Object c=new Object();        MyThreadPrinter pa=new MyThreadPrinter("A",c,a);        MyThreadPrinter pb=new MyThreadPrinter("B",a,b);        MyThreadPrinter pc=new MyThreadPrinter("C",b,c);        new Thread(pa).start();        Thread.sleep(100);        new Thread(pb).start();        Thread.sleep(100);        new Thread(pc).start();        Thread.sleep(100);    }}

2. Condition

如果程序不使用synchronized关键字来保证同步,而是直接用Lock对象来保证同步,则系统中不存在隐式的同步监视器,也就不能使用上述方法

当使用Lock对象来保证同步,java提供一个Condition类来保持协调,使用Condition可以让那些已经得到Lock对象而无法继续执行的线程释放Lock对象,Condition对象也可以唤醒其他处于等待的线程

eg:和Object类的wait()、notify()和notifyAll()一样,当线程使用condition.await()时,要求线程持有相关的重入锁,在condition.await()调用后,这个线程会释放这把锁,signal同理,在signal方法被调用后,系统会从当前Condition对象的等待队列中唤醒一个线程,一旦线程被唤醒,它会重新尝试获得与之绑定的重入锁,一旦成功获取即可执行

package LockTest;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;public class LockCondition implements Runnable {	public static ReentrantLock lock=new ReentrantLock();//	通过lock生成一个与之绑定的condition对象	public static Condition condition=lock.newCondition();	public void run(){		try{			lock.lock();			condition.await();//线程在condition对象上进行等待			System.out.println("Thread is going on");		}catch(InterruptedException e){			e.printStackTrace();		}finally{			lock.unlock();		}	}	public static void main(String[] args) throws InterruptedException {		// TODO Auto-generated method stub		LockCondition lc=new LockCondition();		Thread t1=new Thread(lc);		t1.start();		Thread.sleep(2000);//		主线程main发出通知,告诉等待在condition上的线程可以继续执行了		lock.lock();		condition.signal();		lock.unlock();	}}

这种方式在JDK内部被广泛使用,eg,ArrayBlockingQueue

ArrayBlockingQueue的内部元素都放置在一个对象数组中:

/** The queued items */    final Object[] items;
  • offer(),如果当前队列已经满了,立即返回false
  • put(),如果队列满了,则会一直等待,直到队列中有空闲位置
  • poll(),如果队列为空,直接返回null
  • take(),如果队列空,一直等待,直到队列中有可用元素

为了做好等待和通知,ArrayBlockingQueue定义如下字段:

/** Main lock guarding all access */    final ReentrantLock lock;    /** Condition for waiting takes */    private final Condition notEmpty;    /** Condition for waiting puts */    private final Condition notFull;

当执行take操作,如果队列为空,则让当前线程等待在notEmpty上:

public E take() throws InterruptedException {        final ReentrantLock lock = this.lock;        lock.lockInterruptibly();        try {            while (count == 0)                notEmpty.await();            return dequeue();        } finally {            lock.unlock();        }    }

新元素入队列时,则进行一次notEmpty上的通知:

private void enqueue(E x) {        // assert lock.getHoldCount() == 1;        // assert items[putIndex] == null;        final Object[] items = this.items;        items[putIndex] = x;        if (++putIndex == items.length)            putIndex = 0;        count++;        notEmpty.signal();    }

同理,对于put,当队列满时,让压入线程等待:

public void put(E e) throws InterruptedException {        checkNotNull(e);        final ReentrantLock lock = this.lock;        lock.lockInterruptibly();        try {            while (count == items.length)                notFull.await();            enqueue(e);        } finally {            lock.unlock();        }    }

当有元素从队列中被挪走,队列中出现空位,通知等待入队的线程:

private E dequeue() {        // assert lock.getHoldCount() == 1;        // assert items[takeIndex] != null;        final Object[] items = this.items;        @SuppressWarnings("unchecked")        E x = (E) items[takeIndex];        items[takeIndex] = null;        if (++takeIndex == items.length)            takeIndex = 0;        count--;        if (itrs != null)            itrs.elementDequeued();        notFull.signal();        return x;    }

 

转载地址:https://blog.csdn.net/Autumn03/article/details/81094281 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:java写一个线程安全的单例模式Singleton
下一篇:线程池的使用

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2024年03月03日 03时12分32秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

php sql 给数据库追加内容,php如何向数据库中的某串数据后追加内容【急】 2019-04-21
php微信小程序获取用户信息,微信小程序授权获取用户详细信息openid的实例详解... 2019-04-21
Java三元运算和if,Java三元运算符与<JDK8兼容性中的if / else 2019-04-21
graphql-php enum,php – 如何在不写长查询的情况下查询所有的GraphQL类型字段? 2019-04-21
php date 函数用法,php中date()日期时间函数使用方法 2019-04-21
php除法获取整数和余数,PHP除法取整和取余数 2019-04-21
java迷宫路径,Java中的迷宫路径查找器 2019-04-21
php substr cnblog,php中substr用法示例 2019-04-21
php链接怎么截取,PHP 截取网页中的固定种子链接 2019-04-21
iis运行不起来php报500,解决IIS上安装thinkphp6运行报500错误 2019-04-21
php ajax上传图片过大500错误,javascript – JQuery AJAX文件上传错误500 2019-04-21
matlab 图中的legend,matlab中legend加图示命令的使用 2019-04-21
PHP exec xargs 不执行,Linux中的xargs命令及示例 2019-04-21
php 枚举cookie内容,php设置和获取cookie 2019-04-21
单防区扩展模块怎么用_AB罗克韦尔自动化Micro800 扩展 I/O模块型号及功能介绍 2019-04-21
java矩阵类_Java泛型——泛型矩阵类 2019-04-21
java车牌正则表达式_车牌正则表达式 2019-04-21
wordpress4.9.4 mysql_WordPress 将不再支持 PHP4 和 MySQL 4 2019-04-21
安卓是用java语言写的吗_android开发是用java语言吗? 2019-04-21
java 符号 t_java – 运算符”不能应用于’T’,’T’表示有界泛型类型 2019-04-21