`
phoenix520
  • 浏览: 142247 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Java wait/notify中的坑

    博客分类:
  • Java
阅读更多

近日在基于Netty写异步HttpClient的时候,需要等http连接建立并通道打开后,才能使用该连接来发送数据,但是Netty中只能等待到连接建立就会返回一个用来收发数据的channel,如果channel并没有打开,用来发送数据时就会报错,因此需要在代码中等到channel打开后再返回,想到了使用简单的wait&notify来解决,先上一段代码:


public class HttpClient{
    private Boolean connected = false;

    private final class InnerHandler extends SimpleChannelUpstreamHandler {
        @Override
        public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
            synchronized(connected){
                connected = true;
                channel = e.getChannel();
                logger.debug("{}",this);
                connected.notifyAll();
            }
            logger.debug("channelConnected");
        }
    }

    public void connect() throws Exception {
        ......InnerHandler hander = new InnerHandler();......
        ChannelFuture future = bootstrap.connect();
        future.await();

        synchronized(connected){
          if (future.isSuccess() && !connected) {
            logger.debug("connection opened,waiting for channel connected!");
            logger.debug("{}", hander);
            connected.wait();
          }
          logger.debug("new http client channel established!");
        }
    }
}


结果程序运行到connected.wait()便死锁了,经分析发现,connected.wait()和connected.notifyAll()进行操作的不是同一个对象,因为connected = true这里将connected 重新指向了另一个对象,代码上看来connected.notifyAll()没啥问题,其实这个信号已经没人收得到了,开始的那个connected.wait()也再也得不到任何信号,会一直等下去了。
修改代码,使用一个专门的对象来做锁:
private Object lockObject = new Object();
将synchronized、wait和notifyAll使用的对象都换为lockObject,一切正常。可见,使用对象锁的时候,尽量使用一个不会被潜在改变引用地址的对象做锁,最好专门新建一个Object来做锁。
当然只要保证synchronized、wait和notifyAll使用的是同一个对象,不专门弄个Object来做锁也是可以的,为了多搞点问题出来加深印象,将代码修改了一下:

public class HttpClient{
    private boolean connected = false;

    private final class InnerHandler extends SimpleChannelUpstreamHandler {
        @Override
        public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
            connected = true;
            channel = e.getChannel();
            synchronized(this){                
                logger.debug("notify:{}",this);
                notifyAll();
            }
            logger.debug("channelConnected");
        }
    }

    public void connect() throws Exception {
        ......InnerHandler hander = new InnerHandler();......
        ChannelFuture future = bootstrap.connect();
        future.await();

        if (future.isSuccess() && !connected) {
          logger.debug("connection opened,waiting for channel connected!");
          synchronized(hander){            
            logger.debug("wait:{}", hander);
            hander.wait();
          }
          logger.debug("new http client channel established!");
        }
    }
}


使用hander对象来做锁,这儿的代码已经确保大家使用的都是同一个hander,运行代码,发现一切正常,再运行几次,发现又死锁了,分析debug信息:
connection opened,waiting for channel connected!
notify: com.skymobi.http.HttpClient$InnerHandler@cc52fc
channelConnected
wait: com.skymobi.http.HttpClient$InnerHandler@cc52fc
原来程序执行时先跑去notify,然后再wait了,开始时不是很清楚为啥一定要加个synchronized,只是不加的话就不能用来wait,于是囫囵吞枣的加上个synchronized。这儿看来,加上synchronized应该是获取到锁,然后修改某些状态值,供别的线程根据这些状态值去判断是否需要做某些事情,一般synchronized中如果要使用wait,都需要先判断是否满足需要wait的条件,否则就会导致死锁,而在notify的synchronized代码片段中,一般会对判断条件使用的值进行修改,然后再通知wait的线程。
最后修改过能运行的代码如下:

public class HttpClient{
    private boolean connected = false;

    private final class InnerHandler extends SimpleChannelUpstreamHandler {
        @Override
        public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {

            synchronized(this){          
                connected = true;
                channel = e.getChannel();      
                logger.debug("notify:{}",this);
                notifyAll();
            }
            logger.debug("channelConnected");
        }
    }

    public void connect() throws Exception {
        ......InnerHandler hander = new InnerHandler();......
        ChannelFuture future = bootstrap.connect();
        future.await();

        synchronized(hander){     
          if (future.isSuccess() && !connected) {
            logger.debug("connection opened,waiting for channel connected!");            
            logger.debug("wait:{}", hander);
            hander.wait();
          }
          logger.debug("new http client channel established!");
        }
    }
}

 

分享到:
评论

相关推荐

    Java 同步方式 wait和notify/notifyall

    在Java中,`wait()`, `notify()`, 和 `notifyAll()` 是Java Object类的三个方法,它们在实现线程间通信和协作时扮演着关键角色。这些方法主要用于解决线程等待和唤醒的问题,是基于Java Monitor(监视器)模型的。 ...

    浅谈Java线程间通信之wait/notify

    Java中的wait/notify/notifyAll可用来实现线程间通信,是Object类的方法,这三个方法都是native方法,是平台相关的,常用来实现生产者/消费者模式。 wait()方法将当前线程置于等待状态,直到其他线程调用notify()或...

    java之wait,notify的用法([ 详解+实例 ])

    在Java多线程编程中,wait和notify是两个非常重要的方法,它们都是Object类的方法,用于线程之间的通信和同步。下面我们将详细解释wait和notify的用法。 wait方法 wait方法是Object类的一个方法,用于让当前线程...

    wait_notify_demo

    `wait()`、`notify()`和`notifyAll()`是Java中的三个关键字,它们属于Object类的方法,主要用于线程间的通信,尤其在实现生产者消费者模式时发挥着重要作用。本文将深入探讨这些方法以及如何在实际场景中应用它们。 ...

    JAVAWAIT和NOTIFY的用法.pdf

    JAVAWAIT和NOTIFY的用法.pdf

    Java多线程wait和notify

    在Java中,`wait()` 和 `notify()` 方法是实现线程间通信和协作的重要工具,它们属于 `java.lang.Object` 类,这意味着所有类都默认继承了这两个方法。本文将详细探讨如何使用 `wait()` 和 `notify()` 来控制子线程...

    Java的sychronized、wait和notify范例

    `synchronized`关键字、`wait()`和`notify()`方法是Java多线程中用于控制并发访问共享资源的重要工具,它们是Java内存模型(JMM)的一部分,主要用于解决线程间的同步问题。 一、`synchronized`关键字 `...

    如何在Java中正确使用 wait, notify 和 notifyAll

     在 Java 中可以用 wait、notify 和 notifyAll 来实现线程间的通信。。举个例子,如果你的Java程序中有两个线程——即生产者和消费者,那么生产者可以通知消费者,让消费者开始消耗数据,因为队列缓冲区中有内容待...

    java中几个notify、wait使用实例

    在Java的多线程编程中,`notify()`与`wait()`是实现线程间通信的重要方法,它们主要用于解决生产者消费者问题、读者写者问题等典型同步问题。这两个方法定义在`Object`类中,因此所有Java对象都可以作为锁来使用。在...

    Java多线程中wait、notify、notifyAll使用详解

    3. 由于 wait()、notify/notifyAll() 在synchronized 代码块执行,说明当前线程一定是获取了锁的。 4. wait() 需要被try catch包围,中断也可以使wait等待的线程唤醒。 5. notify 和wait 的顺序不能错,如果A线程先...

    Java 同步锁 wait notify 学习心得

    标题和描述概述的知识点主要集中在Java的多线程机制中,特别是`wait`和`notify`方法在同步锁中的应用。这些方法对于控制线程之间的交互至关重要,尤其是在资源有限或需要确保数据一致性的情况下。 ### Java同步锁...

    浅谈java多线程wait,notify

    在Java多线程编程中,wait和notify是两个非常重要的机制,用于实现线程之间的通信和同步。在本文中,我们将通过示例代码详细介绍Java多线程wait和notify的使用,帮助读者更好地理解和掌握这两个机制。 wait机制 在...

    源码—Java多线程5—死锁和wait notify notifyAll

    源码—Java多线程5—死锁和wait notify notifyAll

    一个理解wait()与notify()的例子

    通过本示例代码的学习,我们可以深入了解`wait()`与`notify()`方法的工作原理及其在Java多线程编程中的应用。这两个方法通过释放和重新获取锁的方式,有效地实现了线程间的通信和同步,是Java并发控制的重要组成部分...

    java-wait和notify的用法.pdf

    在Java编程语言中,`wait()`和`notify()`是Object类中的两个关键方法,它们用于线程间的协作和通信。这两个方法在多线程环境下尤其重要,因为它们允许线程等待特定条件并通知其他线程继续执行。在分析给定的程序之前...

    详解Java程序并发的Wait-Notify机制

    Java程序并发的Wait-Notify机制是Java多线程编程中的一种重要同步工具,它允许线程之间通过共享对象进行通信和协作。这个机制基于Java的内置锁(也称为监视器锁),通常与`synchronized`关键字一起使用。在Java中,`...

    Sun Certified Programmer for the Java 2 Platform(310-065)

    这些题目反映了310-065考试的重点,包括但不限于:Java基础语法、异常处理、多线程编程(并发和同步)、对象监视器和wait/notify机制。考生在准备考试时,需要深入理解这些概念,并能够编写和分析实际的Java代码以...

    wait,notify等线程知识.pdf

    Java中的多线程协同工作是通过一系列的同步机制来实现的,其中wait、notify和notifyAll是基于对象监视器的同步原语。这些方法在Java的Object类中定义,主要用于线程间的通信和协作,确保资源的安全访问。下面将详细...

    wait()、notify()和notifyAll()方法2---马克-to-win java视频

    wait()、notify()和notifyAll()方法2---马克-to-win java视频

    java多线程设计wait[参考].pdf

    Java中的多线程设计涉及到许多核心概念,其中wait/notify机制是实现线程间通信和协作的关键工具。这个机制主要用于解决资源的分配和同步问题,它依赖于Java的内置锁机制,即`synchronized`关键字和对象锁。 首先,...

Global site tag (gtag.js) - Google Analytics