Skip to content

Recent Articles

2

amule 配置

  1. 设置aMule与Firefox关联

    在网络上查阅了好多资料,都是一样的。在firefox下打开about:config。然后添加两条记录

    # 新建boolean,名称为下面。值为true
    network.protocol-handler.external.ed2k
    # 新建字符串,名称为下面。值为/usr/bin/ed2k
    network.protocol-handler.app.ed2k

    然后重启Firefox,点击一个ed2k的链接,在弹出的询问对话框中不要选择默认的/usr/bin/ed2k,而是重新再点“choose“,选择一下”/usr/bin/ed2k”,然后将下面的“记住此选项“打勾,就可以正常下载了。(前提是已经安装amule-utils)

    可是问题出在哪里呢?在更新firefox到3.6以后,我用这个方法怎么也不灵了。设置之后,点ed2k链接还是弹出一个确认框,说firefox未对ed2k协议建立关联。经过多方查询,发现并不像有人说的“新建值的时候需要手动录入,不能copy…..”其实再多建一个值为false的boolean就ok了。其他设置跟上面一样。
    如下:

    # 新建一个boolean,值为false,名称为下行:
    network.protocol-handler.expose.ed2k

31

Ubuntu 10.04 全局菜单(Global Menu)

sudo add-apt-repository ppa:globalmenu-team/ppa
sudo apt-get update
sudo apt-get install gnome-globalmenu
21

我的.vimrc

" All system-wide defaults are set in $VIMRUNTIME/debian.vim (usually just
" /usr/share/vim/vimcurrent/debian.vim) and sourced by the call to :runtime
" you can find below.  If you wish to change any of those settings, you should
" do it in this file (/etc/vim/vimrc), since debian.vim will be overwritten
" everytime an upgrade of the vim packages is performed.  It is recommended to
" make changes after sourcing debian.vim since it alters the value of the
" 'compatible' option.
 
" This line should not be removed as it ensures that various options are
" properly set to work with the Vim-related packages available in Debian.
runtime! debian.vim
 
" Uncomment the next line to make Vim more Vi-compatible
" NOTE: debian.vim sets 'nocompatible'.  Setting 'compatible' changes numerous
" options, so any other options should be set AFTER setting 'compatible'.
"set compatible
 
" Vim5 and later versions support syntax highlighting. Uncommenting the next
" line enables syntax highlighting by default.
" 高亮显示
syntax on
 
" If using a dark background within the editing area and syntax highlighting
" turn on this option as well
" set background=dark
 
" Uncomment the following to have Vim jump to the last position when
" reopening a file
"if has("autocmd")
"  au BufReadPost * if line("'\"") > 0 && line("'\"") <= line("$")
"    \| exe "normal g'\"" | endif
"endif
 
" Uncomment the following to have Vim load indentation rules according to the
" detected filetype. Per default Debian Vim only load filetype specific
" plugins.
"if has("autocmd")
"  filetype indent on
"endif
 
" The following are commented out as they cause vim to behave a lot
" differently from regular Vi. They are highly recommended though.
"set showcmd                " Show (partial) command in status line.
"set showmatch                " Show matching brackets.
"set ignorecase                " Do case insensitive matching
"set smartcase                " Do smart case matching
"set incsearch                " Incremental search
"set autowrite                " Automatically save before commands like :next and :make
"set hidden             " Hide buffers when they are abandoned
"set mouse=a                " Enable mouse usage (all modes) in terminals
 
" Source a global configuration file if available
" XXX Deprecated, please move your changes here in /etc/vim/vimrc
if filereadable("/etc/vim/vimrc.local")
  source /etc/vim/vimrc.local
endif
 
 
if has("autocmd")
 
  " 自动检测文件类型并加载相应的设置
  filetype plugin indent on
 
  " Python 文件的一般设置,比如不要 tab 等
  autocmd FileType python setlocal et | setlocal sta | setlocal sw=4
 
  " Python Unittest 的一些设置
  " 可以让我们在编写 Python 代码及 unittest 测试时不需要离开 vim
  " 键入 :make 或者点击 gvim 工具条上的 make 按钮就自动执行测试用例
  autocmd FileType python compiler pyunit
  autocmd FileType python setlocal makeprg=python\ ./alltests.py
  autocmd BufNewFile,BufRead test*.py setlocal makeprg=python\ %
 
  " 自动使用新文件模板
  " autocmd BufNewFile test*.py 0r ~/.vim/skeleton/test.py
  " autocmd BufNewFile alltests.py 0r ~/.vim/skeleton/alltests.py
  " autocmd BufNewFile *.py 0r ~/.vim/skeleton/skeleton.py
 
endif
 
" python auto-complete code
" Typing the following (in insert mode):
"   os.lis<Ctrl-n>
" will expand to:
"   os.listdir(
" Python 自动补全功能,只需要反覆按 Ctrl-N 就行了
if has("autocmd")
      autocmd FileType python set complete+=k~/.vim/tools/pydiction
endif
 
 
"设置文件类型为python语言
set filetype=python
 
"显示行号
set nu
 
"自动缩进
set autoindent
 
"背景显示颜色
color desert 
 
"高亮搜索
set hlsearch
 
"输入字符串就显示匹配点
set incsearch
 
"输入的命令显示出来,看的清楚些
set showcmd
 
"自动补全命令时候使用菜单式起配列表
set wildmenu
 
"不使用vi的键盘模式,而是vim自己的
set nocompatible
 
set history=50   " keep 50 lines of command line history
set showcmd   " display incomplete commands
 
set guioptions-=T
 
" 设定默认解码
set encoding=utf-8
set fileencodings=utf-8,chinese,latin-1
if has("win32")
    set fileencoding=chinese
else
    set fileencoding=utf-8
endif
language message zh_CN.utf-8
"解决菜单乱码
source $VIMRUNTIME/delmenu.vim
source $VIMRUNTIME/menu.vim
"set font
"set guifont=Nsimsun
 
"设置窗口大小
set lines=35
set columns=120
 
set laststatus=2
 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 文件设置
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 不要备份文件(根据自己需要取舍)
set nobackup
 
" 不要生成swap文件,当buffer被丢弃的时候隐藏它
setlocal noswapfile
set bufhidden=hide
 
" 字符间插入的像素行数目
set linespace=0
 
" 增强模式中的命令行自动完成操作
set wildmenu
 
" 在状态行上显示光标所在位置的行号和列号
set ruler
set rulerformat=%20(%2*%<%f%=\ %m%r\ %3l\ %c\ %p%%%)
 
" 命令行(在状态行下)的高度,默认为1,这里是2
set cmdheight=2
 
" 使回格键(backspace)正常处理indent, eol, start等
set backspace=2
 
" 允许backspace和光标键跨越行边界
set whichwrap+=<,>,h,l
 
" 可以在buffer的任何地方使用鼠标(类似office中在工作区双击鼠标定位)
set mouse=a
set selection=exclusive
set selectmode=mouse,key
 
" 启动的时候不显示那个援助索马里儿童的提示
set shortmess=atI
 
" 通过使用: commands命令,告诉我们文件的哪一行被改变过
set report=0
 
" 不让vim发出讨厌的滴滴声
set noerrorbells
 
" 在被分割的窗口间显示空白,便于阅读
set fillchars=vert:\ ,stl:\ ,stlnc:\
 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 搜索和匹配
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 高亮显示匹配的括号
set showmatch
 
" 匹配括号高亮的时间(单位是十分之一秒)
set matchtime=5
 
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 文本格式和排版
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 自动格式化
set formatoptions=tcrqn
 
" 继承前一行的缩进方式,特别适用于多行注释
set autoindent
 
" 为C程序提供自动缩进
set smartindent
 
" 使用C样式的缩进
set cindent
 
" 制表符为4
set tabstop=4
 
" 统一缩进为4
set softtabstop=4
set shiftwidth=4
 
" 不要用空格代替制表符
set noexpandtab
 
" 不要换行
set nowrap
 
" 在行和段开始处使用制表符
set smarttab
 
"设置Java代码的自动补全
au FileType java setlocal omnifunc=javacomplete#Complete
 
" 按键映射F5直接执行python脚本
map <F5> :!/usr/bin/python %
23

Java单例模式

概要

单例模式是最简单的设计模式之一,但是对于Java的开发者来说,它却有很多缺陷。在本月的专栏中,David Geary探讨了单例模式以及在面对多线程(multithreading)、类装载器(classloaders)和序列化 (serialization)时如何处理这些缺陷。

单例模式适合于一个类只有一个实例的情况,比如窗口管理器,打印缓冲池和文件系统,它们都是原型的例子。典型的情况是,那些对象的类型被遍及一个软件系统的不同对象访问,因此需要一个全局的访问指针,这便是众所周知的单例模式的应用。当然这只有在你确信你不再需要任何多于一个的实例的情况下。

单例模式的用意在于前一段中所关心的。通过单例模式你可以:

  • 确保一个类只有一个实例被建立
  • 提供了一个对对象的全局访问指针
  • 在不影响单例类的客户端的情况下允许将来有多个实例

尽管单例设计模式如在下面的图中的所显示的一样是最简单的设计模式,但对于粗心的Java开发者来说却呈现出许多缺陷。这篇文章讨论了单例模式并揭示了那些缺陷。

注意:你可以从Resources下载这篇文章的源代码。

单例模式

在《设计模式》一书中,作者这样来叙述单例模式的:确保一个类只有一个实例并提供一个对它的全局访问指针。

下图说明了单例模式的类图。
(图1)

单例模式的类图

正如你在上图中所看到的,这不是单例模式的完整部分。此图中单例类保持了一个对唯一的单例实例的静态引用,并且会从静态 getInstance()方法中返回对那个实例的引用。

例1显示了一个经典的单例模式的实现。

例1.经典的单例模式

public class ClassicSingleton {
   private static ClassicSingleton instance = null; 
 
   protected ClassicSingleton() {
      // Exists only to defeat instantiation.
   }
   public static ClassicSingleton getInstance() {
      if(instance == null) {
         instance = new ClassicSingleton();
      }
      return instance;
   }
}

在例1中的单例模式的实现很容易理解。ClassicSingleton类保持了一个对单独的单例实例的静态引用,并且从静态方法 getInstance()中返回那个引用。

关于ClassicSingleton类,有几个让我们感兴趣的地方。首先,ClassicSingleton使用了一个众所周知的懒汉式实例化去创建那个单例类的引用;结果,这个单例类的实例直到getInstance()方法被第一次调用时才被创建。这种技巧可以确保单例类的实例只有在需要时才被建立出来。其次,注意ClassicSingleton实现了一个protected的构造方法,这样客户端不能直接实例化一个 ClassicSingleton类的实例。然而,你会惊奇的发现下面的代码完全合法:

public class SingletonInstantiator {
public SingletonInstantiator() {
    ClassicSingleton instance = ClassicSingleton.getInstance();
    ClassicSingleton anotherInstance = new ClassicSingleton();
    ...
}

前面这个代码片段为何能在没有继承ClassicSingleton并且ClassicSingleton类的构造方法是protected的情况下创建其实例?答案是protected的构造方法可以被其子类以及在同一个包中的其它类调用。因为ClassicSingleton和 SingletonInstantiator位于相同的包(缺省的包),所以SingletonInstantiator方法能创建 ClasicSingleton的实例。

这种情况下有两种解决方案:一是你可以使ClassicSingleton的构造方法变化私有的(private)这样只有 ClassicSingleton的方法能调用它;然而这也意味着ClassicSingleton不能有子类。有时这是一种很合意的解决方法,如果确实如此,那声明你的单例类为final是一个好主意,这样意图明确,并且让编译器去使用一些性能优化选项。另一种解决方法是把你的单例类放到一个外在的包中,以便在其它包中的类(包括缺省的包)无法实例化一个单例类。

关于ClassicSingleton的第三点感兴趣的地方是,如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。

第四点,如果ClasicSingleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。
最后也许是最重要的一点,就是例1中的ClassicSingleton类不是线程安全的。如果两个线程,我们称它们为线程1和线程2,在同一时间调用ClassicSingleton.getInstance()方法,如果线程1先进入if块,然后线程2进行控制,那么就会有 ClassicSingleton的两个的实例被创建。

正如你从前面的讨论中所看到的,尽管单例模式是最简单的设计模式之一,在Java中实现它也是决非想象的那么简单。这篇文章接下来会揭示Java 规范对单例模式进行的考虑,但是首先让我们近水楼台的看看你如何才能测试你的单例类。

测试单例模式

接下来,我使用与log4j相对应的JUnit来测试单例类,它会贯穿在这篇文章余下的部分。如果你对JUnit或log4j不很熟悉,请参考相关资源。

例2是一个用JUnit测试例1的单例模式的案例:

例2.一个单例模式的案例

import org.apache.log4j.Logger;
import junit.framework.Assert;
import junit.framework.TestCase; 
 
public class SingletonTest extends TestCase {
   private ClassicSingleton sone = null, stwo = null;
   private static Logger logger = Logger.getRootLogger(); 
 
   public SingletonTest(String name) {
      super(name);
   }
   public void setUp() {
      logger.info("getting singleton...");
      sone = ClassicSingleton.getInstance();
      logger.info("...got singleton: " + sone); 
 
      logger.info("getting singleton...");
      stwo = ClassicSingleton.getInstance();
      logger.info("...got singleton: " + stwo);
   }
   public void testUnique() {
      logger.info("checking singletons for equality");
      Assert.assertEquals(true, sone == stwo);
   }
}

例2两次调用ClassicSingleton.getInstance(),并且把返回的引用存储在成员变量中。方法testUnique()会检查这些引用看它们是否相同。例3是这个测试案例的输出:

例3.是这个测试案例的输出

Buildfile: build.xml 
 
init:
     [echo] Build 20030414 (14-04-2003 03:08) 
 
compile: 
 
run-test-text:
     [java] .INFO main: [b]getting singleton...[/b]
     [java] INFO main: [b]created singleton:[/b] Singleton@e86f41
     [java] INFO main: ...got singleton: Singleton@e86f41
     [java] INFO main: [b]getting singleton...[/b]
     [java] INFO main: ...got singleton: Singleton@e86f41
     [java] INFO main: checking singletons for equality 
 
     [java] Time: 0.032 
 
     [java] OK (1 test)

正如前面的清单所示,例2的简单测试顺利通过—-通过ClassicSingleton.getInstance()获得的两个单例类的引用确实相同;然而,你要知道这些引用是在单线程中得到的。下面的部分着重于用多线程测试单例类。

多线程因素的考虑

在例1中的ClassicSingleton.getInstance()方法由于下面的代码而不是线程安全的:

if(instance == null) {
    instance = new Singleton();
}

如果一个线程在第二行的赋值语句发生之前切换,那么成员变量instance仍然是null,然后另一个线程可能接下来进入到if块中。在这种情况下,两个不同的单例类实例就被创建。不幸的是这种假定很少发生,这样这种假定也很难在测试期间出现(译注:在这可能是作者对很少出现这种情况而导致无法测试从而使人们放松警惕而感到叹惜)。为了演示这个线程轮换,我得重新实现例1中的那个类。例4就是修订后的单例类:

例4.人为安排的方式

import org.apache.log4j.Logger; 
 
public class Singleton {
  private static Singleton singleton = null;
  private static Logger logger = Logger.getRootLogger();
  private static boolean firstThread = true; 
 
  protected Singleton() {
    // Exists only to defeat instantiation.
  }
  public static Singleton getInstance() {
     if(singleton == null) {
        simulateRandomActivity();
        singleton = new Singleton();
     }
     logger.info("created singleton: " + singleton);
     return singleton;
  }
  private static void simulateRandomActivity() {
     try {
        if(firstThread) {
           firstThread = false;
           logger.info("sleeping..."); 
 
           // This nap should give the second thread enough time
           // to get by the first thread.
             Thread.currentThread().sleep(50);
       }
     }
     catch(InterruptedException ex) {
        logger.warn("Sleep interrupted");
     }
  }
}

除了在这个清单中的单例类强制使用了一个多线程错误处理,例4类似于例1中的单例类。在getInstance()方法第一次被调用时,调用这个方法的线程会休眠50毫秒以便另外的线程也有时间调用getInstance()并创建一个新的单例类实例。当休眠的线程觉醒时,它也会创建一个新的单例类实例,这样我们就有两个单例类实例。

尽管例4是人为如此的,但它却模拟了第一个线程调用了getInstance()并在没有完成时被切换的真实情形。

例5测试了例4的单例类:

例5.失败的测试

import org.apache.log4j.Logger;
import junit.framework.Assert;
import junit.framework.TestCase; 
 
public class SingletonTest extends TestCase {
   private static Logger logger = Logger.getRootLogger();
   private static Singleton singleton = null; 
 
   public SingletonTest(String name) {
      super(name);
   }
   public void setUp() {
      singleton = null;
   }
   public void testUnique() throws InterruptedException {
      // Both threads call Singleton.getInstance().
      Thread threadOne = new Thread(new SingletonTestRunnable()),
             threadTwo = new Thread(new SingletonTestRunnable()); 
 
      threadOne.start();
      threadTwo.start(); 
 
      threadOne.join();
      threadTwo.join();
   }
   private static class SingletonTestRunnable implements Runnable {
      public void run() {
         // Get a reference to the singleton.
         Singleton s = Singleton.getInstance(); 
 
         // Protect singleton member variable from
         // multithreaded access.
         synchronized(SingletonTest.class) {
            if(singleton == null) // If local reference is null...
               singleton = s;     // ...set it to the singleton
         }
         // Local reference must be equal to the one and
         // only instance of Singleton; otherwise, we have two
                  // Singleton instances.
         Assert.assertEquals(true, s == singleton);
      }
   }
}

例5的测试案例创建两个线程,然后各自启动,等待完成。这个案例保持了一个对单例类的静态引用,每个线程都会调用 Singleton.getInstance()。如果这个静态成员变量没有被设置,那么第一个线程就会将它设为通过调用getInstance()而得到的引用,然后这个静态变量会与一个局部变量比较是否相等。

在这个测试案例运行时会发生一系列的事情:第一个线程调用getInstance(),进入if块,然后休眠;接着,第二个线程也调用 getInstance()并且创建了一个单例类的实例。第二个线程会设置这个静态成员变量为它所创建的引用。第二个线程检查这个静态成员变量与一个局部备份的相等性。然后测试通过。当第一个线程觉醒时,它也会创建一个单例类的实例,并且它不会设置那个静态成员变量(因为第二个线程已经设置过了),所以那个静态变量与那个局部变量脱离同步,相等性测试即告失败。例6列出了例5的输出:

例6.例5的输出

Buildfile: build.xml
init:
     [echo] Build 20030414 (14-04-2003 03:06)
compile:
run-test-text:
INFO Thread-1: sleeping...
INFO Thread-2: created singleton: Singleton@7e5cbd
INFO Thread-1: created singleton: Singleton@704ebb
junit.framework.AssertionFailedError: expected: but was:
   at junit.framework.Assert.fail(Assert.java:47)
   at junit.framework.Assert.failNotEquals(Assert.java:282)
   at junit.framework.Assert.assertEquals(Assert.java:64)
   at junit.framework.Assert.assertEquals(Assert.java:149)
   at junit.framework.Assert.assertEquals(Assert.java:155)
   at SingletonTest$SingletonTestRunnable.run(Unknown Source)
   at java.lang.Thread.run(Thread.java:554)
     [java] .
     [java] Time: 0.577 
 
     [java] OK (1 test)

到现在为止我们已经知道例4不是线程安全的,那就让我们看看如何修正它。

同步

要使例4的单例类为线程安全的很容易—-只要像下面一个同步化getInstance()方法:

public synchronized static Singleton getInstance() {
   if(singleton == null) {
      simulateRandomActivity();
      singleton = new Singleton();
   }
   logger.info("created singleton: " + singleton);
   return singleton;
}

在同步化getInstance()方法后,我们就可以得到例5的测试案例返回的下面的结果:

Buildfile: build.xml 
 
init:
     [echo] Build 20030414 (14-04-2003 03:15) 
 
compile:
    [javac] Compiling 2 source files 
 
run-test-text:
INFO Thread-1: sleeping...
INFO Thread-1: created singleton: Singleton@ef577d
INFO Thread-2: created singleton: Singleton@ef577d
     [java] .
     [java] Time: 0.513 
 
     [java] OK (1 test)

这此,这个测试案例工作正常,并且多线程的烦恼也被解决;然而,机敏的读者可能会认识到getInstance()方法只需要在第一次被调用时同步。因为同步的性能开销很昂贵(同步方法比非同步方法能降低到100次左右),或许我们可以引入一种性能改进方法,它只同步单例类的getInstance()方法中的赋值语句。

一种性能改进的方法

寻找一种性能改进方法时,你可能会选择像下面这样重写getInstance()方法:

public static Singleton getInstance() {
   if(singleton == null) {
      synchronized(Singleton.class) {
         singleton = new Singleton();
      }
   }
   return singleton;
}

这个代码片段只同步了关键的代码,而不是同步整个方法。然而这段代码却不是线程安全的。考虑一下下面的假定:线程1进入同步块,并且在它给 singleton成员变量赋值之前线程1被切换。接着另一个线程进入if块。第二个线程将等待直到第一个线程完成,并且仍然会得到两个不同的单例类实例。有修复这个问题的方法吗?请读下去。

双重加锁检查

初看上去,双重加锁检查似乎是一种使懒汉式实例化为线程安全的技术。下面的代码片段展示了这种技术:

public static Singleton getInstance() {
  if(singleton == null) {
     synchronized(Singleton.class) {
       if(singleton == null) {
         singleton = new Singleton();
       }
    }
  }
  return singleton;
}

如果两个线程同时访问getInstance()方法会发生什么?想像一下线程1进行同步块马上又被切换。接着,第二个线程进入if 块。当线程1退出同步块时,线程2会重新检查看是否singleton实例仍然为null。因为线程1设置了singleton成员变量,所以线程2的第二次检查会失败,第二个单例类实例也就不会被创建。似乎就是如此。

不幸的是,双重加锁检查不会保证正常工作,因为编译器会在Singleton的构造方法被调用之前随意给singleton赋一个值。如果在 singleton引用被赋值之后而被初始化之前线程1被切换,线程2就会被返回一个对未初始化的单例类实例的引用。

一个改进的线程安全的单例模式实现

例7列出了一个简单、快速而又是线程安全的单例模式实现:

例7.一个简单的单例类

public class Singleton {
   public final static Singleton INSTANCE = new Singleton();
   private Singleton() {
         // Exists only to defeat instantiation.
      }
}

这段代码是线程安全的是因为静态成员变量一定会在类被第一次访问时被创建。你得到了一个自动使用了懒汉式实例化的线程安全的实现;你应该这样使用它:

      Singleton singleton = Singleton.INSTANCE;
      singleton.dothis();
      singleton.dothat();
      ...

当然万事并不完美,前面的Singleton只是一个折衷的方案;如果你使用那个实现,你就无法改变它以便后来你可能想要允许多个单例类的实例。用一种更折哀的单例模式实现(通过一个getInstance()方法获得实例)你可以改变这个方法以便返回一个唯一的实例或者是数百个实例中的一个.你不能用一个公开且是静态的(public static)成员变量这样做.

你可以安全的使用例7的单例模式实现或者是例1的带一个同步的getInstance()方法的实现.然而,我们必须要研究另一个问题:你必须在编译期指定这个单例类,这样就不是很灵活.一个单例类的注册表会让我们在运行期指定一个单例类.

使用注册表

使用一个单例类注册表可以:

  • 在运行期指定单例类
  • 防止产生多个单例类子类的实例

在例8的单例类中,保持了一个通过类名进行注册的单例类注册表:

例8 带注册表的单例类

import java.util.HashMap;
import org.apache.log4j.Logger; 
 
public class Singleton {
   private static HashMap map = new HashMap();
   private static Logger logger = Logger.getRootLogger(); 
 
   protected Singleton() {
      // Exists only to thwart instantiation
   }
   public static synchronized Singleton getInstance(String classname) {
      if(classname == null) throw new IllegalArgumentException("Illegal classname");
         Singleton singleton = (Singleton)map.get(classname); 
 
      if(singleton != null) {
         logger.info("got singleton from map: " + singleton);
         return singleton;
      }
      if(classname.equals("SingeltonSubclass_One"))
            singleton = new SingletonSubclass_One();
         else if(classname.equals("SingeltonSubclass_Two"))
            singleton = new SingletonSubclass_Two(); 
 
      map.put(classname, singleton);
      logger.info("created singleton: " + singleton);
      return singleton;
   }
   // Assume functionality follows that's attractive to inherit
}

这段代码的基类首先创建出子类的实例,然后把它们存储在一个Map中。但是基类却得付出很高的代价因为你必须为每一个子类替换它的 getInstance()方法。幸运的是我们可以使用反射处理这个问题。

使用反射

在例9的带注册表的单例类中,使用反射来实例化一个特殊的类的对象。与例8相对的是通过这种实现,Singleton.getInstance()方法不需要在每个被实现的子类中重写了。

例9 使用反射实例化单例类

import java.util.HashMap;
import org.apache.log4j.Logger; 
 
public class Singleton {
   private static HashMap map = new HashMap();
   private static Logger logger = Logger.getRootLogger(); 
 
   protected Singleton() {
      // Exists only to thwart instantiation
   }
   public static synchronized Singleton getInstance(String classname) {
      Singleton singleton = (Singleton)map.get(classname); 
 
      if(singleton != null) {
         logger.info("got singleton from map: " + singleton);
         return singleton;
      }
      try {
         singleton = (Singleton)Class.forName(classname).newInstance();
      }
      catch(ClassNotFoundException cnf) {
         logger.fatal("Couldn't find class " + classname);
      }
      catch(InstantiationException ie) {
         logger.fatal("Couldn't instantiate an object of type " + classname);
      }
      catch(IllegalAccessException ia) {
         logger.fatal("Couldn't access class " + classname);
      }
      map.put(classname, singleton);
      logger.info("created singleton: " + singleton); 
 
      return singleton;
   }
}

关于单例类的注册表应该说明的是:它们应该被封装在它们自己的类中以便最大限度的进行复用。

封装注册表

例10列出了一个单例注册表类。

例10 一个SingletonRegistry类

import java.util.HashMap;
import org.apache.log4j.Logger; 
 
public class SingletonRegistry {
   public static SingletonRegistry REGISTRY = new SingletonRegistry(); 
 
   private static HashMap map = new HashMap();
   private static Logger logger = Logger.getRootLogger(); 
 
   protected SingletonRegistry() {
      // Exists to defeat instantiation
   }
   public static synchronized Object getInstance(String classname) {
      Object singleton = map.get(classname); 
 
      if(singleton != null) {
         return singleton;
      }
      try {
         singleton = Class.forName(classname).newInstance();
         logger.info("created singleton: " + singleton);
      }
      catch(ClassNotFoundException cnf) {
         logger.fatal("Couldn't find class " + classname);
      }
      catch(InstantiationException ie) {
         logger.fatal("Couldn't instantiate an object of type " +
                       classname);
      }
      catch(IllegalAccessException ia) {
         logger.fatal("Couldn't access class " + classname);
      }
      map.put(classname, singleton);
      return singleton;
   }
}

注意我是把SingletonRegistry类作为一个单例模式实现的。我也通用化了这个注册表以便它能存储和取回任何类型的对象。例11显示了的 Singleton类使用了这个注册表。

例11 使用了一个封装的注册表的Singleton类

import java.util.HashMap;
import org.apache.log4j.Logger; 
 
public class Singleton { 
 
   protected Singleton() {
      // Exists only to thwart instantiation.
   }
   public static Singleton getInstance() {
      return (Singleton)SingletonRegistry.REGISTRY.getInstance(classname);
   }
}

上面的Singleton类使用那个注册表的唯一实例通过类名取得单例对象。

现在我们已经知道如何实现线程安全的单例类和如何使用一个注册表去在运行期指定单例类名,接着让我们考查一下如何安排类载入器和处理序列化。

Classloaders

在许多情况下,使用多个类载入器是很普通的–包括servlet容器–所以不管你在实现你的单例类时是多么小心你都最终可以得到多个单例类的实例。

如果你想要确保你的单例类只被同一个的类载入器装入,那你就必须自己指定这个类载入器;例如:

private static Class getClass(String classname)
                                         throws ClassNotFoundException {
      ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 
 
      if(classLoader == null)
         classLoader = Singleton.class.getClassLoader(); 
 
      return (classLoader.loadClass(classname));
   }
}

这个方法会尝试把当前的线程与那个类载入器相关联;如果classloader为null,这个方法会使用与装入单例类基类的那个类载入器。这个方法可以用Class.forName()代替。

序列化

如果你序列化一个单例类,然后两次重构它,那么你就会得到那个单例类的两个实例,除非你实现readResolve()方法,像下面这样:

例12 一个可序列化的单例类

import org.apache.log4j.Logger; 
 
public class Singleton implements java.io.Serializable {
   public static Singleton INSTANCE = new Singleton(); 
 
   protected Singleton() {
      // Exists only to thwart instantiation.
   }
   private Object readResolve() {
            return INSTANCE;
      }
   }

上面的单例类实现从readResolve()方法中返回一个唯一的实例;这样无论Singleton类何时被重构,它都只会返回那个相同的单例类实例。

例13测试了例12的单例类。

例13 测试一个可序列化的单例类

import java.io.*;
import org.apache.log4j.Logger;
import junit.framework.Assert;
import junit.framework.TestCase; 
 
public class SingletonTest extends TestCase {
   private Singleton sone = null, stwo = null;
   private static Logger logger = Logger.getRootLogger(); 
 
   public SingletonTest(String name) {
      super(name);
   }
   public void setUp() {
      sone = Singleton.INSTANCE;
      stwo = Singleton.INSTANCE;
   }
   public void testSerialize() {
      logger.info("testing singleton serialization...");
[b]      writeSingleton();
      Singleton s1 = readSingleton();
      Singleton s2 = readSingleton();
      Assert.assertEquals(true, s1 == s2);[/b]   }
   private void writeSingleton() {
      try {
         FileOutputStream fos = new FileOutputStream("serializedSingleton");
         ObjectOutputStream oos = new ObjectOutputStream(fos);
         Singleton s = Singleton.INSTANCE; 
 
         oos.writeObject(Singleton.INSTANCE);
         oos.flush();
      }
      catch(NotSerializableException se) {
         logger.fatal("Not Serializable Exception: " + se.getMessage());
      }
      catch(IOException iox) {
         logger.fatal("IO Exception: " + iox.getMessage());
      }
   }
   private Singleton readSingleton() {
      Singleton s = null; 
 
      try {
         FileInputStream fis = new FileInputStream("serializedSingleton");
         ObjectInputStream ois = new ObjectInputStream(fis);
         s = (Singleton)ois.readObject();
      }
      catch(ClassNotFoundException cnf) {
         logger.fatal("Class Not Found Exception: " + cnf.getMessage());
      }
      catch(NotSerializableException se) {
         logger.fatal("Not Serializable Exception: " + se.getMessage());
      }
      catch(IOException iox) {
         logger.fatal("IO Exception: " + iox.getMessage());
      }
      return s;
   }
   public void testUnique() {
      logger.info("testing singleton uniqueness...");
      Singleton another = new Singleton(); 
 
      logger.info("checking singletons for equality");
      Assert.assertEquals(true, sone == stwo);
   }
}

前面这个测试案例序列化例12中的单例类,并且两次重构它。然后这个测试案例检查看是否被重构的单例类实例是同一个对象。下面是测试案例的输出:

Buildfile: build.xml 
 
init:
     [echo] Build 20030422 (22-04-2003 11:32) 
 
compile: 
 
run-test-text:
     [java] .INFO main: testing singleton serialization...
     [java] .INFO main: testing singleton uniqueness...
     [java] INFO main: checking singletons for equality 
 
     [java] Time: 0.1 
 
     [java] OK (2 tests)

单例模式结束语

单例模式简单却容易让人迷惑,特别是对于Java的开发者来说。在这篇文章中,作者演示了Java开发者在顾及多线程、类载入器和序列化情况如何实现单例模式。作者也展示了你怎样才能实现一个单例类的注册表,以便能够在运行期指定单例类。

转自:Simply Singleton

22

ubuntu10.04 ATI显卡延时

装好10.04之后,发现开源的ATI显卡驱动支持不是很好。花屏严重,正好等到AMD出了ATI10.4的驱动,支持Xorg7.5.可是 最小化最大化的延时bug依然。

好在在ubuntu论坛中找到了解决方案,和9.04一样。需要给X打个补丁。

首先将已经安装的官方ATI驱动或者开源驱动卸载(确保卸载后再进行此操作)。
然后,添加源并更新系统:

sudo add-apt-repository ppa:info-g-com/xserver-xorg-1.7.6-gc
sudo apt-get update && sudo apt-get upgrade

更新后重启,然后安装ATI显卡驱动,再重启。延时问题已经解决。

20

Classical Metal

18

连接sql server 2000服务器的解决方案(转)

解决方案步骤:

一、看ping 服务器IP能否ping通。
这个实际上是看和远程sql server 2000服务器的物理连接是否存在。如果不行,请检查网络,查看配置,当然得确保远程sql server 2000服务器的IP拼写正确。

二、在Dos或命令行下输入telnet 服务器IP 端口,看能否连通。

telnet 202.114.100.100 1433

通常端口值是1433,因为1433是sql server 2000的对于Tcp/IP的默认侦听端口。如果有问题,通常这一步会出问题。通常的提示是“……无法打开连接,连接失败”。
如果这一步有问题,应该检查以下选项。

  • 检查远程服务器是否启动了sql server 2000服务。如果没有,则启动。
  • 检查服务器端有没启用Tcp/IP协议,因为远程连接(通过因特网)需要靠这个协议。检查方法是,在服务器上打开开始菜单->程序->Microsoft SQL Server->服务器网络实用工具,看启用的协议里是否有 tcp/ip协议,如果没有,则启用它。
  • 检查服务器的tcp/ip端口是否配置为1433端口。仍然在服务器网络实用工具里查看启用协议里面的tcp/ip的属性,确保默认端口为1433,并且隐藏服务器复选框没有勾上。
    事实上,如果默认端口被修改,也是可以的,但是在客户端做telnet测试时,写服务器端口号时必须与服务器配置的端口号保持一致。如果隐藏服务器复选框被勾选,则意味着客户端无法通过枚举服务器来看到这台服务器,起到了保护的作用,但不影响连接,但是Tcp/ip协议的默认端口将被隐式修改为2433,在客户端连接时必须作相应的改变。
  • 如果服务器端操作系统打过sp2补丁,则要对windows防火墙作一定的配置,要对它开放 1433端口,通常在测试时可以直接关掉windows防火墙(其他的防火墙也关掉最好)。
  • 检查服务器是否在1433端口侦听。如果服务器没有在tcp连接的1433端口侦听,则是连接不上的。检查方法是在服务器的dos或命令行下面输入 netstat -a -n 或者是netstat -an,在结果列表里看是否有类似 tcp 127.0.0.1 1433 listening 的项。如果没有,则通常需要给sql server 2000打上至少sp3的补丁。其实在服务器端启动查询分析器,输入 select @@version 执行后可以看到版本号,版本号在8.0.2039以下的都需要打补丁。

如果以上都没问题,这时你再做telnet 服务器ip 1433 测试,将会看到屏幕一闪之后光标在左上角不停闪动。恭喜你,你马上可以开始在企业管理器或查询分析器连接了。

三、检查客户端设置程序->Microsoft SQL Server-> 客户端网络使用工具。像在服务器网络实用工具里一样,确保客户端 tcp/ip协议启用,并且默认端口为1433(或其他端口,与服务器端保持一致就行)。

四、在企业管理器里或查询分析器连接测试。

企业管理器->右键SQlserver组->新建 sqlserver注册->下一步->写入远程IP->下一步 -> 选Sqlserver登陆->下一 步->写入登陆名与密码(sa,password)->下一步-> 下一步->完成 。

查询分析器->文件->连接->写入远程IP->写入登录名和密码(sa,password)->确定。通常建议在查询分析器里做,因为默认情况下,通过企业管理器注册另外一台SQL Server的超时设置是4秒,而查询分析器是15秒。

修改默认连接超时的方法:

企业管理器->工具->选项->在弹出的”SQL Server企业管理器属性”窗口中,点击”高级”选项卡->连接设置->在 登录超时(秒) 后面的框里输入一个较大的数字查询分析器->工具->选项->连接->在 登录超时(秒) 后面的框里输入一个较大的数字通常就可以连通了,如果提示错误,则进入下一步。

五、错误产生的原因通常是由于SQL Server使用了”仅 Windows”的身份验证方式,因此用户无法使用SQL Server的登录帐户(如 sa )进行连接。解决方法如下所示:

  • 在服务器端使用企业管理器,并且选择”使用 Windows 身份验证”连接上 SQL Server。
  • 展开”SQL Server组”,鼠标右键点击SQL Server服务器的名称,选择”属性”,再选择”安全性”选项卡。
  • 在”身份验证”下,选择”SQL Server和 Windows “。
  • 重新启动SQL Server服务。(在dos或命令行下面net stop mssqlserver停止服务,net start mssqlserver启动服务,也是一种快捷的方法)。 注:在连接本地服务器时,通常使用的是命名管道协议(在服务器网络实用工具里可以看到启用的协议有这个),默认端口是445,因此在本地能连通是不能说明什么问题的,连接远程服务器是完全不同的协议)。
13

NetBeans字体显示

一直没有弄好netbeans下面字体显示的锯齿问题。昨天查了下G大神,很轻松的搞定了。只需要一行代码(这种方法是作用于JRE全局的,就是所有用到JRE的程序都会开启这个字体渲染,当然也可以在NB的配置文件中单独开启字体渲染)。
在JDK系统配置中加上一行开启渲染参数,这个参数我知道有三个可选值。

#首先打开系统配置文件(需要root身份)
vim /etc/profile
#如果进行过JAVA环境变量配置,则在环境变量配置后面加上这样一行
export _JAVA_OPTIONS='-Dawt.useSystemAAFontSettings=lcd'
#lcd也可为“on"

完成后,重启X,我使用source /etc/profile 不起作用。

NetBeans IDE 6.9 Beta

13

ubuntu10.04下安装Chromium

这里的Chromium不是Chrome。
打开终端,输入下面命令:

    sudo add-apt-repository ppa:chromium-daily/ppa
 
    sudo apt-get update
 
    sudo apt-get install chromium-browser

完成了,如此简单,不用过多解释吧。

chromium

12

林氏营养早餐、林氏养生茶、林氏排毒餐

一、林氏营养早餐

1. 原料

健康熟鸡蛋一个,苹果一个(香蕉一根),一把熟豆(各种豆类的集合),一袋酸奶(牛奶),一勺蛋白粉。

2. 制作方法

放入搅拌机搅碎倒入杯中即可食用。

3. 营养价值及作用

含有丰富的维生素及蛋白质,低热量。

制作简单、食用方便、节省时间、营养丰富,减少胃的消化负担。几乎包含人体所需的全部营养素,在现代人忙碌的生活中,营养早餐可满足一天的营养需求,让你一整天精力充沛,适宜长期食用。

二、林氏养生茶

1. 原料

黄豆、黑豆、芸豆、红豆、白豆、绿豆各一把。

小米、苡仁、黑米各一小把。

以上物质加水混合,浸泡一晚(6小时以上),然后加水煮熟,待用。

加入花生、芝麻、坚果、葡萄干等各一小勺,橄榄油一大勺,蜂蜜一勺,蛋白质粉一勺,维生素B(1.2毫克),维生素C(60毫克)各两颗。

2. 制作方法

将以上全部倒入搅拌机搅拌成奶昔状即可食用。称为茶,意思是可以随时饮用。

3. 营养价值及作用

此食物完全可以替代中餐和晚餐,营养丰富完整,不多不少,容易消化和吸收。对于亚健康及慢性疾病患者,有非常好的康复支持作用。可应用在心血管疾病、糖尿病、老年痴呆、妇女更年期症状调整、不孕、前列腺肥大、手脚冰冷等健康问题上。同时也是一种高能量的抗癌食物。

三、林式排毒餐

1.原料

地瓜一个,芋头少量,土豆一个,南瓜一块,菠萝半个,蛋白质粉半勺,橄榄油两勺。食用时,最好额外补充钙镁、维生素B、维生素C、维生素E及鱼油。

2. 制作方法

食用时将地瓜、芋头、土豆、南瓜、菠萝蒸熟,放入搅拌机,加水搅拌,成奶昔状。味道绝美而香,口感卓越。可以早、中、晚三餐食用,连用一周。

3. 营养价值及作用

排毒餐的制作简单、食用方便、节省时间、营养丰富,减少胃的消化负担。几乎包含人体所需的全部营养素。可作为补充食物,长期食用,对调理慢性疾病有显著的效果。

体内毒素的清除,是我们要做的一个重要工作。食物中有些好东西,可以长期食用,营养丰富,又同时清肠清血。它包括:地瓜、南瓜、土豆、芋头、山药。这些食物含有丰富的维生素及蛋白质,纤维含量高、热量低、营养均衡。能够彻底排除肠内垃圾,以均衡营养,达到美颜皮肤,抗衰老作用。虽然它们的淀粉含量丰富,却可以帮助减肥。
养生