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

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

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

    隨筆 - 18  文章 - 96  trackbacks - 0
    <2007年12月>
    2526272829301
    2345678
    9101112131415
    16171819202122
    23242526272829
    303112345


    常用鏈接

    留言簿(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://www.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://www.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 閱讀(1349) 評論(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的鎖問題就可以了  回復  更多評論
      

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


    網站導航:
     
    主站蜘蛛池模板: 精品丝袜国产自在线拍亚洲| 亚洲国产精品人久久电影| 亚洲精品永久在线观看| 日韩一区二区a片免费观看| 亚洲激情校园春色| 99久久精品日本一区二区免费| 亚洲国产电影在线观看| 亚洲一区二区三区免费在线观看 | 四虎免费影院ww4164h| 亚洲精品日韩专区silk| 国产大片免费网站不卡美女| 亚洲18在线天美| 波多野结衣久久高清免费| 国产亚洲成在线播放va| 中文字幕专区在线亚洲| 日本卡1卡2卡三卡免费| 亚洲国产成人精品无码区在线秒播 | 国产成人免费ā片在线观看老同学 | 国产免费久久精品99久久| 亚洲级αV无码毛片久久精品| 国产成人精品无码免费看| 亚洲综合免费视频| 在线观看免费毛片| 在线播放国产不卡免费视频| 久久精品国产亚洲综合色| 99视频精品全部免费观看| 亚洲一区二区三区久久| 免费h黄肉动漫在线观看| 久草免费福利在线| 亚洲国产精品久久丫| 国产特级淫片免费看| 中文字幕一区二区免费| 亚洲男人的天堂在线| 国产一区二区视频免费| 久久国产免费一区| 亚洲乱理伦片在线观看中字| 亚洲色偷偷综合亚洲AV伊人| xxxxwww免费| 9久久免费国产精品特黄| 亚洲二区在线视频| 亚洲午夜日韩高清一区|