<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 18  文章 - 96  trackbacks - 0
    <2012年2月>
    2930311234
    567891011
    12131415161718
    19202122232425
    26272829123
    45678910


    常用鏈接

    留言簿(4)

    隨筆檔案

    相冊

    我的兄弟們

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    在寫多線程程序的時候,你就像個經理,手下有那么或多或少的職員,你負責協調職員之間的工作,如果你稍不留神,職員之間就陷入了相互等待的尷尬狀態。還好,大多數時候多線程都還在我們掌控之內,即便是遇到這樣的deadlock情況,我們也能夠去修正,但是有的時候生活就是那么不盡人意,特別是NIO這種你不能掌控的時候,且看下面的代碼:

    /**
     * @(#)DeadLock.java  v0.1.0  2007-12-13
     
    */
    package ruislan.rswing.test;

    import java.net.InetSocketAddress;
    import java.nio.channels.ClosedChannelException;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.SocketChannel;
    import java.util.concurrent.Executors;

    /**
     * NIO DeadLock
     * 
     * 
    @author ruislan <a href="mailto:z17520@126.com"/>
     * 
    @version 0.1.0
     
    */
    public class DeadLock {
        
    public static void main(String[] args) throws Exception {
            Service service 
    = new Service();
            Executors.newSingleThreadExecutor().execute(service);

            SocketChannel channel 
    = SocketChannel.open();
            channel.configureBlocking(
    false);
            channel.connect(
    new InetSocketAddress("http://m.tkk7.com"80));
            service.addChannel(channel);
        }

        
    static class Service implements Runnable {
            Selector selector;

            
    public Service() {
            }

            
    public void run() {
                
    try {
                    selector 
    = Selector.open();
                    
    while (true) {
                        selector.select();
                        System.out.println(selector.selectedKeys().size());
                    }
                } 
    catch (Exception e) {
                }
            }

            
    public void addChannel(SocketChannel channel) {
                
    try {
                    channel.register(selector, SelectionKey.OP_CONNECT
                            
    | SelectionKey.OP_READ);
                    System.out.println(
    "can reach here?when pigs fly!");
                } 
    catch (ClosedChannelException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    乍看之下,我們的代碼沒有問題,但是運行之后你會發現,這句System.out.println("can reach here?when pigs fly!");永遠無法執行,也就是說register()方法被阻塞了!Oh god bless,讓我們看看JavaDoc是怎么說的:

    ...
    可在任意時間調用此方法。如果調用此方法的同時正在進行另一個此方法或 configureBlocking 方法的調用,則在另一個操作完成前將首先阻塞該調用。然后此方法將在選擇器的鍵集上實現同步,因此如果調用此方法時并發地調用了涉及同一選擇器的另一個注冊或選擇操作,則可能阻塞此方法的調用。
    ...

    看這句“可在任意時間調用此方法。”,也就是說我們調用的時間沒有任何限制,而阻塞的情況只會出現在“如果調用此方法的同時正在進行另一個此方法或 configureBlocking 方法的調用”的情況下,即便是阻塞了,我相信“正在進行另一個此方法或configureBlocking”也不會花掉太多的時間,況且這里沒有上面這樣的情況出現。那register()是被誰擋住了?或者是BUG?

    我們來分析一下程序,程序有兩個線程主線程和Service線程,主線程啟動后啟動了Service線程,Service線程啟動Selector然后Service線程陷入select()的阻塞中,同時,主線程調用Service的addChannel()方法來添加一個SocketChannel,嗯,兩個線程之間唯一的聯系就是selector,看來要從selector尋找線索,很可惜,selector的實現沒有源代碼可查,不過可以肯定是channel的register()會調用selector的register(),雖然此時持有selector的Service線程被select()方法所阻塞,但是并不影響其他線程對其操作吧?那么,剩下的解釋就是Selector的select()方法和register()方法公用了一個鎖,select()方法阻塞住了,所以register()拿不到這個鎖了,那么這樣一來我們就只能保證讓select()或者register()不能同時調用或者register()調用的時候select()不持有這個鎖,也就是說我們要用Service線程自己來執行addChannel()方法,所以改進如下:

    /**
     * @(#)DeadLock.java  v0.1.0  2007-12-13
     
    */
    package ruislan.rswing.test;

    import java.net.InetSocketAddress;
    import java.nio.channels.ClosedChannelException;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.SocketChannel;
    import java.util.Queue;
    import java.util.concurrent.LinkedBlockingQueue;

    /**
     * NIO DeadLock
     * 
     * 
    @author ruislan <a href="mailto:z17520@126.com"/>
     * 
    @version 0.1.0
     
    */
    public class DeadLock {
        
    public static void main(String[] args) {
            Service service 
    = new Service();
            
    new Thread(service).start();
            
    for (int i = 0; i < 5; i++) {
                
    new Thread(new ChannelAdder(service)).start();
            }
        }

        
    static class ChannelAdder implements Runnable {
            
    private Service service;

            
    public ChannelAdder(Service service) {
                
    this.service = service;
            }

            @Override
            
    public void run() {
                
    try {
                    SocketChannel channel 
    = SocketChannel.open();
                    channel.configureBlocking(
    false);
                    channel.connect(
    new InetSocketAddress(
                            
    "http://m.tkk7.com"80));
                    service.addChannel(channel);
                } 
    catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        
    static class Service implements Runnable {
            
    private Selector selector;
            
    private Queue<SocketChannel> pendingRegisters;

            
    public Service() {
                pendingRegisters 
    = new LinkedBlockingQueue<SocketChannel>();
            }

            
    public void run() {
                
    try {
                    selector 
    = Selector.open();
                    
    while (true) {
                        selector.select();
                        System.out.println(selector.selectedKeys().size());
                        handlePendingRegisters();
                    }
                } 
    catch (Exception e) {
                }
            }

            
    public void handlePendingRegisters() {
                
    while (!pendingRegisters.isEmpty()) {
                    SocketChannel channel 
    = pendingRegisters.poll();
                    
    try {
                        channel.register(selector, SelectionKey.OP_CONNECT);
                        System.out.println(
    "can reach here?yeah!");
                    } 
    catch (ClosedChannelException e) {
                        e.printStackTrace();
                    }
                }
            }

            
    public void addChannel(SocketChannel channel) {
                pendingRegisters.offer(channel);
                selector.wakeup();
            }
        }
    }


    新的代碼,我們在Service的線程提供了一個待處理Channel隊列,然后在添加一個SocketChannel到隊列中時喚醒這個selector,取消阻塞,然后在Service的循環中處理這個pendingChannel,這樣就避免這個Deadlock的發生了。當然我們亦可以在那個代碼上將select的超時時間設置非常的短,然后讓兩個線程去競爭,這樣做有太多的不可控性,不推薦了。

    posted on 2007-12-13 18:31 ruislan 閱讀(1348) 評論(3)  編輯  收藏

    FeedBack:
    # re: NIO的DeadLock 2007-12-16 17:16 或潛于淵
    正常情況下能用到java.nio時當然要用java.util.concurrent包處理并發了……  回復  更多評論
      
    # re: NIO的DeadLock 2008-02-19 17:01 wan
    幫了大忙。謝謝!  回復  更多評論
      
    # re: NIO的DeadLock 2012-02-15 14:37 balancejia
    改進后增加ChannelAdder的用意何在?我覺得沒有必要引入ChannelAdder ,只要解決select和register的鎖問題就可以了  回復  更多評論
      

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    主站蜘蛛池模板: 边摸边脱吃奶边高潮视频免费 | 精品亚洲成在人线AV无码| 国产精品高清视亚洲精品| 久久综合国产乱子伦精品免费| 一个人看的www在线观看免费| 亚洲精品tv久久久久久久久久| 亚洲成熟xxxxx电影| 亚洲精品无码专区久久| 亚洲网址在线观看你懂的| 亚洲日韩中文字幕一区| 免费a级毛片高清视频不卡| 中文字幕专区在线亚洲| 久久www免费人成精品香蕉| 亚洲国产另类久久久精品小说| 亚洲妇女熟BBW| 日本二区免费一片黄2019| 日韩中文无码有码免费视频| 亚洲成a人片77777kkkk| 免费无码毛片一区二区APP| 免费人成激情视频| 久久不见久久见中文字幕免费| 婷婷亚洲综合五月天小说在线| 全黄A免费一级毛片| 免费人成网站在线播放| 亚洲成人午夜电影| 国内自产少妇自拍区免费| 色www免费视频| 内射干少妇亚洲69XXX| 成人女人A级毛片免费软件| 久久亚洲AV成人无码国产电影| 一二三四免费观看在线视频中文版 | 久久香蕉国产线看免费| 亚洲第一页日韩专区| 国产性生大片免费观看性| 无码欧精品亚洲日韩一区夜夜嗨| 日韩免费观看视频| fc2成年免费共享视频网站| 久久精品亚洲一区二区| 日本免费观看网站| 你懂的免费在线观看网站| 亚洲夂夂婷婷色拍WW47|