Java多线程/并发11、线程同步通信:notify、wait

假设有两个线程,一个线程负责打印5次”Hello”,一个线程负责打印5次”Word”。现在提出一个要求,要求两个线程交替打印,也就是要求Hello和Word交替出现。
我们先实现两个线程打印字符的功能。代码如下:

package JConcurrence.Study;
public class ExecuteDemo {
    public static void main(String[] args) {
        final SyncLockTest LockTest = new SyncLockTest();
        /* 第一个线程说5次Hello */
        new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 5; i++) {
                    LockTest.Hello();
                }
            }
        }).start();
        /* 第二个线程说5次Word */
        new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 5; i++) {
                    LockTest.Word();
                }
            }
        }).start();
    }
}
/* 定义外部测试类SyncLockTest */
class SyncLockTest {
    public void Hello() {
        System.out.println(Thread.currentThread().getName() + "say:Hello");
    }
    public void Word() {
        System.out.println(Thread.currentThread().getName() + "say:World");
    }
}

运行结果:

Thread-0say:Hello
Thread-0say:Hello
Thread-0say:Hello
Thread-0say:Hello
Thread-0say:Hello
Thread-1say:World
Thread-1say:World
Thread-1say:World
Thread-1say:World
Thread-1say:World

可以看到结果并没有交替出现。

这里要强调两点:
1、Hello()和Word()都没有使用synchronized方法。因为每个方法中只有一条System.out.println语句,System.out属于临界资源,不会因为多个线程的竞争,而破化输出。所以不会出现一个字符串还没有打印完时(如:只输出前几个字符的时侯),就被另一个线程抢夺的情况。所以System.out.println本身是线程安全的,不需要画蛇添足的加上synchronized。

2、运行结果排得很整齐,感觉像是先运行完Thread-0,再运行Thread-1。千万别被迷惑,那是因为计算机处理能力强大了。
如果在每个方法前加上sleep模拟耗时操作,就会看到,两个进程不分先后的往外输出。

public void Hello() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "say:Hello");
    }
    public void Word() {
        try {
            Thread.sleep(7);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "say:World");
    }

这时结果很零乱:

Thread-1say:World
Thread-0say:Hello
Thread-1say:World
Thread-0say:Hello
Thread-1say:World
Thread-1say:World
Thread-0say:Hello
Thread-1say:World
Thread-0say:Hello
Thread-0say:Hello

不过以上并不是本文重点。

现在开始说重点,我们如何实现两个线程交替打印呢?这里用到了同步对像锁的wait()和 notify()方法。

wait()、notify()是定义在Object类里的方法,可以用来控制线程的状态。这三个方法最终调用的都是jvm级的native方法。随着jvm运行平台的不同可能有些许差异。
1、如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。
2、如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。如果有多个等待的线程,那么会依靠JVM调度选出一个线程运行。
3、另外还有一个notifyAll方法,会通知所有等待这个对象控制权的线程继续运行。

现在改造SyncLockTest类:
1、首先让两个线程方法成为有着相同锁的synchronized同步方法,即在方法前加上synchronized关键字
2、添加两个Boolen类型的标记,用于判断当前哪个方法运行,哪个方法阻塞。
3、当标记不满足当前方法运行的条件时,使用wait()对运行当前方法的线程阻塞。
4、在执行完当前方法的功能后,更新两个Boolen标记值,同时调用notify()唤醒运行另一个方法的线程。

修改后的类代码如下:

/* 定义外部测试类SyncLockTest */
class SyncLockTest {
    /*
     * 为了增加代码可读性,这里用了两个Boolean变量作为正在执行方法的标记
     * Hello_WillRun如果等于true,表明Hello()方法即将要获得this锁,否则就保持wait
     * 因为两个方法是交替执行,同一时间只有一个执行,所以两个变量必须保持互反:Hello_WillRun=!Word_WillRun
     * 默认将先执行的那个设为true,后执行的设为false
     */
    Boolean Hello_WillRun = true;
    Boolean Word_WillRun = false;
    /*两个方法拥有同样的锁:this*/
    public synchronized void Hello() {
        /* 如果Hello()不是接下来将要运行的状态,即:!Hello_WillRun,那么保持等待wait() 
        while用于防止线程假醒后,顺序往下执行输出功能,从而破坏交替输出*/
        while (!Hello_WillRun) {
            try {
                /* Hello()进行等待
                 * 调用wait()和notify()的对象必须和synchronized锁对象一致,因此这里用this*/
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        /*执行Hello()核心功能*/
        System.out.println(Thread.currentThread().getName() + "say:Hello");

        /* Hello()执行完毕,设置下一步的标记状态值 */
        Hello_WillRun = false;
        Word_WillRun = true;
        /*唤醒另一个线程*/
        this.notify();

    }
    /*两个方法拥有同样的锁:this*/
    public synchronized void Word() {
        while (!Word_WillRun) {
            try {
                /* Word()进行等待
                 * 调用wait()和notify()的对象必须和synchronized锁对象一致,因此这里用this*/
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        /*执行Word()核心功能*/
        System.out.println(Thread.currentThread().getName() + "say:World");
        /* Word()执行完毕,设置下一步的状态值 */
        Hello_WillRun = true;
        Word_WillRun = false;
        /*唤醒另一个线程*/
        this.notify();

    }
}

成功输出结果:

Thread-0say:Hello
Thread-1say:World
Thread-0say:Hello
Thread-1say:World
Thread-0say:Hello
Thread-1say:World
Thread-0say:Hello
Thread-1say:World
Thread-0say:Hello
Thread-1say:World
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值