分类目录归档:程序生活

Program Life – Web

Eclipse 3.8 M1发布,支持Java 7

Eclipse基金会发布了Eclipse 3.8 M1版,这是第一个支持Java 7的里程碑版本。
 

该版本的改进包括:
 

  • 代码自动提示支持Diamond operators。
  • 移除多余的类型文字,并使用diamond operator代替
  • Content assist can be used to insert types into diamonds
  • Multi-catch can be extended with a "Surround with try/multi-catch", complementing the existing "Surround with try/catch"
  • Multi-catch can be extended with new exception types
  • Exception blocks can be combined into a single multi-catch block
  • Resources closed with try-with-resources can detect thrown exceptions and add to (multi-) catch blocks
  • Strings-in-switch 语句能够被转换到嵌套的 if/else 语句中
  • The Java7 execution environment has been added for dependency on execution environments

该版本针对Java 7的详细改进信息http://www.eclipse.org/jdt/ui/r3_8/Java7news/whats-new-java-7.html

下载地址http://download.eclipse.org/eclipse/downloads/drops/S-3.8M1-201108031800/

使用 dom4j 解析 XML

dom4j API 包含一个解析 XML 文档的工具。本文中将使用这个解析器创建一个示例 XML 文档。清单 1 显示了这个示例 XML 文档,catalog.xml。

清单 1. 示例 XML 文档(catalog.xml)

 

<?xml version="1.0" encoding="UTF-8"?> 
<catalog> 
<!--An XML Catalog--> 
<?target instruction?>
  <journal title="XML Zone" 
                  publisher="IBM developerWorks"> 
<article level="Intermediate" date="December-2001">
 <title>Java configuration with XML Schema</title> 
 <author> 
     <firstname>Marcello</firstname> 
     <lastname>Vitaletti</lastname> 
 </author>
  </article>
  </journal> 
</catalog>

 

然后使用同一个解析器修改 catalog.xml,清单 2 是修改后的 XML 文档,catalog-modified.xml。

清单 2. 修改后的 XML 文档(catalog-modified.xml)

 

<?xml version="1.0" encoding="UTF-8"?> 
<catalog> 
<!--An XML catalog--> 
<?target instruction?>
  <journal title="XML Zone"
                   publisher="IBM developerWorks"> 
<article level="Introductory" date="October-2002">
 <title>Create flexible and extensible XML schemas</title> 
 <author> 
     <firstname>Ayesha</firstname> 
     <lastname>Malik</lastname> 
 </author> 
  </article>
  </journal> 
</catalog>

 

与 W3C DOM API 相比,使用 dom4j 所包含的解析器的好处是 dom4j 拥有本地的 XPath 支持。DOM 解析器不支持使用 XPath 选择节点。

本文包括以下几个部分:

  • 预先设置
  • 创建文档
  • 修改文档

预先设置

这个解析器可以从 http://dom4j.org 获取。通过设置使 dom4j-1.4/dom4j-full.jar 能够在 classpath 中访问,该文件中包括 dom4j 类、XPath 引擎以及 SAX 和 DOM 接口。如果已经使用了 JAXP 解析器中包含的 SAX 和 DOM 接口,向 classpath 中增加 dom4j-1.4/dom4j.jardom4j.jar 包括 dom4j 类和 XPath 引擎,但是不含 SAX 与 DOM 接口。

创建文档

本节讨论使用 dom4j API 创建 XML 文档的过程,并创建示例 XML 文档 catalog.xml。

使用 import 语句导入 dom4j API 类:

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

 

使用 DocumentHelper 类创建一个文档实例。 DocumentHelper 是生成 XML 文档节点的 dom4j API 工厂类。

 Document document = DocumentHelper.createDocument();

 

使用 addElement() 方法创建根元素 catalog addElement() 用于向 XML 文档中增加元素。

Element catalogElement = document.addElement("catalog");

 

catalog 元素中使用 addComment() 方法添加注释“An XML catalog”。

 catalogElement.addComment("An XML catalog");

 

catalog 元素中使用 addProcessingInstruction() 方法增加一个处理指令。

catalogElement.addProcessingInstruction("target","text");

 

catalog 元素中使用 addElement() 方法增加 journal 元素。

Element journalElement =  catalogElement.addElement("journal");

 

使用 addAttribute() 方法向 journal 元素添加 titlepublisher 属性。

journalElement.addAttribute("title", "XML Zone");
         journalElement.addAttribute("publisher", "IBM developerWorks");

 

article 元素中添加 journal 元素。

Element articleElement=journalElement.addElement("article");

 

article 元素增加 leveldate 属性。

articleElement.addAttribute("level", "Intermediate");
      articleElement.addAttribute("date", "December-2001");

 

article 元素中增加 title 元素。

Element titleElement=articleElement.addElement("title");

 

使用 setText() 方法设置 article 元素的文本。

titleElement.setText("Java configuration with XML Schema");

 

article 元素中增加 author 元素。

Element authorElement=articleElement.addElement("author");

 

author 元素中增加 firstname 元素并设置该元素的文本。

Element  firstNameElement=authorElement.addElement("firstname");
     firstNameElement.setText("Marcello");

 

author 元素中增加 lastname 元素并设置该元素的文本。

Element lastNameElement=authorElement.addElement("lastname");
     lastNameElement.setText("Vitaletti");

 

可以使用 addDocType() 方法添加文档类型说明。

document.addDocType("catalog", null,"file://c:/Dtds/catalog.dtd");

 

这样就向 XML 文档中增加文档类型说明:

<!DOCTYPE catalog SYSTEM "file://c:/Dtds/catalog.dtd">

 

如果文档要使用文档类型定义(DTD)文档验证则必须有 Doctype。

XML 声明 <?xml version="1.0" encoding="UTF-8"?> 自动添加到 XML 文档中。

清单 3 所示的例子程序 XmlDom4J.java 用于创建 XML 文档 catalog.xml。

清单 3. 生成 XML 文档 catalog.xml 的程序(XmlDom4J.java)

 

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.XMLWriter;
import java.io.*;
public class XmlDom4J{
public void generateDocument(){
Document document = DocumentHelper.createDocument();
     Element catalogElement = document.addElement("catalog");
     catalogElement.addComment("An XML Catalog");
     catalogElement.addProcessingInstruction("target","text");
     Element journalElement =  catalogElement.addElement("journal");
     journalElement.addAttribute("title", "XML Zone");
     journalElement.addAttribute("publisher", "IBM developerWorks");
     Element articleElement=journalElement.addElement("article");
     articleElement.addAttribute("level", "Intermediate");
     articleElement.addAttribute("date", "December-2001");
     Element  titleElement=articleElement.addElement("title");
     titleElement.setText("Java configuration with XML Schema");
     Element authorElement=articleElement.addElement("author");
     Element  firstNameElement=authorElement.addElement("firstname");
     firstNameElement.setText("Marcello");
     Element lastNameElement=authorElement.addElement("lastname");
     lastNameElement.setText("Vitaletti");
     document.addDocType("catalog",
                           null,"file://c:/Dtds/catalog.dtd");
    try{
    XMLWriter output = new XMLWriter(
            new FileWriter( new File("c:/catalog/catalog.xml") ));
        output.write( document );
        output.close();
        }
     catch(IOException e){System.out.println(e.getMessage());}
}
public static void main(String[] argv){
XmlDom4J dom4j=new XmlDom4J();
dom4j.generateDocument();
}}

 

这一节讨论了创建 XML 文档的过程,下一节将介绍使用 dom4j API 修改这里创建的 XML 文档。

修改文档

这一节说明如何使用 dom4j API 修改示例 XML 文档 catalog.xml。

使用 SAXReader 解析 XML 文档 catalog.xml:

SAXReader saxReader = new SAXReader();
 Document document = saxReader.read(inputXml);

 

SAXReader 包含在 org.dom4j.io 包中。

inputXml 是从 c:/catalog/catalog.xml 创建的 java.io.File。使用 XPath 表达式从 article 元素中获得 level 节点列表。如果 level 属性值是“Intermediate”则改为“Introductory”。

List list = document.selectNodes("//article/@level" );
      Iterator iter=list.iterator();
        while(iter.hasNext()){
            Attribute attribute=(Attribute)iter.next();
               if(attribute.getValue().equals("Intermediate"))
               attribute.setValue("Introductory"); 
       }

 

获取 article 元素列表,从 article 元素中的 title 元素得到一个迭代器,并修改 title 元素的文本。

list = document.selectNodes("//article" );
     iter=list.iterator();
   while(iter.hasNext()){
       Element element=(Element)iter.next();
      Iterator iterator=element.elementIterator("title");
   while(iterator.hasNext()){
   Element titleElement=(Element)iterator.next();
   if(titleElement.getText().equals("Java configuration with XML Schema"))
     titleElement.setText("Create flexible and extensible XML schema");
    }}

 

通过和 title 元素类似的过程修改 author 元素。

清单 4 所示的示例程序 Dom4JParser.java 用于把 catalog.xml 文档修改成 catalog-modified.xml 文档。

清单 4. 用于修改 catalog.xml 的程序(Dom4Jparser.java)

 

import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Attribute;
import java.util.List;
import java.util.Iterator;
import org.dom4j.io.XMLWriter;
import java.io.*;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader; 
public class Dom4JParser{
 public void modifyDocument(File inputXml){
  try{
   SAXReader saxReader = new SAXReader();
   Document document = saxReader.read(inputXml);
   List list = document.selectNodes("//article/@level" );
   Iterator iter=list.iterator();
   while(iter.hasNext()){
    Attribute attribute=(Attribute)iter.next();
    if(attribute.getValue().equals("Intermediate"))
      attribute.setValue("Introductory"); 
       }
   
   list = document.selectNodes("//article/@date" );
   iter=list.iterator();
   while(iter.hasNext()){
    Attribute attribute=(Attribute)iter.next();
    if(attribute.getValue().equals("December-2001"))
      attribute.setValue("October-2002");
       }
   list = document.selectNodes("//article" );
   iter=list.iterator();
   while(iter.hasNext()){
    Element element=(Element)iter.next();
    Iterator iterator=element.elementIterator("title");
      while(iterator.hasNext()){
        Element titleElement=(Element)iterator.next();
        if(titleElement.getText().equals("Java configuration with XML
      Schema"))
        titleElement.setText("Create flexible and extensible XML schema");
                                          }
                                }
    list = document.selectNodes("//article/author" );
    iter=list.iterator();
     while(iter.hasNext()){
     Element element=(Element)iter.next();
     Iterator iterator=element.elementIterator("firstname");
     while(iterator.hasNext()){
      Element firstNameElement=(Element)iterator.next();
      if(firstNameElement.getText().equals("Marcello"))
      firstNameElement.setText("Ayesha");
                                     }
                              }
    list = document.selectNodes("//article/author" );
    iter=list.iterator();
     while(iter.hasNext()){
      Element element=(Element)iter.next();
      Iterator iterator=element.elementIterator("lastname");
     while(iterator.hasNext()){
      Element lastNameElement=(Element)iterator.next();
      if(lastNameElement.getText().equals("Vitaletti"))
      lastNameElement.setText("Malik");
                                  }
                               }
     XMLWriter output = new XMLWriter(
      new FileWriter( new File("c:/catalog/catalog-modified.xml") ));
     output.write( document );
     output.close();
   }
 
  catch(DocumentException e)
                 {
                  System.out.println(e.getMessage());
                            }
  catch(IOException e){
                       System.out.println(e.getMessage());
                    }
 }
 public static void main(String[] argv){
  Dom4JParser dom4jParser=new Dom4JParser();
  dom4jParser.modifyDocument(new File("c:/catalog/catalog.xml"));
                                        }
   }

 

这一节说明了如何使用 dom4j 中的解析器修改示例 XML 文档。这个解析器不使用 DTD 或者模式验证 XML 文档。如果 XML 文档需要验证,可以解释用 dom4j 与 JAXP SAX 解析器。

结束语

包含在 dom4j 中的解析器是一种用于解析 XML 文档的非验证性工具,可以与JAXP、Crimson 或 Xerces 集成。本文说明了如何使用该解析器创建和修改 XML 文档。

 

参考资料

Java线程:新特征-障碍器

Java线程:新特征-障碍器
 
Java5中,添加了障碍器类,为了适应一种新的设计需求,比如一个大型的任务,常常需要分配好多子任务去执行,只有当所有子任务都执行完成时候,才能执行主任务,这时候,就可以选择障碍器了。
 
障碍器是多线程并发控制的一种手段,用法很简单。下面给个例子:
 
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
* Java线程:新特征-障碍器
*
* @author leizhimin 2009-11-6 10:50:10
*/

public class Test {
        public static void main(String[] args) {
                //创建障碍器,并设置MainTask为所有定数量的线程都达到障碍点时候所要执行的任务(Runnable)
                CyclicBarrier cb = new CyclicBarrier(7, new MainTask());
                new SubTask("A", cb).start();
                new SubTask("B", cb).start();
                new SubTask("C", cb).start();
                new SubTask("D", cb).start();
                new SubTask("E", cb).start();
                new SubTask("F", cb).start();
                new SubTask("G", cb).start();
        }
}

/**
* 主任务
*/

class MainTask implements Runnable {
        public void run() {
                System.out.println(">>>>主任务执行了!<<<<");
        }
}

/**
* 子任务
*/

class SubTask extends Thread {
        private String name;
        private CyclicBarrier cb;

        SubTask(String name, CyclicBarrier cb) {
                this.name = name;
                this.cb = cb;
        }

        public void run() {
                System.out.println("[子任务" + name + "]开始执行了!");
                for (int i = 0; i < 999999; i++) ;    //模拟耗时的任务
                System.out.println("[子任务" + name + "]开始执行完成了,并通知障碍器已经完成!");
                try {
                        //通知障碍器已经完成
                        cb.await();
                } catch (InterruptedException e) {
                        e.printStackTrace();
                } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                }
        }
}

 
运行结果:
[子任务E]开始执行了!
[子任务E]开始执行完成了,并通知障碍器已经完成!
[子任务F]开始执行了!
[子任务G]开始执行了!
[子任务F]开始执行完成了,并通知障碍器已经完成!
[子任务G]开始执行完成了,并通知障碍器已经完成!
[子任务C]开始执行了!
[子任务B]开始执行了!
[子任务C]开始执行完成了,并通知障碍器已经完成!
[子任务D]开始执行了!
[子任务A]开始执行了!
[子任务D]开始执行完成了,并通知障碍器已经完成!
[子任务B]开始执行完成了,并通知障碍器已经完成!
[子任务A]开始执行完成了,并通知障碍器已经完成!
>>>>主任务执行了!<<<<

Process finished with exit code 0

 
从执行结果可以看出,所有子任务完成的时候,主任务执行了,达到了控制的目标。

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/222738

Java线程:新特征-原子量

Java线程:新特征-原子量
 
所谓的原子量即操作变量的操作是“原子的”,该操作不可再分,因此是线程安全的。
 
为何要使用原子变量呢,原因是多个线程对单个变量操作也会引起一些问题。在Java5之前,可以通过volatile、synchronized关键字来解决并发访问的安全问题,但这样太麻烦。
Java5之后,专门提供了用来进行单变量多线程并发安全访问的工具包java.util.concurrent.atomic,其中的类也很简单。
 

下面给出一个反面例子(切勿模仿):
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;

/**
* Java线程:新特征-原子量
*
* @author leizhimin 2009-11-6 9:53:11
*/

public class Test {
        public static void main(String[] args) {
                ExecutorService pool = Executors.newFixedThreadPool(2);
                Runnable t1 = new MyRunnable("张三", 2000);
                Runnable t2 = new MyRunnable("李四", 3600);
                Runnable t3 = new MyRunnable("王五", 2700);
                Runnable t4 = new MyRunnable("老张", 600);
                Runnable t5 = new MyRunnable("老牛", 1300);
                Runnable t6 = new MyRunnable("胖子", 800);
                //执行各个线程
                pool.execute(t1);
                pool.execute(t2);
                pool.execute(t3);
                pool.execute(t4);
                pool.execute(t5);
                pool.execute(t6);
                //关闭线程池
                pool.shutdown();
        }
}

class MyRunnable implements Runnable {
        private static AtomicLong aLong = new AtomicLong(10000);        //原子量,每个线程都可以自由操作
        private String name;                //操作人
        private int x;                            //操作数额

        MyRunnable(String name, int x) {
                this.name = name;
                this.x = x;
        }

        public void run() {
                System.out.println(name + "执行了" + x + ",当前余额:" + aLong.addAndGet(x));
        }
}

 
运行结果:
李四执行了3600,当前余额:13600
王五执行了2700,当前余额:16300
老张执行了600,当前余额:16900
老牛执行了1300,当前余额:18200
胖子执行了800,当前余额:19000
张三执行了2000,当前余额:21000

Process finished with exit code 0

 
张三执行了2000,当前余额:12000
王五执行了2700,当前余额:18300
老张执行了600,当前余额:18900
老牛执行了1300,当前余额:20200
胖子执行了800,当前余额:21000
李四执行了3600,当前余额:15600

Process finished with exit code 0

 
张三执行了2000,当前余额:12000
李四执行了3600,当前余额:15600
老张执行了600,当前余额:18900
老牛执行了1300,当前余额:20200
胖子执行了800,当前余额:21000
王五执行了2700,当前余额:18300

Process finished with exit code 0

 
从运行结果可以看出,虽然使用了原子量,但是程序并发访问还是有问题,那究竟问题出在哪里了?
 
这里要注意的一点是,原子量虽然可以保证单个变量在某一个操作过程的安全,但无法保证你整个代码块,或者整个程序的安全性。因此,通常还应该使用锁等同步机制来控制整个程序的安全性。
 
下面是对这个错误修正:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.atomic.AtomicLong;

/**
* Java线程:新特征-原子量
*
* @author leizhimin 2009-11-6 9:53:11
*/

public class Test {
        public static void main(String[] args) {
                ExecutorService pool = Executors.newFixedThreadPool(2);
                Lock lock = new ReentrantLock(false);
                Runnable t1 = new MyRunnable("张三", 2000,lock);
                Runnable t2 = new MyRunnable("李四", 3600,lock);
                Runnable t3 = new MyRunnable("王五", 2700,lock);
                Runnable t4 = new MyRunnable("老张", 600,lock);
                Runnable t5 = new MyRunnable("老牛", 1300,lock);
                Runnable t6 = new MyRunnable("胖子", 800,lock);
                //执行各个线程
                pool.execute(t1);
                pool.execute(t2);
                pool.execute(t3);
                pool.execute(t4);
                pool.execute(t5);
                pool.execute(t6);
                //关闭线程池
                pool.shutdown();
        }
}

class MyRunnable implements Runnable {
        private static AtomicLong aLong = new AtomicLong(10000);        //原子量,每个线程都可以自由操作
        private String name;                //操作人
        private int x;                            //操作数额
        private Lock lock;

        MyRunnable(String name, int x,Lock lock) {
                this.name = name;
                this.x = x;
                this.lock = lock;
        }

        public void run() {
                lock.lock();
                System.out.println(name + "执行了" + x + ",当前余额:" + aLong.addAndGet(x));
                lock.unlock();
        }
}

 
执行结果:
张三执行了2000,当前余额:12000
王五执行了2700,当前余额:14700
老张执行了600,当前余额:15300
老牛执行了1300,当前余额:16600
胖子执行了800,当前余额:17400
李四执行了3600,当前余额:21000

Process finished with exit code 0

 
这里使用了一个对象锁,来控制对并发代码的访问。不管运行多少次,执行次序如何,最终余额均为21000,这个结果是正确的。
 
有关原子量的用法很简单,关键是对原子量的认识,原子仅仅是保证变量操作的原子性,但整个程序还需要考虑线程安全的。
 

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/222541

Java线程:新特征-条件变量

Java线程:新特征-条件变量
 
条件变量是Java5线程中很重要的一个概念,顾名思义,条件变量就是表示条件的一种变量。但是必须说明,这里的条件是没有实际含义的,仅仅是个标记而已,并且条件的含义往往通过代码来赋予其含义。
 
这里的条件和普通意义上的条件表达式有着天壤之别。
 
条件变量都实现了java.util.concurrent.locks.Condition接口,条件变量的实例化是通过一个Lock对象上 调用newCondition()方法来获取的,这样,条件就和一个锁对象绑定起来了。因此,Java中的条件变量只能和锁配合使用,来控制并发程序访问 竞争资源的安全。
 
条件变量的出现是为了更精细控制线程等待与唤醒,在Java5之前,线程的等待与唤醒依靠的是Object对象的wait()和notify()/notifyAll()方法,这样的处理不够精细。
 
而在Java5中,一个锁可以有多个条件,每个条件上可以有多个线程等待,通过调用await()方法,可以让线程在该条件下等待。当调用signalAll()方法,又可以唤醒该条件下的等待的线程。有关Condition接口的API可以具体参考JavaAPI文档。
 
条件变量比较抽象,原因是他不是自然语言中的条件概念,而是程序控制的一种手段。
 
下面以一个银行存取款的模拟程序为例来揭盖Java多线程条件变量的神秘面纱:
 
有一个账户,多个用户(线程)在同时操作这个账户,有的存款有的取款,存款随便存,取款有限制,不能透支,任何试图透支的操作都将等待里面有足够存款才执行操作。
 
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* Java线程:条件变量
*
* @author leizhimin 2009-11-5 10:57:29
*/

public class Test {
        public static void main(String[] args) {
                //创建并发访问的账户
                MyCount myCount = new MyCount("95599200901215522", 10000);
                //创建一个线程池
                ExecutorService pool = Executors.newFixedThreadPool(2);
                Thread t1 = new SaveThread("张三", myCount, 2000);
                Thread t2 = new SaveThread("李四", myCount, 3600);
                Thread t3 = new DrawThread("王五", myCount, 2700);
                Thread t4 = new SaveThread("老张", myCount, 600);
                Thread t5 = new DrawThread("老牛", myCount, 1300);
                Thread t6 = new DrawThread("胖子", myCount, 800);
                //执行各个线程
                pool.execute(t1);
                pool.execute(t2);
                pool.execute(t3);
                pool.execute(t4);
                pool.execute(t5);
                pool.execute(t6);
                //关闭线程池
                pool.shutdown();
        }
}

/**
* 存款线程类
*/

class SaveThread extends Thread {
        private String name;                //操作人
        private MyCount myCount;        //账户
        private int x;                            //存款金额

        SaveThread(String name, MyCount myCount, int x) {
                this.name = name;
                this.myCount = myCount;
                this.x = x;
        }

        public void run() {
                myCount.saving(x, name);
        }
}

/**
* 取款线程类
*/

class DrawThread extends Thread {
        private String name;                //操作人
        private MyCount myCount;        //账户
        private int x;                            //存款金额

        DrawThread(String name, MyCount myCount, int x) {
                this.name = name;
                this.myCount = myCount;
                this.x = x;
        }

        public void run() {
                myCount.drawing(x, name);
        }
}

/**
* 普通银行账户,不可透支
*/

class MyCount {
        private String oid;                         //账号
        private int cash;                             //账户余额
        private Lock lock = new ReentrantLock();                //账户锁
        private Condition _save = lock.newCondition();    //存款条件
        private Condition _draw = lock.newCondition();    //取款条件

        MyCount(String oid, int cash) {
                this.oid = oid;
                this.cash = cash;
        }

        /**
         * 存款
         *
         * @param x        操作金额
         * @param name 操作人
         */

        public void saving(int x, String name) {
                lock.lock();                        //获取锁
                if (x > 0) {
                        cash += x;                    //存款
                        System.out.println(name + "存款" + x + ",当前余额为" + cash);
                }
                _draw.signalAll();            //唤醒所有等待线程。
                lock.unlock();                    //释放锁
        }

        /**
         * 取款
         *
         * @param x        操作金额
         * @param name 操作人
         */

        public void drawing(int x, String name) {
                lock.lock();                                 //获取锁
                try {
                        if (cash – x < 0) {
                                _draw.await();             //阻塞取款操作
                        } else {
                                cash -= x;                     //取款
                                System.out.println(name + "取款" + x + ",当前余额为" + cash);
                        }
                        _save.signalAll();             //唤醒所有存款操作
                } catch (InterruptedException e) {
                        e.printStackTrace();
                } finally {
                        lock.unlock();                     //释放锁
                }
        }
}

 
 
李四存款3600,当前余额为13600
张三存款2000,当前余额为15600
老张存款600,当前余额为16200
老牛取款1300,当前余额为14900
胖子取款800,当前余额为14100
王五取款2700,当前余额为11400

Process finished with exit code 0

 
假如我们不用锁和条件变量,如何实现此功能呢?下面是实现代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* Java线程:不用条件变量
*
* @author leizhimin 2009-11-5 10:57:29
*/

public class Test {
        public static void main(String[] args) {
                //创建并发访问的账户
                MyCount myCount = new MyCount("95599200901215522", 10000);
                //创建一个线程池
                ExecutorService pool = Executors.newFixedThreadPool(2);
                Thread t1 = new SaveThread("张三", myCount, 2000);
                Thread t2 = new SaveThread("李四", myCount, 3600);
                Thread t3 = new DrawThread("王五", myCount, 2700);
                Thread t4 = new SaveThread("老张", myCount, 600);
                Thread t5 = new DrawThread("老牛", myCount, 1300);
                Thread t6 = new DrawThread("胖子", myCount, 800);
                //执行各个线程
                pool.execute(t1);
                pool.execute(t2);
                pool.execute(t3);
                pool.execute(t4);
                pool.execute(t5);
                pool.execute(t6);
                //关闭线程池
                pool.shutdown();
        }
}

/**
* 存款线程类
*/

class SaveThread extends Thread {
        private String name;                //操作人
        private MyCount myCount;        //账户
        private int x;                            //存款金额

        SaveThread(String name, MyCount myCount, int x) {
                this.name = name;
                this.myCount = myCount;
                this.x = x;
        }

        public void run() {
                myCount.saving(x, name);
        }
}

/**
* 取款线程类
*/

class DrawThread extends Thread {
        private String name;                //操作人
        private MyCount myCount;        //账户
        private int x;                            //存款金额

        DrawThread(String name, MyCount myCount, int x) {
                this.name = name;
                this.myCount = myCount;
                this.x = x;
        }

        public void run() {
                myCount.drawing(x, name);
        }
}

/**
* 普通银行账户,不可透支
*/

class MyCount {
        private String oid;                         //账号
        private int cash;                             //账户余额

        MyCount(String oid, int cash) {
                this.oid = oid;
                this.cash = cash;
        }

        /**
         * 存款
         *
         * @param x        操作金额
         * @param name 操作人
         */

        public synchronized void saving(int x, String name) {
                if (x > 0) {
                        cash += x;                    //存款
                        System.out.println(name + "存款" + x + ",当前余额为" + cash);
                }
                notifyAll();            //唤醒所有等待线程。
        }

        /**
         * 取款
         *
         * @param x        操作金额
         * @param name 操作人
         */

        public synchronized void drawing(int x, String name) {
                if (cash – x < 0) {
                        try {
                                wait();
                        } catch (InterruptedException e1) {
                                e1.printStackTrace();
                        }
                } else {
                        cash -= x;                     //取款
                        System.out.println(name + "取款" + x + ",当前余额为" + cash);
                }
                notifyAll();             //唤醒所有存款操作
        }
}

 
输出结果为:
李四存款3600,当前余额为13600
王五取款2700,当前余额为10900
老张存款600,当前余额为11500
老牛取款1300,当前余额为10200
胖子取款800,当前余额为9400
张三存款2000,当前余额为11400

Process finished with exit code 0

 
结合先前同步代码知识,举一反三,将此例改为同步代码块来实现,代码如下:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* Java线程:改为同步代码块
*
* @author leizhimin 2009-11-5 10:57:29
*/

public class Test {
        public static void main(String[] args) {
                //创建并发访问的账户
                MyCount myCount = new MyCount("95599200901215522", 10000);
                //创建一个线程池
                ExecutorService pool = Executors.newFixedThreadPool(2);
                Thread t1 = new SaveThread("张三", myCount, 2000);
                Thread t2 = new SaveThread("李四", myCount, 3600);
                Thread t3 = new DrawThread("王五", myCount, 2700);
                Thread t4 = new SaveThread("老张", myCount, 600);
                Thread t5 = new DrawThread("老牛", myCount, 1300);
                Thread t6 = new DrawThread("胖子", myCount, 800);
                //执行各个线程
                pool.execute(t1);
                pool.execute(t2);
                pool.execute(t3);
                pool.execute(t4);
                pool.execute(t5);
                pool.execute(t6);
                //关闭线程池
                pool.shutdown();
        }
}

/**
* 存款线程类
*/

class SaveThread extends Thread {
        private String name;                //操作人
        private MyCount myCount;        //账户
        private int x;                            //存款金额

        SaveThread(String name, MyCount myCount, int x) {
                this.name = name;
                this.myCount = myCount;
                this.x = x;
        }

        public void run() {
                myCount.saving(x, name);
        }
}

/**
* 取款线程类
*/

class DrawThread extends Thread {
        private String name;                //操作人
        private MyCount myCount;        //账户
        private int x;                            //存款金额

        DrawThread(String name, MyCount myCount, int x) {
                this.name = name;
                this.myCount = myCount;
                this.x = x;
        }

        public void run() {
                myCount.drawing(x, name);
        }
}

/**
* 普通银行账户,不可透支
*/

class MyCount {
        private String oid;                         //账号
        private int cash;                             //账户余额

        MyCount(String oid, int cash) {
                this.oid = oid;
                this.cash = cash;
        }

        /**
         * 存款
         *
         * @param x        操作金额
         * @param name 操作人
         */

        public void saving(int x, String name) {
                if (x > 0) {
                        synchronized (this) {
                                cash += x;                    //存款
                                System.out.println(name + "存款" + x + ",当前余额为" + cash);
                                notifyAll();            //唤醒所有等待线程。
                        }
                }
        }

        /**
         * 取款
         *
         * @param x        操作金额
         * @param name 操作人
         */

        public synchronized void drawing(int x, String name) {
                synchronized (this) {
                        if (cash – x < 0) {
                                try {
                                        wait();
                                } catch (InterruptedException e1) {
                                        e1.printStackTrace();
                                }
                        } else {
                                cash -= x;                     //取款
                                System.out.println(name + "取款" + x + ",当前余额为" + cash);
                        }
                }
                notifyAll();             //唤醒所有存款操作
        }
}

 
李四存款3600,当前余额为13600
王五取款2700,当前余额为10900
老张存款600,当前余额为11500
老牛取款1300,当前余额为10200
胖子取款800,当前余额为9400
张三存款2000,当前余额为11400

Process finished with exit code 0

 
对比以上三种方式,从控制角度上讲,第一种最灵活,第二种代码最简单,第三种容易犯错。
 

本文出自 “熔 岩” 博客,转载请与作者联系!

Java线程:新特征-阻塞栈

Java线程:新特征-阻塞栈
 
对于阻塞栈,与阻塞队列相似。不同点在于栈是“后入先出”的结构,每次操作的是栈顶,而队列是“先进先出”的结构,每次操作的是队列头。
 
这里要特别说明一点的是,阻塞栈是Java6的新特征。、
 
Java为阻塞栈定义了接口:java.util.concurrent.BlockingDeque,其实现类也比较多,具体可以查看JavaAPI文档。
 
下面看一个简单例子:
 
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;

/**
* Java线程:新特征-阻塞栈
*
* @author leizhimin 2009-11-5 15:34:29
*/

public class Test {
        public static void main(String[] args) throws InterruptedException {
                BlockingDeque bDeque = new LinkedBlockingDeque(20);
                for (int i = 0; i < 30; i++) {
                        //将指定元素添加到此阻塞栈中,如果没有可用空间,将一直等待(如果有必要)。
                        bDeque.putFirst(i);
                        System.out.println("向阻塞栈中添加了元素:" + i);
                }
                System.out.println("程序到此运行结束,即将退出—-");
        }
}

 
输出结果:

向阻塞栈中添加了元素:0
向阻塞栈中添加了元素:1
向阻塞栈中添加了元素:2
向阻塞栈中添加了元素:3
向阻塞栈中添加了元素:4
向阻塞栈中添加了元素:5
向阻塞栈中添加了元素:6
向阻塞栈中添加了元素:7
向阻塞栈中添加了元素:8
向阻塞栈中添加了元素:9
向阻塞栈中添加了元素:10
向阻塞栈中添加了元素:11
向阻塞栈中添加了元素:12
向阻塞栈中添加了元素:13
向阻塞栈中添加了元素:14
向阻塞栈中添加了元素:15
向阻塞栈中添加了元素:16
向阻塞栈中添加了元素:17
向阻塞栈中添加了元素:18
向阻塞栈中添加了元素:19

 
从上面结果可以看到,程序并没结束,二是阻塞住了,原因是栈已经满了,后面追加元素的操作都被阻塞了。
 
 

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/222530

Java线程:新特征-阻塞队列

Java线程:新特征-阻塞队列
 
阻塞队列是Java5线程新特征中的内容,Java定义了阻塞队列的接口 java.util.concurrent.BlockingQueue,阻塞队列的概念是,一个指定长度的队列,如果队列满了,添加新元素的操作会被阻 塞等待,直到有空位为止。同样,当队列为空时候,请求队列元素的操作同样会阻塞等待,直到有可用元素为止。
 
有了这样的功能,就为多线程的排队等候的模型实现开辟了便捷通道,非常有用。
 
java.util.concurrent.BlockingQueue继承了java.util.Queue接口,可以参看API文档。
 
下面给出一个简单应用的例子:
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ArrayBlockingQueue;

/**
* Java线程:新特征-阻塞队列
*
* @author leizhimin 2009-11-5 14:59:15
*/

public class Test {
        public static void main(String[] args) throws InterruptedException {
                BlockingQueue bqueue = new ArrayBlockingQueue(20);
                for (int i = 0; i < 30; i++) {
                        //将指定元素添加到此队列中,如果没有可用空间,将一直等待(如果有必要)。
                        bqueue.put(i);
                        System.out.println("向阻塞队列中添加了元素:" + i);
                }
                System.out.println("程序到此运行结束,即将退出—-");
        }
}

 
输出结果:
向阻塞队列中添加了元素:0
向阻塞队列中添加了元素:1
向阻塞队列中添加了元素:2
向阻塞队列中添加了元素:3
向阻塞队列中添加了元素:4
向阻塞队列中添加了元素:5
向阻塞队列中添加了元素:6
向阻塞队列中添加了元素:7
向阻塞队列中添加了元素:8
向阻塞队列中添加了元素:9
向阻塞队列中添加了元素:10
向阻塞队列中添加了元素:11
向阻塞队列中添加了元素:12
向阻塞队列中添加了元素:13
向阻塞队列中添加了元素:14
向阻塞队列中添加了元素:15
向阻塞队列中添加了元素:16
向阻塞队列中添加了元素:17
向阻塞队列中添加了元素:18
向阻塞队列中添加了元素:19

 
可以看出,输出到元素19时候,就一直处于等待状态,因为队列满了,程序阻塞了。
 
这里没有用多线程来演示,没有这个必要。
 
另外,阻塞队列还有更多实现类,用来满足各种复杂的需求:ArrayBlockingQueue, DelayQueue, LinkedBlockingQueue, PriorityBlockingQueue, SynchronousQueue ,具体的API差别也很小。
 

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/222524

Java线程:新特征-信号量

Java线程:新特征-信号量
 
Java的信号量实际上是一个功能完毕的计数器,对控制一定资源的消费与回收有着很重要的意义,信号量常常用于多线程的代码中,并能监控有多少 数目的线程等待获取资源,并且通过信号量可以得知可用资源的数目等等,这里总是在强调“数目”二字,但不能指出来有哪些在等待,哪些资源可用。
 
因此,本人认为,这个信号量类如果能返回数目,还能知道哪些对象在等待,哪些资源可使用,就非常完美了,仅仅拿到这些概括性的数字,对精确控制意义不是很大。目前还没想到更好的用法。
 
下面是一个简单例子:
 
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

/**
* Java线程:新特征-信号量
*
* @author leizhimin 2009-11-5 13:44:45
*/

public class Test {
        public static void main(String[] args) {
                MyPool myPool = new MyPool(20);
                //创建线程池
                ExecutorService threadPool = Executors.newFixedThreadPool(2);
                MyThread t1 = new MyThread("任务A", myPool, 3);
                MyThread t2 = new MyThread("任务B", myPool, 12);
                MyThread t3 = new MyThread("任务C", myPool, 7);
                //在线程池中执行任务
                threadPool.execute(t1);
                threadPool.execute(t2);
                threadPool.execute(t3);
                //关闭池
                threadPool.shutdown();
        }
}

/**
* 一个池
*/

class MyPool {
        private Semaphore sp;     //池相关的信号量

        /**
         * 池的大小,这个大小会传递给信号量
         *
         * @param size 池的大小
         */

        MyPool(int size) {
                this.sp = new Semaphore(size);
        }

        public Semaphore getSp() {
                return sp;
        }

        public void setSp(Semaphore sp) {
                this.sp = sp;
        }
}

class MyThread extends Thread {
        private String threadname;            //线程的名称
        private MyPool pool;                        //自定义池
        private int x;                                    //申请信号量的大小

        MyThread(String threadname, MyPool pool, int x) {
                this.threadname = threadname;
                this.pool = pool;
                this.x = x;
        }

        public void run() {
                try {
                        //从此信号量获取给定数目的许可
                        pool.getSp().acquire(x);
                        //todo:也许这里可以做更复杂的业务
                        System.out.println(threadname + "成功获取了" + x + "个许可!");
                } catch (InterruptedException e) {
                        e.printStackTrace();
                } finally {
                        //释放给定数目的许可,将其返回到信号量。
                        pool.getSp().release(x);
                        System.out.println(threadname + "释放了" + x + "个许可!");
                }
        }
}

 
任务B成功获取了12个许可!
任务B释放了12个许可!
任务A成功获取了3个许可!
任务C成功获取了7个许可!
任务C释放了7个许可!
任务A释放了3个许可!

Process finished with exit code 0

 
从结果可以看出,信号量仅仅是对池资源进行监控,但不保证线程的安全,因此,在使用时候,应该自己控制线程的安全访问池资源。
 

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/222469

Java线程:新特征-锁(下)

Java线程:新特征-锁(下)
 
在上文中提到了Lock接口以及对象,使用它,很优雅的控制了竞争资源的安全访问,但是这种锁不区分读写,称这种锁为普通锁。为了提高性能,Java提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,在一定程度上提高了程序的执行效率。
 
Java中读写锁有个接口java.util.concurrent.locks.ReadWriteLock,也有具体的实现ReentrantReadWriteLock,详细的API可以查看JavaAPI文档。
 
下面这个例子是在文例子的基础上,将普通锁改为读写锁,并添加账户余额查询的功能,代码如下:
 
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
* Java线程:锁
*
* @author leizhimin 2009-11-5 10:57:29
*/

public class Test {
        public static void main(String[] args) {
                //创建并发访问的账户
                MyCount myCount = new MyCount("95599200901215522", 10000);
                //创建一个锁对象
                ReadWriteLock lock = new ReentrantReadWriteLock(false);
                //创建一个线程池
                ExecutorService pool = Executors.newFixedThreadPool(2);
                //创建一些并发访问用户,一个信用卡,存的存,取的取,好热闹啊
                User u1 = new User("张三", myCount, -4000, lock, false);
                User u2 = new User("张三他爹", myCount, 6000, lock, false);
                User u3 = new User("张三他弟", myCount, -8000, lock, false);
                User u4 = new User("张三", myCount, 800, lock, false);
                User u5 = new User("张三他爹", myCount, 0, lock, true);
                //在线程池中执行各个用户的操作
                pool.execute(u1);
                pool.execute(u2);
                pool.execute(u3);
                pool.execute(u4);
                pool.execute(u5);
                //关闭线程池
                pool.shutdown();
        }
}

/**
* 信用卡的用户
*/

class User implements Runnable {
        private String name;                //用户名
        private MyCount myCount;        //所要操作的账户
        private int iocash;                 //操作的金额,当然有正负之分了
        private ReadWriteLock myLock;                //执行操作所需的锁对象
        private boolean ischeck;        //是否查询

        User(String name, MyCount myCount, int iocash, ReadWriteLock myLock, boolean ischeck) {
                this.name = name;
                this.myCount = myCount;
                this.iocash = iocash;
                this.myLock = myLock;
                this.ischeck = ischeck;
        }

        public void run() {
                if (ischeck) {
                        //获取读锁
                        myLock.readLock().lock();
                        System.out.println("读:" + name + "正在查询" + myCount + "账户,当前金额为" + myCount.getCash());
                        //释放读锁
                        myLock.readLock().unlock();
                } else {
                        //获取写锁
                        myLock.writeLock().lock();
                        //执行现金业务
                        System.out.println("写:" + name + "正在操作" + myCount + "账户,金额为" + iocash + ",当前金额为" + myCount.getCash());
                        myCount.setCash(myCount.getCash() + iocash);
                        System.out.println("写:" + name + "操作" + myCount + "账户成功,金额为" + iocash + ",当前金额为" + myCount.getCash());
                        //释放写锁
                        myLock.writeLock().unlock();
                }
        }
}

/**
* 信用卡账户,可随意透支
*/

class MyCount {
        private String oid;         //账号
        private int cash;             //账户余额

        MyCount(String oid, int cash) {
                this.oid = oid;
                this.cash = cash;
        }

        public String getOid() {
                return oid;
        }

        public void setOid(String oid) {
                this.oid = oid;
        }

        public int getCash() {
                return cash;
        }

        public void setCash(int cash) {
                this.cash = cash;
        }

        @Override
        public String toString() {
                return "MyCount{" +
                                "oid='" + oid + '\'' +
                                ", cash=" + cash +
                                '}';
        }
}

 
写:张三正在操作MyCount{oid='95599200901215522', cash=10000}账户,金额为-4000,当前金额为10000
写:张三操作MyCount{oid='95599200901215522', cash=6000}账户成功,金额为-4000,当前金额为6000
写:张三他弟正在操作MyCount{oid='95599200901215522', cash=6000}账户,金额为-8000,当前金额为6000
写:张三他弟操作MyCount{oid='95599200901215522', cash=-2000}账户成功,金额为-8000,当前金额为-2000
写:张三正在操作MyCount{oid='95599200901215522', cash=-2000}账户,金额为800,当前金额为-2000
写:张三操作MyCount{oid='95599200901215522', cash=-1200}账户成功,金额为800,当前金额为-1200
读:张三他爹正在查询MyCount{oid='95599200901215522', cash=-1200}账户,当前金额为-1200
写:张三他爹正在操作MyCount{oid='95599200901215522', cash=-1200}账户,金额为6000,当前金额为-1200
写:张三他爹操作MyCount{oid='95599200901215522', cash=4800}账户成功,金额为6000,当前金额为4800

Process finished with exit code 0

 
在实际开发中,最好在能用读写锁的情况下使用读写锁,而不要用普通锁,以求更好的性能。
 

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/222433

Java线程:新特征-锁(上)

Java线程:新特征-锁(上)
 
在Java5中,专门提供了锁对象,利用锁可以方便的实现资源的封锁,用来控制对竞争资源并发访问的控制,这些内容主要集中在 java.util.concurrent.locks 包下面,里面有三个重要的接口Condition、Lock、ReadWriteLock。
 
Condition ConditionObject 监视器方法(waitnotifynotifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set (wait-set)。
Lock Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
ReadWriteLock ReadWriteLock 维护了一对相关的锁定,一个用于只读操作,另一个用于写入操作。

 
有关锁的介绍,API文档解说很多,看得很烦,还是看个例子再看文档比较容易理解。
 
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* Java线程:锁
*
* @author leizhimin 2009-11-5 10:57:29
*/

public class Test {
        public static void main(String[] args) {
                //创建并发访问的账户
                MyCount myCount = new MyCount("95599200901215522", 10000);
                //创建一个锁对象
                Lock lock = new ReentrantLock();
                //创建一个线程池
                ExecutorService pool = Executors.newCachedThreadPool();
                //创建一些并发访问用户,一个信用卡,存的存,取的取,好热闹啊
                User u1 = new User("张三", myCount, -4000, lock);
                User u2 = new User("张三他爹", myCount, 6000, lock);
                User u3 = new User("张三他弟", myCount, -8000, lock);
                User u4 = new User("张三", myCount, 800, lock);
                //在线程池中执行各个用户的操作
                pool.execute(u1);
                pool.execute(u2);
                pool.execute(u3);
                pool.execute(u4);
                //关闭线程池
                pool.shutdown();
        }
}

/**
* 信用卡的用户
*/

class User implements Runnable {
        private String name;                //用户名
        private MyCount myCount;        //所要操作的账户
        private int iocash;                 //操作的金额,当然有正负之分了
        private Lock myLock;                //执行操作所需的锁对象

        User(String name, MyCount myCount, int iocash, Lock myLock) {
                this.name = name;
                this.myCount = myCount;
                this.iocash = iocash;
                this.myLock = myLock;
        }

        public void run() {
                //获取锁
                myLock.lock();
                //执行现金业务
                System.out.println(name + "正在操作" + myCount + "账户,金额为" + iocash + ",当前金额为" + myCount.getCash());
                myCount.setCash(myCount.getCash() + iocash);
                System.out.println(name + "操作" + myCount + "账户成功,金额为" + iocash + ",当前金额为" + myCount.getCash());
                //释放锁,否则别的线程没有机会执行了
                myLock.unlock();
        }
}

/**
* 信用卡账户,可随意透支
*/

class MyCount {
        private String oid;         //账号
        private int cash;             //账户余额

        MyCount(String oid, int cash) {
                this.oid = oid;
                this.cash = cash;
        }

        public String getOid() {
                return oid;
        }

        public void setOid(String oid) {
                this.oid = oid;
        }

        public int getCash() {
                return cash;
        }

        public void setCash(int cash) {
                this.cash = cash;
        }

        @Override
        public String toString() {
                return "MyCount{" +
                                "oid='" + oid + '\'' +
                                ", cash=" + cash +
                                '}';
        }
}

 
张三正在操作MyCount{oid='95599200901215522', cash=10000}账户,金额为-4000,当前金额为10000
张三操作MyCount{oid='95599200901215522', cash=6000}账户成功,金额为-4000,当前金额为6000
张三他爹正在操作MyCount{oid='95599200901215522', cash=6000}账户,金额为6000,当前金额为6000
张三他爹操作MyCount{oid='95599200901215522', cash=12000}账户成功,金额为6000,当前金额为12000
张三他弟正在操作MyCount{oid='95599200901215522', cash=12000}账户,金额为-8000,当前金额为12000
张三他弟操作MyCount{oid='95599200901215522', cash=4000}账户成功,金额为-8000,当前金额为4000
张三正在操作MyCount{oid='95599200901215522', cash=4000}账户,金额为800,当前金额为4000
张三操作MyCount{oid='95599200901215522', cash=4800}账户成功,金额为800,当前金额为4800

Process finished with exit code 0

 
从上面的输出可以看到,利用锁对象太方便了,比直接在某个不知情的对象上用锁清晰多了。
 
但一定要注意的是,在获取了锁对象后,用完后应该尽快释放锁,以便别的等待该锁的线程有机会去执行。

本文出自 “熔 岩” 博客,请务必保留此出处http://lavasoft.blog.51cto.com/62575/222084