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


常用链接

留言簿(4)

随笔档案

相册

我的兄弟们

搜索

  •  

最新评论

阅读排行榜

评论排行榜

     摘要: 很久没有回来这里写技术BLOG了,这里的氛围还行,大家都对一个问题积极的思考(至少之前这里给我的感觉是这样的),2年里面自己也忙着做些事情,没有写,最近有空也就写写,偶尔会去oschine.net看看新闻,然后就在那里看到了一个人提出的问题很有意思,就是怎么表达式求解,例如(1 + 2) / 3 - 1 * 2 + 5 / (3 + 2)这样的字符串输入,怎么样解析之后输出结果。说来也好笑,对于我...  阅读全文
posted @ 2011-11-09 10:36 ruislan 阅读(1698) | 评论 (6)编辑 收藏
     摘要: 很久没来了,不是一位朋友给我发邮件问我关于swing的问题,才想起,然后翻看了之前的代码,发现当年还实现了一个Vista风格的按钮没有放出来,现在补上,也许现在人们的swing水平对我这代码不屑一顾,不过还是依然抛砖引玉,给未知的人们一个启发。还是老惯例,上效果图: 正常情况下:(和vista一样具有焦点的按钮的颜色渐深渐浅的循环变化) 鼠标在区域内时: 鼠标按下去: 然后是代...  阅读全文
posted @ 2009-09-12 12:54 ruislan 阅读(2452) | 评论 (3)编辑 收藏
认为自己是达人的就不用看了。只是一点小技巧,不敢班门弄斧,做个总结,为那些还不知道的解解惑,随便告诉大家我还活着。

最近客户提了个小改动,客户网站上图片存放的目录需要改动一下。例如在网上访问是www.blogjava.net/images/*.*,在服务器上的目录是D:/<webroot>/images/*.*,客户想把这个images目录下的资源全部移动到E:/data/里面去,但是在网上www.blogjava.net/images/*.*还是同样可以访问得到,我刚开始犯了形式主义的错误,老是想用程序解决,一会filter,一会servlet/action,后来我配置程序的时候突然看到了server.xml,于是我想到了选择用映射的方式。正好,server.xml中的<Context>就是做这个事情的。于是乎我们在<Host></Host>中增加了一个<Context docBase="E:/data/images" path="/images">,OK,重启之后,所有检索www.blogjava.net/images路径下的资源实际上都由E:/data/images下的资源提供了。

posted @ 2008-02-15 16:41 ruislan 阅读(1246) | 评论 (3)编辑 收藏
在写多线程程序的时候,你就像个经理,手下有那么或多或少的职员,你负责协调职员之间的工作,如果你稍不留神,职员之间就陷入了相互等待的尴尬状态。还好,大多数时候多线程都还在我们掌控之内,即便是遇到这样的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.blogjava.net"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.blogjava.net"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 @ 2007-12-13 18:31 ruislan 阅读(1319) | 评论 (3)编辑 收藏