<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://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 閱讀(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的鎖問題就可以了  回復  更多評論
      

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


    網站導航:
     
    主站蜘蛛池模板: 免费99精品国产自在现线| 国产无遮挡裸体免费视频在线观看 | 96免费精品视频在线观看| 亚洲另类春色国产精品| 五月婷婷综合免费| 免费人成视频在线播放| 久热综合在线亚洲精品| 牛牛在线精品观看免费正| 亚洲国产另类久久久精品黑人| 性色av极品无码专区亚洲| 无遮免费网站在线入口| 一区二区三区免费视频网站| 日本一区免费电影| 99999久久久久久亚洲| 久草免费在线观看视频| 亚洲视频一区在线观看| 免费A级毛片无码专区| 亚洲成AV人片在线观看无码| 成年人网站在线免费观看| 亚洲午夜国产精品| 亚洲毛片av日韩av无码| 日本永久免费a∨在线视频| 亚洲色图古典武侠| 国产亚洲精品看片在线观看| 九九热久久免费视频| 亚洲欧洲∨国产一区二区三区| 成人免费在线看片| 国产免费久久精品99久久| 伊人久久五月丁香综合中文亚洲| 毛片a级毛片免费播放下载| 99热在线日韩精品免费| 亚洲国产精品久久久久秋霞小| 亚洲国产美国国产综合一区二区 | 亚洲视频在线免费播放| 亚洲福利精品一区二区三区| 67194成是人免费无码| 高清一区二区三区免费视频| 亚洲综合视频在线| 中文字幕在线亚洲精品| 国产成人免费片在线视频观看| 无人在线直播免费观看|