当前位置: 首页 > news >正文

南京高端网站建设工作室免费dede企业网站模板

南京高端网站建设工作室,免费dede企业网站模板,凡客诚品上一年度市场份额,付费下载网站源码本文转载自http://www.cnblogs.com/riskyer/p/3263032.html Java线程#xff1a;概念与原理 一、操作系统中线程和进程的概念 现在的操作系统是多任务操作系统。多线程是实现多任务的一种方式。 进程是指一个内存中运行的应用程序#xff0c;每个进程都有自己独立的一块内存空…本文转载自http://www.cnblogs.com/riskyer/p/3263032.html Java线程概念与原理 一、操作系统中线程和进程的概念 现在的操作系统是多任务操作系统。多线程是实现多任务的一种方式。 进程是指一个内存中运行的应用程序每个进程都有自己独立的一块内存空间一个进程中可以启动多个线程。比如在Windows系统中一个运行的exe就是一个进程。 线程是指进程中的一个执行流程一个进程中可以运行多个线程。比如java.exe进程中可以运行很多线程。线程总是属于某个进程进程中的多个线程共享进程的内存。 “同时”执行是人的感觉在线程之间实际上轮换执行。 二、Java中的线程 在Java中“线程”指两件不同的事情 1、java.lang.Thread类的一个实例 2、线程的执行。 使用java.lang.Thread类或者java.lang.Runnable接口编写代码来定义、实例化和启动新线程。 一个Thread类实例只是一个对象像Java中的任何其他对象一样具有变量和方法生死于堆上。 Java中每个线程都有一个调用栈即使不在程序中创建任何新的线程线程也在后台运行着。 一个Java应用总是从main()方法开始运行mian()方法运行在一个线程内它被称为主线程。 一旦创建一个新的线程就产生一个新的调用栈。 线程总体分两类用户线程和守候线程。当所有用户线程执行完毕的时候JVM自动关闭。但是守候线程却不独立于JVM守候线程一般是由操作系统或者用户自己创建的。 Java线程创建与启动 一、定义线程 1、扩展java.lang.Thread类。 此类中有个run()方法应该注意其用法public void run() 如果该线程是使用独立的Runnable运行对象构造的则调用该Runnable对象的run方法否则该方法不执行任何操作并返回。 Thread的子类应该重写该方法。 2、实现java.lang.Runnable接口。void run() 使用实现接口Runnable的对象创建一个线程时启动该线程将导致在独立执行的线程中调用对象的run方法。方法run的常规协定是它可能执行任何所需的操作。 二、实例化线程 1、如果是扩展java.lang.Thread类的线程则直接new即可。 2、如果是实现了java.lang.Runnable接口的类则用Thread的构造方法 Thread(Runnable target)  Thread(Runnable target, String name)  Thread(ThreadGroup group, Runnable target)  Thread(ThreadGroup group, Runnable target, String name)  Thread(ThreadGroup group, Runnable target, String name, long stackSize)三、启动线程 在线程的Thread对象上调用start()方法而不是run()或者别的方法。在调用start()方法之前线程处于新状态中新状态指有一个Thread对象但还没有一个真正的线程。 在调用start()方法之后发生了一系列复杂的事情 启动新的执行线程具有新的调用栈该线程从新状态转移到可运行状态当该线程获得机会执行时其目标run()方法将运行。注意对Java来说run()方法没有任何特别之处。像main()方法一样它只是新线程知道调用的方法名称(和签名)。因此在Runnable上或者Thread上调用run方法是合法的。但并不启动新的线程。 四、例子 1、实现Runnable接口的多线程例子 1 /** 2 * 实现Runnable接口的类3 * 4 * author leizhimin 2008-9-13 18:12:10 5 */ 6 publi cclass DoSomething implements Runnable {7 private String name;8 9 public DoSomething(String name) { 10 this.name name; 11 } 12 13 public void run() { 14 for (int i 0; i 5; i) { 15 for (long k 0; k 100000000; k) ; 16 System.out.println(name : i); 17 } 18 } 19 } 1 /** 2 * 测试Runnable类实现的多线程程序3 * 4 * author leizhimin 2008-9-13 18:15:02 5 */ 6 public class TestRunnable {7 public static void main(String[] args) {8 DoSomething ds1 new DoSomething(阿三);9 DoSomething ds2 new DoSomething(李四); 10 11 Thread t1 new Thread(ds1); 12 Thread t2 new Thread(ds2); 13 14 t1.start(); 15 t2.start(); 16 } 17 } 执行结果 李四: 0 阿三: 0  李四: 1  阿三: 1  李四: 2  李四: 3  阿三: 2  李四: 4  阿三: 3  阿三: 4  Process finished with exit code 0 2、扩展Thread类实现的多线程例子 1 /** 2 * 测试扩展Thread类实现的多线程程序3 * 4 * author leizhimin 2008-9-13 18:22:13 5 */ 6 public class TestThreadextends Thread{ 7 public TestThread(String name) {8 super(name);9 } 10 11 public void run() { 12 for(int i 0;i5;i){ 13 for(long k 0; k 100000000;k); 14 System.out.println(this.getName() :i); 15 } 16 } 17 18 public static void main(String[] args) { 19 Thread t1 new TestThread(阿三); 20 Thread t2 new TestThread(李四); 21 t1.start(); 22 t2.start(); 23 } 24 }  执行结果 阿三 :0 李四 :0  阿三 :1  李四 :1  阿三 :2  李四 :2  阿三 :3  阿三 :4  李四 :3  李四 :4  Process finished with exit code 0 对于上面的多线程程序代码来说输出的结果是不确定的。其中的一条语句for(long k 0; k 100000000;k);是用来模拟一个非常耗时的操作的。 五、一些常见问题 1、线程的名字一个运行中的线程总是有名字的名字有两个来源一个是虚拟机自己给的名字一个是你自己的定的名字。在没有指定线程名字的情况下虚拟机总会为线程指定名字并且主线程的名字总是mian非主线程的名字不确定。 2、线程都可以设置名字也可以获取线程的名字连主线程也不例外。 3、获取当前线程的对象的方法是Thread.currentThread() 4、在上面的代码中只能保证每个线程都将启动每个线程都将运行直到完成。一系列线程以某种顺序启动并不意味着将按该顺序执行。对于任何一组启动的线程来说调度程序不能保证其执行次序持续时间也无法保证。 5、当线程目标run()方法结束时该线程完成。 6、一旦线程启动它就永远不能再重新启动。只有一个新的线程可以被启动并且只能一次。一个可运行的线程或死线程可以被重新启动。 7、线程的调度是JVM的一部分在一个CPU的机器上上实际上一次只能运行一个线程。一次只有一个线程栈执行。JVM线程调度程序决定实际运行哪个处于可运行状态的线程。 众多可运行线程中的某一个会被选中做为当前线程。可运行线程被选择运行的顺序是没有保障的。 8、尽管通常采用队列形式但这是没有保障的。队列形式是指当一个线程完成“一轮”时它移到可运行队列的尾部等待直到它最终排队到该队列的前端为止它才能被再次选中。事实上我们把它称为可运行池而不是一个可运行队列目的是帮助认识线程并不都是以某种有保障的顺序排列唱呢个一个队列的事实。 9、尽管我们没有无法控制线程调度程序但可以通过别的方式来影响线程调度的方式。 Java线程线程栈模型与线程的变量 要理解线程调度的原理以及线程执行过程必须理解线程栈模型。 线程栈是指某时刻时内存中线程调度的栈信息当前调用的方法总是位于栈顶。线程栈的内容是随着程序的运行动态变化的因此研究线程栈必须选择一个运行的时刻实际上指代码运行到什么地方)。 下面通过一个示例性的代码说明线程调用栈的变化过程。 这幅图描述在代码执行到两个不同时刻1、2时候虚拟机线程调用栈示意图。 当程序执行到t.start();时候程序多出一个分支增加了一个调用栈B这样栈A、栈B并行执行。 从这里就可以看出方法调用和线程启动的区别了。 Java线程线程状态的转换 一、线程状态 线程的状态转换是线程控制的基础。线程状态总的可分为五大状态分别是生、死、可运行、运行、等待/阻塞。用一个图来描述如下 1、新状态线程对象已经创建还没有在其上调用start()方法。 2、可运行状态当线程有资格运行但调度程序还没有把它选定为运行线程时线程所处的状态。当start()方法调用时线程首先进入可运行状态。在线程运行之后或者从阻塞、等待或睡眠状态回来后也返回到可运行状态。 3、运行状态线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。 4、等待/阻塞/睡眠状态这是线程有资格运行时它所处的状态。实际上这个三状态组合为一种其共同点是线程仍旧是活的但是当前没有条件运行。换句话说它是可运行的但是如果某件事件出现他可能返回到可运行状态。 5、死亡态当线程的run()方法完成时就认为它死去。这个线程对象也许是活的但是它已经不是一个单独执行的线程。线程一旦死亡就不能复生。如果在一个死去的线程上调用start()方法会抛出java.lang.IllegalThreadStateException异常。 有关详细状态转换图可以参看本人的“Java多线程编程总结”中的图 二、阻止线程执行 对于线程的阻止考虑一下三个方面不考虑IO阻塞的情况 睡眠等待因为需要一个对象的锁定而被阻塞。 1、睡眠 Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)静态方法强制当前正在执行的线程休眠暂停执行以“减慢线程”。当线程睡眠时它入睡在某个地方在苏醒之前不会返回到可运行状态。当睡眠时间到期则返回到可运行状态。 线程睡眠的原因线程执行太快或者需要强制进入下一轮因为Java规范不保证合理的轮换。 睡眠的实现调用静态方法。 try {Thread.sleep(123); } catch (InterruptedException e) {e.printStackTrace(); } 睡眠的位置为了让其他线程有机会执行可以将Thread.sleep()的调用放线程run()之内。这样才能保证该线程执行过程中会睡眠。 例如在前面的例子中将一个耗时的操作改为睡眠以减慢线程的执行。可以这么写 public void run() { for(int i 0;i5;i){// 很耗时的操作用来减慢线程的执行// for(long k 0; k 100000000;k);try {Thread.sleep(3);} catch (InterruptedException e) {e.printStackTrace(); .}System.out.println(this.getName() :i);} } 运行结果 阿三 :0 李四 :0  阿三 :1  阿三 :2  阿三 :3  李四 :1  李四 :2  阿三 :4  李四 :3  李四 :4  Process finished with exit code 0 这样线程在每次执行过程中总会睡眠3毫秒睡眠了其他的线程就有机会执行了。 注意 线程睡眠是帮助所有线程获得运行机会的最好方法。线程睡眠到期自动苏醒并返回到可运行状态不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。因此sleep()方法不能保证该线程睡眠到期后就开始执行。sleep()是静态方法只能控制当前正在运行的线程。下面给个例子 /** * 一个计数器计数到100在每个数字之间暂停1秒每隔10个数字输出一个字符串 */ public class MyThreadextends Thread {public void run() {for (int i 0; i 100; i) {if ((i) % 10 0) {System.out.println(------- i);} System.out.print(i); try {Thread.sleep(1); System.out.print( 线程睡眠1毫秒\n);} catch (InterruptedException e) {e.printStackTrace(); } } } public static void main(String[] args) {new MyThread().start();} } -------0  0    线程睡眠1毫秒 1    线程睡眠1毫秒 2    线程睡眠1毫秒 3    线程睡眠1毫秒 4    线程睡眠1毫秒 5    线程睡眠1毫秒 6    线程睡眠1毫秒 7    线程睡眠1毫秒 8    线程睡眠1毫秒 9    线程睡眠1毫秒 -------10  10    线程睡眠1毫秒 11    线程睡眠1毫秒 12    线程睡眠1毫秒 13    线程睡眠1毫秒 14    线程睡眠1毫秒 15    线程睡眠1毫秒 16    线程睡眠1毫秒 17    线程睡眠1毫秒 18    线程睡眠1毫秒 19    线程睡眠1毫秒 -------20  20    线程睡眠1毫秒 21    线程睡眠1毫秒 22    线程睡眠1毫秒 23    线程睡眠1毫秒 24    线程睡眠1毫秒 25    线程睡眠1毫秒 26    线程睡眠1毫秒 27    线程睡眠1毫秒 28    线程睡眠1毫秒 29    线程睡眠1毫秒 -------30  30    线程睡眠1毫秒 31    线程睡眠1毫秒 32    线程睡眠1毫秒 33    线程睡眠1毫秒 34    线程睡眠1毫秒 35    线程睡眠1毫秒 36    线程睡眠1毫秒 37    线程睡眠1毫秒 38    线程睡眠1毫秒 39    线程睡眠1毫秒 -------40  40    线程睡眠1毫秒 41    线程睡眠1毫秒 42    线程睡眠1毫秒 43    线程睡眠1毫秒 44    线程睡眠1毫秒 45    线程睡眠1毫秒 46    线程睡眠1毫秒 47    线程睡眠1毫秒 48    线程睡眠1毫秒 49    线程睡眠1毫秒 -------50  50    线程睡眠1毫秒 51    线程睡眠1毫秒 52    线程睡眠1毫秒 53    线程睡眠1毫秒 54    线程睡眠1毫秒 55    线程睡眠1毫秒 56    线程睡眠1毫秒 57    线程睡眠1毫秒 58    线程睡眠1毫秒 59    线程睡眠1毫秒 -------60  60    线程睡眠1毫秒 61    线程睡眠1毫秒 62    线程睡眠1毫秒 63    线程睡眠1毫秒 64    线程睡眠1毫秒 65    线程睡眠1毫秒 66    线程睡眠1毫秒 67    线程睡眠1毫秒 68    线程睡眠1毫秒 69    线程睡眠1毫秒 -------70  70    线程睡眠1毫秒 71    线程睡眠1毫秒 72    线程睡眠1毫秒 73    线程睡眠1毫秒 74    线程睡眠1毫秒 75    线程睡眠1毫秒 76    线程睡眠1毫秒 77    线程睡眠1毫秒 78    线程睡眠1毫秒 79    线程睡眠1毫秒 -------80  80    线程睡眠1毫秒 81    线程睡眠1毫秒 82    线程睡眠1毫秒 83    线程睡眠1毫秒 84    线程睡眠1毫秒 85    线程睡眠1毫秒 86    线程睡眠1毫秒 87    线程睡眠1毫秒 88    线程睡眠1毫秒 89    线程睡眠1毫秒 -------90  90    线程睡眠1毫秒 91    线程睡眠1毫秒 92    线程睡眠1毫秒 93    线程睡眠1毫秒 94    线程睡眠1毫秒 95    线程睡眠1毫秒 96    线程睡眠1毫秒 97    线程睡眠1毫秒 98    线程睡眠1毫秒 99    线程睡眠1毫秒 Process finished with exit code 0 2、线程的优先级和线程让步yield() 线程的让步是通过Thread.yield()来实现的。yield()方法的作用是暂停当前正在执行的线程对象并执行其他线程。 要理解yield()必须了解线程的优先级的概念。线程总是存在优先级优先级范围在1~10之间。JVM线程调度程序是基于优先级的抢先调度机制。在大多数情况下当前运行的线程优先级将大于或等于线程池中任何线程的优先级。但这仅仅是大多数情况。 注意当设计多线程应用程序的时候一定不要依赖于线程的优先级。因为线程调度优先级操作是没有保障的只能把线程优先级作用作为一种提高程序效率的方法但是要保证程序不依赖这种操作。 当线程池中线程都具有相同的优先级调度程序的JVM实现自由选择它喜欢的线程。这时候调度程序的操作有两种可能一是选择一个线程运行直到它阻塞或者运行完成为止。二是时间分片为池内的每个线程提供均等的运行机会。 设置线程的优先级线程默认的优先级是创建它的执行线程的优先级。可以通过setPriority(int newPriority)更改线程的优先级。例如         Thread t new MyThread();         t.setPriority(8);         t.start(); 线程优先级为1~10之间的正整数JVM从不会改变一个线程的优先级。然而1~10之间的值是没有保证的。一些JVM可能不能识别10个不同的值而将这些优先级进行每两个或多个合并变成少于10个的优先级则两个或多个优先级的线程可能被映射为一个优先级。 线程默认优先级是5Thread类中有三个常量定义线程优先级范围 static int MAX_PRIORITY            线程可以具有的最高优先级。 static int MIN_PRIORITY            线程可以具有的最低优先级。 static int NORM_PRIORITY            分配给线程的默认优先级。 3、Thread.yield()方法 Thread.yield()方法作用是暂停当前正在执行的线程对象并执行其他线程。 yield()应该做的是让当前运行线程回到可运行状态以允许具有相同优先级的其他线程获得运行机会。因此使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是实际中无法保证yield()达到让步目的因为让步的线程还有可能被线程调度程序再次选中。 结论yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下yield()将导致线程从运行状态转到可运行状态但有可能没有效果。 4、join()方法 Thread的非静态方法join()让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前B不能工作。例如         Thread t new MyThread();         t.start();         t.join(); 另外join()方法还有带超时限制的重载版本。例如t.join(5000);则让线程等待5000毫秒如果超过这个时间则停止等待变为可运行状态。 线程的加入join()对线程栈导致的结果是线程栈发生了变化当然这些变化都是瞬时的。下面给示意图       小结 到目前位置介绍了线程离开运行状态的3种方法 1、调用Thread.sleep()使当前线程睡眠至少多少毫秒尽管它可能在指定的时间之前被中断。 2、调用Thread.yield()不能保障太多事情尽管通常它会让当前运行线程回到可运行性状态使得有相同优先级的线程有机会执行。 3、调用join()方法保证当前线程停止执行直到该线程所加入的线程完成为止。然而如果它加入的线程没有存活则当前线程不需要停止。   除了以上三种方式外还有下面几种特殊情况可能使线程离开运行状态 1、线程的run()方法完成。 2、在对象上调用wait()方法不是在线程上调用。 3、线程不能在对象上获得锁定它正试图运行该对象的方法代码。 4、线程调度程序可以决定将当前运行状态移动到可运行状态以便让另一个线程获得运行机会而不需要任何理由。 Java线程线程的同步与锁 一、同步问题提出 线程的同步是为了防止多个线程访问一个数据对象时对数据造成的破坏。 例如两个线程ThreadA、ThreadB都操作同一个对象Foo对象并修改Foo对象上的数据。 public class Foo {     private int x 100;     public int getX() {         return x;     }      publicint fix(int y) {         x x - y;          return x;     }  }   publicclass MyRunnableimplements Runnable {     private Foo foo new Foo();      publicstaticvoid main(String[] args) {         MyRunnable r  new MyRunnable();         Thread ta  new Thread(r,Thread-A);          Thread tb  new Thread(r,Thread-B);          ta.start();          tb.start();      }      publicvoid run() {         for (int i 0; i 3; i) {             this.fix(30);             try {                 Thread.sleep(1);              } catch (InterruptedException e) {                 e.printStackTrace();              }              System.out.println(Thread.currentThread().getName()   :当前foo对象的x值   foo.getX());         }      }      publicint fix(int y) {         return foo.fix(y);     }  }   运行结果 Thread-A : 当前foo对象的x值 40 Thread-B : 当前foo对象的x值 40 Thread-B : 当前foo对象的x值 -20 Thread-A : 当前foo对象的x值 -50 Thread-A : 当前foo对象的x值 -80 Thread-B : 当前foo对象的x值 -80 Process finished with exit code 0   从结果发现这样的输出值明显是不合理的。原因是两个线程不加控制的访问Foo对象并修改其数据所致。 如果要保持结果的合理性只需要达到一个目的就是将对Foo的访问加以限制每次只能有一个线程在访问。这样就能保证Foo对象中数据的合理性了。 在具体的Java代码中需要完成一下两个操作 把竞争访问的资源类Foo变量x标识为private 同步哪些修改变量的代码使用synchronized关键字同步方法或代码。 二、同步和锁定 1、锁的原理 Java中每个对象都有一个内置锁 当程序运行到非静态的synchronized同步方法上时自动获得与正在执行代码类的当前实例this实例有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。 当程序运行到synchronized同步方法或代码块时才该对象锁才起作用。 一个对象只有一个锁。所以如果一个线程获得该锁就没有其他线程可以获得锁直到第一个线程释放或返回锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块直到该锁被释放。 释放锁是指持锁线程退出了synchronized同步方法或代码块。 关于锁和同步有一下几个要点 1、只能同步方法而不能同步变量和类 2、每个对象只有一个锁当提到同步时应该清楚在什么上同步也就是说在哪个对象上同步 3、不必同步类中所有的方法类可以同时拥有同步和非同步方法。 4、如果两个线程要执行一个类中的synchronized方法并且两个线程使用相同的实例来调用方法那么一次只能有一个线程能够执行方法另一个需要等待直到锁被释放。也就是说如果一个线程在对象上获得一个锁就没有任何其他线程可以进入该对象的类中的任何一个同步方法。 5、如果线程拥有同步和非同步方法则非同步方法可以被多个线程自由访问而不受锁的限制。 6、线程睡眠时它所持的任何锁都不会释放。 7、线程可以获得多个锁。比如在一个对象的同步方法里面调用另外一个对象的同步方法则获取了两个对象的同步锁。 8、同步损害并发性应该尽可能缩小同步范围。同步不但可以同步整个方法还可以同步方法中一部分代码块。 9、在使用同步代码块时候应该指定在哪个对象上同步也就是说要获取哪个对象的锁。例如     public int fix(int y) {         synchronized (this) {             x x - y;         }         return x;     } 当然同步方法也可以改写为非同步方法但功能完全一样的例如     public synchronized int getX() {         return x;     } 与     public int getX() {         synchronized (this) {             return x;         }     } 效果是完全一样的。 三、静态方法同步 要同步静态方法需要一个用于整个类对象的锁这个对象是就是这个类XXX.class)。 例如 public static synchronized int setName(String name){       Xxx.name name; } 等价于 public static int setName(String name){       synchronized(Xxx.class){             Xxx.name name;       } } 四、如果线程不能不能获得锁会怎么样 如果线程试图进入同步方法而其锁已经被占用则线程在该对象上被阻塞。实质上线程进入该对象的的一种池中必须在哪里等待直到其锁被释放该线程再次变为可运行或运行为止。 当考虑阻塞时一定要注意哪个对象正被用于锁定 1、调用同一个对象中非静态同步方法的线程将彼此阻塞。如果是不同对象则每个线程有自己的对象的锁线程间彼此互不干预。 2、调用同一个类中的静态同步方法的线程将彼此阻塞它们都是锁定在相同的Class对象上。 3、静态同步方法和非静态同步方法将永远不会彼此阻塞因为静态方法锁定在Class对象上非静态方法锁定在该类的对象上。 4、对于同步代码块要看清楚什么对象已经用于锁定synchronized后面括号的内容。在同一个对象上进行同步的线程将彼此阻塞在不同对象上锁定的线程将永远不会彼此阻塞。 五、何时需要同步 在多个线程同时访问互斥可交换数据时应该同步以保护数据确保两个线程不会同时修改更改它。 对于非静态字段中可更改的数据通常使用非静态方法访问。 对于静态字段中可更改的数据通常使用静态方法访问。 如果需要在非静态方法中使用静态字段或者在静态字段中调用非静态方法问题将变得非常复杂。已经超出SJCP考试范围了。 六、线程安全类 当一个类已经很好的同步以保护它的数据时这个类就称为“线程安全的”。 即使是线程安全类也应该特别小心因为操作的线程是间仍然不一定安全。 举个形象的例子比如一个集合是线程安全的有两个线程在操作同一个集合对象当第一个线程查询集合非空后删除集合中所有元素的时候。第二个线程也来执行与第一个线程相同的操作也许在第一个线程查询后第二个线程也查询出集合非空但是当第一个执行清除后第二个再执行删除显然是不对的因为此时集合已经为空了。 看个代码 publicclass NameList {     private List nameList Collections.synchronizedList(new LinkedList());     publicvoid add(String name) {         nameList.add(name);      }      public String removeFirst() {         if (nameList.size() 0) {             return (String) nameList.remove(0);         } else {             returnnull;         }      }  }   publicclass Test {     publicstaticvoid main(String[] args) {         final NameList nl new NameList();          nl.add(aaa);         class NameDropperextends Thread{              publicvoid run(){                 String name nl.removeFirst();                  System.out.println(name);              }          }          Thread t1  new NameDropper();         Thread t2  new NameDropper();         t1.start();          t2.start();      }  } 虽然集合对象     private List nameList Collections.synchronizedList(new LinkedList()); 是同步的但是程序还不是线程安全的。 出现这种事件的原因是上例中一个线程操作列表过程中无法阻止另外一个线程对列表的其他操作。 解决上面问题的办法是在操作集合对象的NameList上面做一个同步。改写后的代码如下 publicclass NameList {     private List nameList Collections.synchronizedList(new LinkedList());     publicsynchronizedvoid add(String name) {         nameList.add(name);      }      publicsynchronized String removeFirst() {         if (nameList.size() 0) {             return (String) nameList.remove(0);         } else {             returnnull;         }      }  } 这样当一个线程访问其中一个同步方法时其他线程只有等待。 七、线程死锁 死锁对Java程序来说是很复杂的也很难发现问题。当两个线程被阻塞每个线程在等待另一个线程时就发生死锁。 还是看一个比较直观的死锁例子 publicclass DeadlockRisk {     privatestaticclass Resource {         publicint value;     }      private Resource resourceA new Resource();      private Resource resourceB new Resource();      publicint read() {         synchronized (resourceA) {             synchronized (resourceB) {                 return resourceB.value resourceA.value;             }          }      }      publicvoid write(int a,int b) {          synchronized (resourceB) {             synchronized (resourceA) {                 resourceA.value a;                  resourceB.value b;              }          }      }  } 假设read()方法由一个线程启动write()方法由另外一个线程启动。读线程将拥有resourceA锁写线程将拥有resourceB锁两者都坚持等待的话就出现死锁。 实际上上面这个例子发生死锁的概率很小。因为在代码内的某个点CPU必须从读线程切换到写线程所以死锁基本上不能发生。 但是无论代码中发生死锁的概率有多小一旦发生死锁程序就死掉。有一些设计方法能帮助避免死锁包括始终按照预定义的顺序获取锁这一策略。已经超出SCJP的考试范围。 八、线程同步小结 1、线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。 2、线程同步方法是通过锁来实现每个对象都有切仅有一个锁这个锁与一个特定的对象关联线程一旦获取了对象锁其他访问该对象的线程就无法再访问该对象的其他同步方法。 3、对于静态同步方法锁是针对这个类的锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁当在一个同步方法中访问另外对象上的同步方法时会获取这两个对象锁。 4、对于同步要时刻清醒在哪个对象上同步这是关键。 5、编写线程安全的类需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断对“原子”操作做出分析并保证原子操作期间别的线程无法访问竞争资源。 6、当多个线程等待一个对象锁时没有获取到锁的线程将发生阻塞。 7、死锁是线程间相互等待锁锁造成的在实际中发生的概率非常的小。真让你写个死锁程序不一定好使呵呵。但是一旦程序发生死锁程序将死掉。 Java线程线程的交互 线程交互是比较复杂的问题SCJP要求不很基础给定一个场景编写代码来恰当使用等待、通知和通知所有线程。 一、线程交互的基础知识 SCJP所要求的线程交互知识点需要从java.lang.Object的类的三个方法来学习  void notify()            唤醒在此对象监视器上等待的单个线程。  void notifyAll()            唤醒在此对象监视器上等待的所有线程。  void wait()            导致当前的线程等待直到其他线程调用此对象的 notify()方法或 notifyAll()方法。 当然wait()还有另外两个重载方法  void wait(long timeout)            导致当前的线程等待直到其他线程调用此对象的 notify()方法或 notifyAll()方法或者超过指定的时间量。   void wait(long timeout, int nanos)            导致当前的线程等待直到其他线程调用此对象的 notify()方法或 notifyAll()方法或者其他某个线程中断当前线程或者已超过某个实际时间量。   以上这些方法是帮助线程传递线程关心的时间状态。 关于等待/通知要记住的关键点是 必须从同步环境内调用wait()、notify()、notifyAll()方法。线程不能调用对象上等待或通知的方法除非它拥有那个对象的锁。 wait()、notify()、notifyAll()都是Object的实例方法。与每个对象具有锁一样每个对象可以有一个线程列表他们等待来自该信号通知。线程通过执行对象上的wait()方法获得这个等待列表。从那时候起它不再执行任何其他指令直到调用对象的notify()方法为止。如果多个线程在同一个对象上等待则将只选择一个线程不保证以何种顺序继续执行。如果没有线程等待则不采取任何特殊操作。 下面看个例子就明白了 /**  * 计算输出其他线程锁计算的数据 *  * author leizhimin 2008-9-15 13:20:38  */  publicclass ThreadA {     publicstaticvoid main(String[] args) {         ThreadB b  new ThreadB();         //启动计算线程         b.start();          //线程A拥有b对象上的锁。线程为了调用wait()或notify()方法该线程必须是那个对象锁的拥有者         synchronized (b) {             try {                 System.out.println(等待对象b完成计算。。。);                 //当前线程A等待                 b.wait();              } catch (InterruptedException e) {                 e.printStackTrace();              }              System.out.println(b对象计算的总和是  b.total);         }      }  }   /**  * 计算123 ... 100的和 *  * author leizhimin 2008-9-15 13:20:49  */  publicclass ThreadBextends Thread {     int total;      publicvoid run() {         synchronized (this) {             for (int i 0; i 101; i) {                 total i;              }              //完成计算了唤醒在此对象监视器上等待的单个线程在本例中线程A被唤醒             notify();          }      }  }   等待对象b完成计算。。。 b对象计算的总和是5050  Process finished with exit code 0 千万注意 当在对象上调用wait()方法时执行该代码的线程立即放弃它在对象上的锁。然而调用notify()时并不意味着这时线程会放弃其锁。如果线程荣然在完成同步代码则线程在移出之前不会放弃锁。因此只要调用notify()并不意味着这时该锁变得可用。 二、多个线程在等待一个对象锁时候使用notifyAll() 在多数情况下最好通知等待某个对象的所有线程。如果这样做可以在对象上使用notifyAll()让所有在此对象上等待的线程冲出等待区返回到可运行状态。 下面给个例子 /**  * 计算线程  *  * author leizhimin 2008-9-20 11:15:46  */  publicclass Calculatorextends Thread {         int total;         publicvoid run() {                 synchronized (this) {                         for (int i 0; i 101; i) {                                 total i;                          }                  }                  //通知所有在此对象上等待的线程                 notifyAll();          }  }   /**  * 获取计算结果并输出  *  * author leizhimin 2008-9-20 11:15:22  */  publicclass ReaderResultextends Thread {         Calculator c;          public ReaderResult(Calculator c) {                 this.c c;         }          publicvoid run() {                 synchronized (c) {                         try {                                 System.out.println(Thread.currentThread()  等待计算结果。。。);                                 c.wait();                          } catch (InterruptedException e) {                                 e.printStackTrace();                          }                          System.out.println(Thread.currentThread() 计算结果为  c.total);                 }          }          publicstaticvoid main(String[] args) {                 Calculator calculator  new Calculator();                 //启动三个线程分别获取计算结果                 new ReaderResult(calculator).start();                 new ReaderResult(calculator).start();                 new ReaderResult(calculator).start();                 //启动计算线程                 calculator.start();          }  }   运行结果 Thread[Thread-1,5,main]等待计算结果。。。 Thread[Thread-2,5,main]等待计算结果。。。 Thread[Thread-3,5,main]等待计算结果。。。 Exception in thread Thread-0 java.lang.IllegalMonitorStateException: current thread not owner   at java.lang.Object.notifyAll(Native Method)    at threadtest.Calculator.run(Calculator.java:18)  Thread[Thread-1,5,main]计算结果为5050 Thread[Thread-2,5,main]计算结果为5050 Thread[Thread-3,5,main]计算结果为5050 Process finished with exit code 0   运行结果表明程序中有异常并且多次运行结果可能有多种输出结果。这就是说明这个多线程的交互程序还存在问题。究竟是出了什么问题需要深入的分析和思考下面将做具体分析。   实际上上面这个代码中我们期望的是读取结果的线程在计算线程调用notifyAll()之前等待即可。但是如果计算线程先执行并在读取结果线程等待之前调用了notify()方法那么又会发生什么呢这种情况是可能发生的。因为无法保证线程的不同部分将按照什么顺序来执行。幸运的是当读取线程运行时它只能马上进入等待状态----它没有做任何事情来检查等待的事件是否已经发生。  ----因此如果计算线程已经调用了notifyAll()方法那么它就不会再次调用notifyAll()----并且等待的读取线程将永远保持等待。这当然是开发者所不愿意看到的问题。   因此当等待的事件发生时需要能够检查notifyAll()通知事件是否已经发生。 通常解决上面问题的最佳方式是将 Java线程线程的调度-休眠 Java线程调度是Java多线程的核心只有良好的调度才能充分发挥系统的性能提高程序的执行效率。 这里要明确的一点不管程序员怎么编写调度只能最大限度的影响线程执行的次序而不能做到精准控制。 线程休眠的目的是使线程让出CPU的最简单的做法之一线程休眠时候会将CPU资源交给其他线程以便能轮换执行当休眠一定时间后线程会苏醒进入准备状态等待执行。 线程休眠的方法是Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)均为静态方法那调用sleep休眠的哪个线程呢简单说哪个线程调用sleep就休眠哪个线程。 /**  * Java线程线程的调度-休眠 *  * author leizhimin 2009-11-4 9:02:40  */  publicclass Test {         publicstaticvoid main(String[] args) {                 Thread t1  new MyThread1();                 Thread t2  new Thread(new MyRunnable());                 t1.start();                  t2.start();          }  }  class MyThread1 extends Thread {          publicvoid run() {                 for (int i 0; i 3; i) {                         System.out.println(线程1第  i  次执行);                         try {                                 Thread.sleep(50);                          } catch (InterruptedException e) {                                 e.printStackTrace();                          }                  }          }  }  class MyRunnableimplements Runnable {         publicvoid run() {                 for (int i 0; i 3; i) {                         System.out.println(线程2第  i  次执行);                         try {                                 Thread.sleep(50);                          } catch (InterruptedException e) {                                 e.printStackTrace();                          }                  }          }  }   线程2第0次执行 线程1第0次执行 线程1第1次执行 线程2第1次执行 线程1第2次执行 线程2第2次执行 Process finished with exit code 0   从上面的结果输出可以看出无法精准保证线程执行次序。 Java线程线程的调度-优先级 与线程休眠类似线程的优先级仍然无法保障线程的执行次序。只不过优先级高的线程获取CPU资源的概率较大优先级低的并非没机会执行。 线程的优先级用1-10之间的整数表示数值越大优先级越高默认的优先级为5。 在一个线程中开启另外一个新线程则新开线程称为该线程的子线程子线程初始优先级与父线程相同。 /**  * Java线程线程的调度-优先级 *  * author leizhimin 2009-11-4 9:02:40  */  publicclass Test {         publicstaticvoid main(String[] args) {                 Thread t1  new MyThread1();                 Thread t2  new Thread(new MyRunnable());                 t1.setPriority(10);                  t2.setPriority(1);                  t2.start();                  t1.start();          }  }  class MyThread1 extends Thread {          publicvoid run() {                 for (int i 0; i 10; i) {                         System.out.println(线程1第  i  次执行);                         try {                                 Thread.sleep(100);                          } catch (InterruptedException e) {                                 e.printStackTrace();                          }                  }          }  }  class MyRunnableimplements Runnable {         publicvoid run() {                 for (int i 0; i 10; i) {                         System.out.println(线程2第  i  次执行);                         try {                                 Thread.sleep(100);                          } catch (InterruptedException e) {                                 e.printStackTrace();                          }                  }          }  }   线程1第0次执行 线程2第0次执行 线程2第1次执行 线程1第1次执行 线程2第2次执行 线程1第2次执行 线程1第3次执行 线程2第3次执行 线程2第4次执行 线程1第4次执行 线程1第5次执行 线程2第5次执行 线程1第6次执行 线程2第6次执行 线程1第7次执行 线程2第7次执行 线程1第8次执行 线程2第8次执行 线程1第9次执行 线程2第9次执行 Process finished with exit code 0 Java线程线程的调度-让步 线程的让步含义就是使当前运行着线程让出CPU资源但是然给谁不知道仅仅是让出线程状态回到可运行状态。 线程的让步使用Thread.yield()方法yield()为静态方法功能是暂停当前正在执行的线程对象并执行其他线程。 /**  * Java线程线程的调度-让步 *  * author leizhimin 2009-11-4 9:02:40  */  publicclass Test {         publicstaticvoid main(String[] args) {                 Thread t1  new MyThread1();                 Thread t2  new Thread(new MyRunnable());                 t2.start();                  t1.start();          }  }  class MyThread1 extends Thread {          publicvoid run() {                 for (int i 0; i 10; i) {                         System.out.println(线程1第  i  次执行);                 }          }  }  class MyRunnableimplements Runnable {         publicvoid run() {                 for (int i 0; i 10; i) {                         System.out.println(线程2第  i  次执行);                         Thread.yield();                  }          }  }   线程2第0次执行 线程2第1次执行 线程2第2次执行 线程2第3次执行 线程1第0次执行 线程1第1次执行 线程1第2次执行 线程1第3次执行 线程1第4次执行 线程1第5次执行 线程1第6次执行 线程1第7次执行 线程1第8次执行 线程1第9次执行 线程2第4次执行 线程2第5次执行 线程2第6次执行 线程2第7次执行 线程2第8次执行 线程2第9次执行 Process finished with exit code 0 Java线程线程的调度-合并 线程的合并的含义就是将几个并行线程的线程合并为一个单线程执行应用场景是当一个线程必须等待另一个线程执行完毕才能执行时可以使用join方法。 join为非静态方法定义如下 void join()         等待该线程终止。     void join(long millis)         等待该线程终止的时间最长为 millis毫秒。     void join(long millis,int nanos)         等待该线程终止的时间最长为 millis毫秒  nanos 纳秒。   /**  * Java线程线程的调度-合并 *  * author leizhimin 2009-11-4 9:02:40  */  publicclass Test {         publicstaticvoid main(String[] args) {                 Thread t1  new MyThread1();                 t1.start();                  for (int i 0; i 20; i) {                         System.out.println(主线程第  i 次执行);                         if (i 2)try {                                  //t1线程合并到主线程中主线程停止执行过程转而执行t1线程直到t1执行完毕后继续。                                 t1.join();                          } catch (InterruptedException e) {                                 e.printStackTrace();                          }                  }          }  }  class MyThread1 extends Thread {          publicvoid run() {                 for (int i 0; i 10; i) {                         System.out.println(线程1第  i  次执行);                 }          }  }   主线程第0次执行 主线程第1次执行 主线程第2次执行 线程1第0次执行 主线程第3次执行 线程1第1次执行 线程1第2次执行 线程1第3次执行 线程1第4次执行 线程1第5次执行 线程1第6次执行 线程1第7次执行 线程1第8次执行 线程1第9次执行 主线程第4次执行 主线程第5次执行 主线程第6次执行 主线程第7次执行 主线程第8次执行 主线程第9次执行 主线程第10次执行 主线程第11次执行 主线程第12次执行 主线程第13次执行 主线程第14次执行 主线程第15次执行 主线程第16次执行 主线程第17次执行 主线程第18次执行 主线程第19次执行 Process finished with exit code 0 Java线程线程的调度-守护线程 守护线程与普通线程写法上基本么啥区别调用线程对象的方法setDaemon(true)则可以将其设置为守护线程。   守护线程使用的情况较少但并非无用举例来说JVM的垃圾回收、内存管理等线程都是守护线程。还有就是在做数据库应用时候使用的数据库连接池连接池本身也包含着很多后台线程监控连接个数、超时时间、状态等等。   setDaemon方法的详细说明 publicfinalvoid setDaemon(boolean on)将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时Java虚拟机退出。       该方法必须在启动线程前调用。       该方法首先调用该线程的 checkAccess方法且不带任何参数。这可能抛出 SecurityException在当前线程中。       参数     on - 如果为true则将该线程标记为守护线程。       抛出         IllegalThreadStateException - 如果该线程处于活动状态。         SecurityException - 如果当前线程无法修改该线程。   另请参见     isDaemon(), checkAccess()   /**  * Java线程线程的调度-守护线程 *  * author leizhimin 2009-11-4 9:02:40  */  publicclass Test {         publicstaticvoid main(String[] args) {                 Thread t1  new MyCommon();                 Thread t2  new Thread(new MyDaemon());                 t2.setDaemon(true);        //设置为守护线程                 t2.start();                  t1.start();          }  }  class MyCommon extends Thread {          publicvoid run() {                 for (int i 0; i 5; i) {                         System.out.println(线程1第  i  次执行);                         try {                                 Thread.sleep(7);                          } catch (InterruptedException e) {                                 e.printStackTrace();                          }                  }          }  }  class MyDaemon implements Runnable {          publicvoid run() {                 for (long i 0; i 9999999L; i) {                         System.out.println(后台线程第  i 次执行);                         try {                                 Thread.sleep(7);                          } catch (InterruptedException e) {                                 e.printStackTrace();                          }                  }          }  }   后台线程第0次执行 线程1第0次执行 线程1第1次执行 后台线程第1次执行 后台线程第2次执行 线程1第2次执行 线程1第3次执行 后台线程第3次执行 线程1第4次执行 后台线程第4次执行 后台线程第5次执行 后台线程第6次执行 后台线程第7次执行 Process finished with exit code 0   从上面的执行结果可以看出 前台线程是保证执行完毕的后台线程还没有执行完毕就退出了。   实际上JRE判断程序是否执行结束的标准是所有的前台执线程行完毕了而不管后台线程的状态因此在使用后台县城时候一定要注意这个问题。 Java线程线程的同步-同步方法 线程的同步是保证多线程安全访问竞争资源的一种手段。 线程的同步是Java多线程编程的难点往往开发者搞不清楚什么是竞争资源、什么时候需要考虑同步怎么同步等等问题当然这些问题没有很明确的答案但有些原则问题需要考虑是否有竞争资源被同时改动的问题   在本文之前请参阅《Java线程线程的同步与锁》本文是在此基础上所写的。   对于同步在具体的Java代码中需要完成一下两个操作 把竞争访问的资源标识为private 同步哪些修改变量的代码使用synchronized关键字同步方法或代码。 当然这不是唯一控制并发安全的途径。   synchronized关键字使用说明 synchronized只能标记非抽象的方法不能标识成员变量。   为了演示同步方法的使用构建了一个信用卡账户起初信用额为100w然后模拟透支、存款等多个操作。显然银行账户User对象是个竞争资源而多个并发操作的是账户方法oper(int x)当然应该在此方法上加上同步并将账户的余额设为私有变量禁止直接访问。     /**  * Java线程线程的同步  *  * author leizhimin 2009-11-4 11:23:32  */  publicclass Test {         publicstaticvoid main(String[] args) {                 User u  new User(张三, 100);                 MyThread t1  new MyThread(线程A, u, 20);                 MyThread t2  new MyThread(线程B, u, -60);                 MyThread t3  new MyThread(线程C, u, -80);                 MyThread t4  new MyThread(线程D, u, -30);                 MyThread t5  new MyThread(线程E, u, 32);                 MyThread t6  new MyThread(线程F, u, 21);                 t1.start();                  t2.start();                  t3.start();                  t4.start();                  t5.start();                  t6.start();          }  }  class MyThread extends Thread {          private User u;         privateint y 0;         MyThread(String name, User u, int y) {                 super(name);                 this.u u;                 this.y y;         }          publicvoid run() {                 u.oper(y);          }  }  class User {          private String code;         privateint cash;         User(String code, int cash) {                 this.code code;                 this.cash cash;         }          public String getCode() {                 return code;         }          publicvoid setCode(String code) {                 this.code code;         }          /**           * 业务方法          * param x 添加x万元          */          publicsynchronizedvoid oper(int x) {                 try {                         Thread.sleep(10L);                          this.cash x;                         System.out.println(Thread.currentThread().getName()  运行结束增加“  x ”当前用户账户余额为  cash);                         Thread.sleep(10L);                  } catch (InterruptedException e) {                         e.printStackTrace();                  }          }          Override          public String toString() {                 returnUser{                                  code  code \                                 , cash  cash                                 };          }  }   输出结果 线程A运行结束增加“20”当前用户账户余额为120 线程F运行结束增加“21”当前用户账户余额为141 线程E运行结束增加“32”当前用户账户余额为173 线程C运行结束增加“-80”当前用户账户余额为93 线程B运行结束增加“-60”当前用户账户余额为33 线程D运行结束增加“-30”当前用户账户余额为3 Process finished with exit code 0     反面教材不同步的情况也就是去掉oper(int x)方法的synchronized修饰符然后运行程序结果如下 线程A运行结束增加“20”当前用户账户余额为61 线程D运行结束增加“-30”当前用户账户余额为63 线程B运行结束增加“-60”当前用户账户余额为3 线程F运行结束增加“21”当前用户账户余额为61 线程E运行结束增加“32”当前用户账户余额为93 线程C运行结束增加“-80”当前用户账户余额为61 Process finished with exit code 0   很显然上面的结果是错误的导致错误的原因是多个线程并发访问了竞争资源u并对u的属性做了改动。   可见同步的重要性。     注意 通过前文可知线程退出同步方法时将释放掉方法所属对象的锁但还应该注意的是同步方法中还可以使用特定的方法对线程进行调度。这些方法来自于java.lang.Object类。   void notify()                         唤醒在此对象监视器上等待的单个线程。     void notifyAll()                         唤醒在此对象监视器上等待的所有线程。     void wait()                         导致当前的线程等待直到其他线程调用此对象的 notify()方法或 notifyAll()方法。     void wait(long timeout)                         导致当前的线程等待直到其他线程调用此对象的 notify()方法或 notifyAll()方法或者超过指定的时间量。     void wait(long timeout,int nanos)                         导致当前的线程等待直到其他线程调用此对象的 notify()方法或 notifyAll()方法或者其他某个线程中断当前线程或者已超过某个实际时间量。   结合以上方法处理多线程同步与互斥问题非常重要著名的生产者-消费者例子就是一个经典的例子任何语言多线程必学的例子。 Java线程线程的同步-同步块 对于同步除了同步方法外还可以使用同步代码块有时候同步代码块会带来比同步方法更好的效果。   追其同步的根本的目的是控制竞争资源的正确的访问因此只要在访问竞争资源的时候保证同一时刻只能一个线程访问即可因此Java引入了同步代码快的策略以提高性能。   在上个例子的基础上对oper方法做了改动由同步方法改为同步代码块模式程序的执行逻辑并没有问题。     /**  * Java线程线程的同步-同步代码块 *  * author leizhimin 2009-11-4 11:23:32  */  publicclass Test {         publicstaticvoid main(String[] args) {                 User u  new User(张三, 100);                 MyThread t1  new MyThread(线程A, u, 20);                 MyThread t2  new MyThread(线程B, u, -60);                 MyThread t3  new MyThread(线程C, u, -80);                 MyThread t4  new MyThread(线程D, u, -30);                 MyThread t5  new MyThread(线程E, u, 32);                 MyThread t6  new MyThread(线程F, u, 21);                 t1.start();                  t2.start();                  t3.start();                  t4.start();                  t5.start();                  t6.start();          }  }  class MyThread extends Thread {          private User u;         privateint y 0;         MyThread(String name, User u, int y) {                 super(name);                 this.u u;                 this.y y;         }          publicvoid run() {                 u.oper(y);          }  }  class User {          private String code;         privateint cash;         User(String code, int cash) {                 this.code code;                 this.cash cash;         }          public String getCode() {                 return code;         }          publicvoid setCode(String code) {                 this.code code;         }          /**           * 业务方法          *           * param x 添加x万元          */          publicvoid oper(int x) {                 try {                         Thread.sleep(10L);                          synchronized (this) {                                 this.cash x;                                 System.out.println(Thread.currentThread().getName() 运行结束增加“  x ”当前用户账户余额为  cash);                         }                          Thread.sleep(10L);                  } catch (InterruptedException e) {                         e.printStackTrace();                  }          }          Override          public String toString() {                 returnUser{                                  code  code \                                 , cash  cash                                 };          }  }   线程E运行结束增加“32”当前用户账户余额为132 线程B运行结束增加“-60”当前用户账户余额为72 线程D运行结束增加“-30”当前用户账户余额为42 线程F运行结束增加“21”当前用户账户余额为63 线程C运行结束增加“-80”当前用户账户余额为-17 线程A运行结束增加“20”当前用户账户余额为3 Process finished with exit code 0   注意 在使用synchronized关键字时候应该尽可能避免在synchronized方法或synchronized块中使用sleep或者yield方法因为synchronized程序块占有着对象锁你休息那么其他的线程只能一边等着你醒来执行完了才能执行。不但严重影响效率也不合逻辑。 同样在同步程序块内调用yeild方法让出CPU资源也没有意义因为你占用着锁其他互斥线程还是无法访问同步程序块。当然与同步程序块无关的线程可以获得更多的执行时间。 Java线程并发协作-生产者消费者模型 对于多线程程序来说不管任何编程语言生产者和消费者模型都是最经典的。就像学习每一门编程语言一样Hello World都是最经典的例子。   实际上准确说应该是“生产者-消费者-仓储”模型离开了仓储生产者消费者模型就显得没有说服力了。 对于此模型应该明确一下几点 1、生产者仅仅在仓储未满时候生产仓满则停止生产。 2、消费者仅仅在仓储有产品时候才能消费仓空则等待。 3、当消费者发现仓储没产品可消费时候会通知生产者生产。 4、生产者在生产出可消费产品时候应该通知等待的消费者去消费。   此模型将要结合java.lang.Object的wait与notify、notifyAll方法来实现以上的需求。这是非常重要的。   /**  * Java线程并发协作-生产者消费者模型 *  * author leizhimin 2009-11-4 14:54:36  */  publicclass Test {         publicstaticvoid main(String[] args) {                 Godown godown  new Godown(30);                 Consumer c1  new Consumer(50, godown);                 Consumer c2  new Consumer(20, godown);                 Consumer c3  new Consumer(30, godown);                 Producer p1  new Producer(10, godown);                 Producer p2  new Producer(10, godown);                 Producer p3  new Producer(10, godown);                 Producer p4  new Producer(10, godown);                 Producer p5  new Producer(10, godown);                 Producer p6  new Producer(10, godown);                 Producer p7  new Producer(80, godown);                 c1.start();                  c2.start();                  c3.start();                  p1.start();                  p2.start();                  p3.start();                  p4.start();                  p5.start();                  p6.start();                  p7.start();          }  }  /**  * 仓库  */  class Godown {          publicstaticfinalint max_size 100;//最大库存量         publicint curnum;    //当前库存量         Godown() {          }          Godown(int curnum) {                 this.curnum curnum;         }          /**           * 生产指定数量的产品          *           * param neednum           */          publicsynchronizedvoid produce(int neednum) {                 //测试是否需要生产                 while (neednum curnum max_size) {                         System.out.println(要生产的产品数量  neednum 超过剩余库存量  (max_size - curnum) 暂时不能执行生产任务!);                         try {                                 //当前的生产线程等待                                 wait();                          } catch (InterruptedException e) {                                 e.printStackTrace();                          }                  }                  //满足生产条件则进行生产这里简单的更改当前库存量                 curnum neednum;                  System.out.println(已经生产了  neednum 个产品现仓储量为  curnum);                 //唤醒在此对象监视器上等待的所有线程                 notifyAll();          }          /**           * 消费指定数量的产品          *           * param neednum           */          publicsynchronizedvoid consume(int neednum) {                 //测试是否可消费                 while (curnum neednum) {                         try {                                 //当前的生产线程等待                                 wait();                          } catch (InterruptedException e) {                                 e.printStackTrace();                          }                  }                  //满足消费条件则进行消费这里简单的更改当前库存量                 curnum - neednum;                  System.out.println(已经消费了  neednum 个产品现仓储量为  curnum);                 //唤醒在此对象监视器上等待的所有线程                 notifyAll();          }  }  /**  * 生产者  */  class Producer extends Thread {          privateint neednum;                //生产产品的数量         private Godown godown;            //仓库         Producer(int neednum, Godown godown) {                 this.neednum neednum;                 this.godown godown;         }          publicvoid run() {                 //生产指定数量的产品                 godown.produce(neednum);          }  }  /**  * 消费者  */  class Consumer extends Thread {          privateint neednum;                //生产产品的数量         private Godown godown;            //仓库         Consumer(int neednum, Godown godown) {                 this.neednum neednum;                 this.godown godown;         }          publicvoid run() {                 //消费指定数量的产品                 godown.consume(neednum);          }  }   已经生产了10个产品现仓储量为40 已经生产了10个产品现仓储量为50 已经消费了50个产品现仓储量为0 已经生产了80个产品现仓储量为80 已经消费了30个产品现仓储量为50 已经生产了10个产品现仓储量为60 已经消费了20个产品现仓储量为40 已经生产了10个产品现仓储量为50 已经生产了10个产品现仓储量为60 已经生产了10个产品现仓储量为70 Process finished with exit code 0   说明 对于本例要说明的是当发现不能满足生产或者消费条件的时候调用对象的wait方法wait方法的作用是释放当前线程的所获得的锁并调用对象的notifyAll()方法通知唤醒该对象上其他等待线程使得其继续执行。这样整个生产者、消费者线程得以正确的协作执行。 notifyAll() 方法起到的是一个通知作用不释放锁也不获取锁。只是告诉该对象上等待的线程“可以竞争执行了都醒来去执行吧”。   本例仅仅是生产者消费者模型中最简单的一种表示本例中如果消费者消费的仓储量达不到满足而又没有生产者则程序会一直处于等待状态这当然是不对的。实际上可以将此例进行修改修改为根据消费驱动生产同时生产兼顾仓库如果仓不满就生产并对每次最大消费量做个限制这样就不存在此问题了当然这样的例子更复杂更难以说明这样一个简单模型。   我喜欢简单的例子。 Java线程并发协作-死锁 线程发生死锁可能性很小即使看似可能发生死锁的代码在运行时发生死锁的可能性也是小之又小。 发生死锁的原因一般是两个对象的锁相互等待造成的。 在《Java线程线程的同步与锁》一文中简述死锁的概念与简单例子但是所给的例子是不完整的这里给出一个完整的例子。   /**  * Java线程并发协作-死锁 *  * author Administrator 2009-11-4 22:06:13  */  publicclass Test {         publicstaticvoid main(String[] args) {                 DeadlockRisk dead  new DeadlockRisk();                 MyThread t1  new MyThread(dead, 1, 2);                 MyThread t2  new MyThread(dead, 3, 4);                 MyThread t3  new MyThread(dead, 5, 6);                 MyThread t4  new MyThread(dead, 7, 8);                 t1.start();                  t2.start();                  t3.start();                  t4.start();          }  }  class MyThread extends Thread {          private DeadlockRisk dead;         privateint a, b;         MyThread(DeadlockRisk dead, int a,int b) {                  this.dead dead;                 this.a a;                 this.b b;         }          Override          publicvoid run() {                 dead.read();                  dead.write(a, b);          }  }  class DeadlockRisk {         privatestaticclass Resource {                 publicint value;         }          private Resource resourceA new Resource();          private Resource resourceB new Resource();          publicint read() {                 synchronized (resourceA) {                         System.out.println(read():  Thread.currentThread().getName() 获取了resourceA的锁);                         synchronized (resourceB) {                                 System.out.println(read():  Thread.currentThread().getName() 获取了resourceB的锁);                                 return resourceB.value resourceA.value;                         }                  }          }          publicvoid write(int a,int b) {                  synchronized (resourceB) {                         System.out.println(write():  Thread.currentThread().getName() 获取了resourceA的锁);                         synchronized (resourceA) {                                 System.out.println(write(): Thread.currentThread().getName() 获取了resourceB的锁);                                 resourceA.value a;                                  resourceB.value b;                          }                  }          }  }   下面死锁的情况发生了真是难得一见啊     Java线程volatile关键字 Java™ 语言包含两种内在的同步机制同步块或方法和 volatile变量。这两种机制的提出都是为了实现代码线程的安全性。其中 Volatile变量的同步性较差但有时它更简单并且开销更低而且其使用也更容易出错。 谈及到volatile关键字不得不提的一篇文章是《Java理论与实践:正确使用 Volatile 变量》这篇文章对volatile关键字的用法做了相当精辟的阐述。 之所以要单独提出volatile这个不常用的关键字原因是这个关键字在高性能的多线程程序中也有很重要的用途只是这个关键字用不好会出很多问题。 首先考虑一个问题为什么变量需要volatile来修饰呢 要搞清楚这个问题首先应该明白计算机内部都做什么了。比如做了一个i操作计算机内部做了三次处理读取修改写入。 同样对于一个long型数据做了个赋值操作在32系统下需要经过两步才能完成先修改低32位然后修改高32位。 假想一下当将以上的操作放到一个多线程环境下操作时候有可能出现的问题是这些步骤执行了一部分而另外一个线程就已经引用了变量值这样就导致了读取脏数据的问题。 通过这个设想就不难理解volatile关键字了。 volatile可以用在任何变量前面但不能用于final变量前面因为final型的变量是禁止修改的。也不存在线程安全的问题。 更多的内容请参看《Java理论与实践:正确使用 Volatile 变量》一文写得很好。 Java线程新特征-线程池 Sun在Java5中对Java线程的类库做了大量的扩展其中线程池就是Java5的新特征之一除了线程池之外还有很多多线程相关的内容为多线程的编程带来了极大便利。为了编写高效稳定可靠的多线程程序线程部分的新增内容显得尤为重要。 有关Java5线程新特征的内容全部在java.util.concurrent下面里面包含数目众多的接口和类熟悉这部分API特征是一项艰难的学习过程。目前有关这方面的资料和书籍都少之又少大所属介绍线程方面书籍还停留在java5之前的知识层面上。 当然新特征对做多线程程序没有必须的关系在java5之前通用可以写出很优秀的多线程程序。只是代价不一样而已。 线程池的基本思想还是一种对象池的思想开辟一块内存空间里面存放了众多未死亡的线程池中线程执行调度由池管理器来处理。当有线程任务时从池中取一个执行完成后线程对象归池这样可以避免反复创建线程对象所带来的性能开销节省了系统的资源。 在Java5之前要实现一个线程池是相当有难度的现在Java5为我们做好了一切我们只需要按照提供的API来使用即可享受线程池带来的极大便利。 Java5的线程池分好多种固定尺寸的线程池、可变尺寸连接池、。 在使用线程池之前必须知道如何去创建一个线程池在Java5中需要了解的是java.util.concurrent.Executors类的API这个类提供大量创建连接池的静态方法是必须掌握的。 一、固定大小的线程池 import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; /**  * Java线程线程池-  *  * author Administrator 2009-11-4 23:30:44  */  publicclass Test {         publicstaticvoid main(String[] args) {                 //创建一个可重用固定线程数的线程池                 ExecutorService pool Executors.newFixedThreadPool(2);                  //创建实现了Runnable接口对象Thread对象当然也实现了Runnable接口                 Thread t1  new MyThread();                 Thread t2  new MyThread();                 Thread t3  new MyThread();                 Thread t4  new MyThread();                 Thread t5  new MyThread();                 //将线程放入池中进行执行                 pool.execute(t1);                  pool.execute(t2);                  pool.execute(t3);                  pool.execute(t4);                  pool.execute(t5);                  //关闭线程池                 pool.shutdown();          }  }  class MyThread extends Thread{          Override          publicvoid run() {                 System.out.println(Thread.currentThread().getName()正在执行。。。);         }  }   pool-1-thread-1正在执行。。。 pool-1-thread-1正在执行。。。 pool-1-thread-1正在执行。。。 pool-1-thread-1正在执行。。。 pool-1-thread-2正在执行。。。 Process finished with exit code 0   二、单任务线程池   在上例的基础上改一行创建pool对象的代码为                 //创建一个使用单个 worker线程的 Executor以无界队列方式来运行该线程。                 ExecutorService pool Executors.newSingleThreadExecutor();   输出结果为 pool-1-thread-1正在执行。。。 pool-1-thread-1正在执行。。。 pool-1-thread-1正在执行。。。 pool-1-thread-1正在执行。。。 pool-1-thread-1正在执行。。。 Process finished with exit code 0   对于以上两种连接池大小都是固定的当要加入的池的线程或者任务超过池最大尺寸时候则入此线程池需要排队等待。 一旦池中有线程完毕则排队等待的某个线程会入池执行。   三、可变尺寸的线程池 与上面的类似只是改动下pool的创建方式                 //创建一个可根据需要创建新线程的线程池但是在以前构造的线程可用时将重用它们。                 ExecutorService pool Executors.newCachedThreadPool();   pool-1-thread-5正在执行。。。 pool-1-thread-1正在执行。。。 pool-1-thread-4正在执行。。。 pool-1-thread-3正在执行。。。 pool-1-thread-2正在执行。。。 Process finished with exit code 0   四、延迟连接池   import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /**  * Java线程线程池-  *  * author Administrator 2009-11-4 23:30:44  */  publicclass Test {         publicstaticvoid main(String[] args) {                 //创建一个线程池它可安排在给定延迟后运行命令或者定期地执行。                 ScheduledExecutorService pool Executors.newScheduledThreadPool(2);                 //创建实现了Runnable接口对象Thread对象当然也实现了Runnable接口                 Thread t1  new MyThread();                 Thread t2  new MyThread();                 Thread t3  new MyThread();                 Thread t4  new MyThread();                 Thread t5  new MyThread();                 //将线程放入池中进行执行                 pool.execute(t1);                  pool.execute(t2);                  pool.execute(t3);                  //使用延迟执行风格的方法                 pool.schedule(t4, 10, TimeUnit.MILLISECONDS);                  pool.schedule(t5, 10, TimeUnit.MILLISECONDS);                  //关闭线程池                 pool.shutdown();          }  }  class MyThread extends Thread {          Override          publicvoid run() {                 System.out.println(Thread.currentThread().getName()  正在执行。。。);         }  }   pool-1-thread-1正在执行。。。 pool-1-thread-2正在执行。。。 pool-1-thread-1正在执行。。。 pool-1-thread-1正在执行。。。 pool-1-thread-2正在执行。。。 Process finished with exit code 0   五、单任务延迟连接池   在四代码基础上做改动                 //创建一个单线程执行程序它可安排在给定延迟后运行命令或者定期地执行。                 ScheduledExecutorService pool Executors.newSingleThreadScheduledExecutor();   pool-1-thread-1正在执行。。。 pool-1-thread-1正在执行。。。 pool-1-thread-1正在执行。。。 pool-1-thread-1正在执行。。。 pool-1-thread-1正在执行。。。 Process finished with exit code 0   六、自定义线程池   import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /**  * Java线程线程池-自定义线程池 *  * author Administrator 2009-11-4 23:30:44  */  publicclass Test {         publicstaticvoid main(String[] args) {                 //创建等待队列                 BlockingQueueRunnable bqueue  newArrayBlockingQueueRunnable(20);                 //创建一个单线程执行程序它可安排在给定延迟后运行命令或者定期地执行。                 ThreadPoolExecutor pool  newThreadPoolExecutor(2,3,2,TimeUnit.MILLISECONDS,bqueue);                 //创建实现了Runnable接口对象Thread对象当然也实现了Runnable接口                 Thread t1  new MyThread();                 Thread t2  new MyThread();                 Thread t3  new MyThread();                 Thread t4  new MyThread();                 Thread t5  new MyThread();                 Thread t6  new MyThread();                 Thread t7  new MyThread();                 //将线程放入池中进行执行                 pool.execute(t1);                  pool.execute(t2);                  pool.execute(t3);                  pool.execute(t4);                  pool.execute(t5);                  pool.execute(t6);                  pool.execute(t7);                  //关闭线程池                 pool.shutdown();          }  }  class MyThread extends Thread {          Override          publicvoid run() {                 System.out.println(Thread.currentThread().getName()  正在执行。。。);                 try {                         Thread.sleep(100L);                  } catch (InterruptedException e) {                         e.printStackTrace();                  }          }  }   pool-1-thread-1正在执行。。。 pool-1-thread-2正在执行。。。 pool-1-thread-2正在执行。。。 pool-1-thread-1正在执行。。。 pool-1-thread-2正在执行。。。 pool-1-thread-1正在执行。。。 pool-1-thread-2正在执行。。。 Process finished with exit code 0   创建自定义线程池的构造方法很多本例中参数的含义如下 ThreadPoolExecutor public ThreadPoolExecutor(int corePoolSize,                          int maximumPoolSize,                          long keepAliveTime,                           TimeUnit unit,                          BlockingQueueRunnable workQueue) 用给定的初始参数和默认的线程工厂及处理程序创建新的ThreadPoolExecutor。使用Executors工厂方法之一比使用此通用构造方法方便得多。 参数 corePoolSize -池中所保存的线程数包括空闲线程。 maximumPoolSize -池中允许的最大线程数。 keepAliveTime -当线程数大于核心时此为终止前多余的空闲线程等待新任务的最长时间。 unit - keepAliveTime参数的时间单位。 workQueue -执行前用于保持任务的队列。此队列仅保持由execute方法提交的Runnable任务。 抛出 IllegalArgumentException -如果 corePoolSize或 keepAliveTime小于零或者 maximumPoolSize小于或等于零或者 corePoolSize大于 maximumPoolSize。 NullPointerException -如果workQueue为 null   自定义连接池稍微麻烦些不过通过创建的ThreadPoolExecutor线程池对象可以获取到当前线程池的尺寸、正在执行任务的线程数、工作队列等等。   有关Java5线程池的内容到此就没有了更多的内容还需要研读API来获取。 Java线程新特征-有返回值的线程 在Java5之前线程是没有返回值的常常为了“有”返回值破费周折而且代码很不好写。或者干脆绕过这道坎走别的路了。   现在Java终于有可返回值的任务也可以叫做线程了。   可返回值的任务必须实现Callable接口类似的无返回值的任务必须Runnable接口。   执行Callable任务后可以获取一个Future的对象在该对象上调用get就可以获取到Callable任务返回的Object了。   下面是个很简单的例子   import java.util.concurrent.*; /**  * Java线程有返回值的线程 *  * author Administrator 2009-11-5 0:41:50  */  publicclass Test {         publicstaticvoid main(String[] args)throws ExecutionException, InterruptedException {                 //创建一个线程池                 ExecutorService pool Executors.newFixedThreadPool(2);                  //创建两个有返回值的任务                 Callable c1  new MyCallable(A);                 Callable c2  new MyCallable(B);                 //执行任务并获取Future对象                 Future f1 pool.submit(c1);                  Future f2 pool.submit(c2);                  //从Future对象上获取任务的返回值并输出到控制台                 System.out.println(f1.get().toString());                 System.out.println(f2.get().toString());                 //关闭线程池                 pool.shutdown();          }  }  class MyCallableimplements Callable{         private String oid;         MyCallable(String oid) {                  this.oid oid;         }          Override          public Object call()throws Exception {                 return oid任务返回的内容;         }  }   A任务返回的内容 B任务返回的内容  Process finished with exit code 0   非常的简单要深入了解还需要看Callable和Future接口的API啊。 Java线程新特征-锁上 在Java5中专门提供了锁对象利用锁可以方便的实现资源的封锁用来控制对竞争资源并发访问的控制这些内容主要集中在java.util.concurrent.locks包下面里面有三个重要的接口Condition、Lock、ReadWriteLock。         Condition   Condition将Object监视器方法wait、notify和 notifyAll分解成截然不同的对象以便通过将这些对象与任意Lock实现组合使用为每个对象提供多个等待 setwait-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  */  publicclass Test {         publicstaticvoid 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;        //所要操作的账户         privateint 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;         }          publicvoid 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;        //账号         privateint cash;            //账户余额         MyCount(String oid, int cash) {                 this.oid oid;                 this.cash cash;         }          public String getOid() {                 return oid;         }          publicvoid setOid(String oid) {                 this.oid oid;         }          publicint getCash() {                 return cash;         }          publicvoid setCash(int cash) {                 this.cash cash;         }          Override          public String toString() {                 returnMyCount{                                  oid  oid \                                 , cash  cash                                 };          }  }   张三正在操作MyCount{oid95599200901215522, cash10000}账户金额为-4000当前金额为10000 张三操作MyCount{oid95599200901215522, cash6000}账户成功金额为-4000当前金额为6000 张三他爹正在操作MyCount{oid95599200901215522, cash6000}账户金额为6000当前金额为6000 张三他爹操作MyCount{oid95599200901215522, cash12000}账户成功金额为6000当前金额为12000 张三他弟正在操作MyCount{oid95599200901215522, cash12000}账户金额为-8000当前金额为12000 张三他弟操作MyCount{oid95599200901215522, cash4000}账户成功金额为-8000当前金额为4000 张三正在操作MyCount{oid95599200901215522, cash4000}账户金额为800当前金额为4000 张三操作MyCount{oid95599200901215522, cash4800}账户成功金额为800当前金额为4800 Process finished with exit code 0   从上面的输出可以看到利用锁对象太方便了比直接在某个不知情的对象上用锁清晰多了。   但一定要注意的是在获取了锁对象后用完后应该尽快释放锁以便别的等待该锁的线程有机会去执行。 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  */  publicclass Test {         publicstaticvoid 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;        //所要操作的账户         privateint iocash;                //操作的金额当然有正负之分了         private ReadWriteLock myLock;                //执行操作所需的锁对象         privateboolean ischeck;        //是否查询         User(String name, MyCount myCount, int iocash, ReadWriteLock myLock,booleanischeck) {                 this.name name;                 this.myCount myCount;                 this.iocash iocash;                 this.myLock myLock;                 this.ischeck ischeck;         }          publicvoid 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;        //账号         privateint cash;            //账户余额         MyCount(String oid, int cash) {                 this.oid oid;                 this.cash cash;         }          public String getOid() {                 return oid;         }          publicvoid setOid(String oid) {                 this.oid oid;         }          publicint getCash() {                 return cash;         }          publicvoid setCash(int cash) {                 this.cash cash;         }          Override          public String toString() {                 returnMyCount{                                  oid  oid \                                 , cash  cash                                 };          }  }   写张三正在操作MyCount{oid95599200901215522, cash10000}账户金额为-4000当前金额为10000 写张三操作MyCount{oid95599200901215522, cash6000}账户成功金额为-4000当前金额为6000 写张三他弟正在操作MyCount{oid95599200901215522, cash6000}账户金额为-8000当前金额为6000 写张三他弟操作MyCount{oid95599200901215522, cash-2000}账户成功金额为-8000当前金额为-2000 写张三正在操作MyCount{oid95599200901215522, cash-2000}账户金额为800当前金额为-2000 写张三操作MyCount{oid95599200901215522, cash-1200}账户成功金额为800当前金额为-1200 读张三他爹正在查询MyCount{oid95599200901215522, cash-1200}账户当前金额为-1200 写张三他爹正在操作MyCount{oid95599200901215522, cash-1200}账户金额为6000当前金额为-1200 写张三他爹操作MyCount{oid95599200901215522, cash4800}账户成功金额为6000当前金额为4800 Process finished with exit code 0   在实际开发中最好在能用读写锁的情况下使用读写锁而不要用普通锁以求更好的性能。 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  */  publicclass Test {         publicstaticvoid 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;         }          publicvoid setSp(Semaphore sp) {                 this.sp sp;         }  }  class MyThread extends Thread {          private String threadname;            //线程的名称         private MyPool pool;                        //自定义池         privateintx;                                    //申请信号量的大小         MyThread(String threadname, MyPool pool, int x) {                 this.threadname threadname;                 this.pool pool;                 this.x x;         }          publicvoid 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   从结果可以看出信号量仅仅是对池资源进行监控但不保证线程的安全因此在使用时候应该自己控制线程的安全访问池资源。     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  */  publicclass Test {         publicstaticvoid 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差别也很小。 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  */  publicclass Test {         publicstaticvoid 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   从上面结果可以看到程序并没结束二是阻塞住了原因是栈已经满了后面追加元素的操作都被阻塞了。 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  */  publicclass Test {         publicstaticvoid 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 SaveThreadextends Thread {         private String name;                //操作人         private MyCount myCount;        //账户         privateint x;                            //存款金额         SaveThread(String name, MyCount myCount, int x) {                 this.name name;                 this.myCount myCount;                 this.x x;         }          publicvoid run() {                 myCount.saving(x, name);          }  }  /**  * 取款线程类  */  class DrawThreadextends Thread {         private String name;                //操作人         private MyCount myCount;        //账户         privateint x;                            //存款金额         DrawThread(String name, MyCount myCount, int x) {                 this.name name;                 this.myCount myCount;                 this.x x;         }          publicvoid run() {                 myCount.drawing(x, name);          }  }  /**  * 普通银行账户不可透支  */  class MyCount {          private String oid;                        //账号         privateint 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 操作人          */          publicvoid 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 操作人          */          publicvoid 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  */  publicclass Test {         publicstaticvoid 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 SaveThreadextends Thread {         private String name;                //操作人         private MyCount myCount;        //账户         privateint x;                            //存款金额         SaveThread(String name, MyCount myCount, int x) {                 this.name name;                 this.myCount myCount;                 this.x x;         }          publicvoid run() {                 myCount.saving(x, name);          }  }  /**  * 取款线程类  */  class DrawThreadextends Thread {         private String name;                //操作人         private MyCount myCount;        //账户         privateint x;                            //存款金额         DrawThread(String name, MyCount myCount, int x) {                 this.name name;                 this.myCount myCount;                 this.x x;         }          publicvoid run() {                 myCount.drawing(x, name);          }  }  /**  * 普通银行账户不可透支  */  class MyCount {          private String oid;                        //账号         privateint cash;                            //账户余额         MyCount(String oid, int cash) {                 this.oid oid;                 this.cash cash;         }          /**           * 存款           *           * param x        操作金额          * param name 操作人          */          publicsynchronizedvoid saving(int x, String name) {                 if (x 0) {                         cash x;                    //存款                         System.out.println(name  存款  x 当前余额为  cash);                 }                  notifyAll();            //唤醒所有等待线程。         }          /**           * 取款           *           * param x        操作金额          * param name 操作人          */          publicsynchronizedvoid 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  */  publicclass Test {         publicstaticvoid 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 SaveThreadextends Thread {         private String name;                //操作人         private MyCount myCount;        //账户         privateint x;                            //存款金额         SaveThread(String name, MyCount myCount, int x) {                 this.name name;                 this.myCount myCount;                 this.x x;         }          publicvoid run() {                 myCount.saving(x, name);          }  }  /**  * 取款线程类  */  class DrawThreadextends Thread {         private String name;                //操作人         private MyCount myCount;        //账户         privateint x;                            //存款金额         DrawThread(String name, MyCount myCount, int x) {                 this.name name;                 this.myCount myCount;                 this.x x;         }          publicvoid run() {                 myCount.drawing(x, name);          }  }  /**  * 普通银行账户不可透支  */  class MyCount {          private String oid;                        //账号         privateint cash;                            //账户余额         MyCount(String oid, int cash) {                 this.oid oid;                 this.cash cash;         }          /**           * 存款           *           * param x        操作金额          * param name 操作人          */          publicvoid saving(int x, String name) {                 if (x 0) {                         synchronized (this) {                                 cash x;                    //存款                                 System.out.println(name  存款  x 当前余额为  cash);                                 notifyAll();            //唤醒所有等待线程。                         }                  }          }          /**           * 取款           *           * param x        操作金额          * param name 操作人          */          publicsynchronizedvoid 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线程新特征-原子量 所谓的原子量即操作变量的操作是“原子的”该操作不可再分因此是线程安全的。 为何要使用原子变量呢原因是多个线程对单个变量操作也会引起一些问题。在Java5之前可以通过volatile、synchronized关键字来解决并发访问的安全问题但这样太麻烦。 Java5之后专门提供了用来进行单变量多线程并发安全访问的工具包java.util.concurrent.atomic其中的类也很简单。 下面给出一个反面例子切勿模仿 1 package com.bmw.mms.test;2 3 import java.util.concurrent.ExecutorService;4 import java.util.concurrent.Executors;5 import java.util.concurrent.atomic.AtomicLong;6 7 /**8 * Java线程新特征-原子量9 */ 10 public class format { 11 public static void main(String[] args) { 12 ExecutorService pool Executors.newFixedThreadPool(2); 13 Runnable t1 new MyRunnable(张三, 2000); 14 Runnable t2 new MyRunnable(李四, 3600); 15 Runnable t3 new MyRunnable(王五, 2700); 16 Runnable t4 new MyRunnable(老张, 600); 17 Runnable t5 new MyRunnable(老牛, 1300); 18 Runnable t6 new MyRunnable(胖子, 800); 19 // 执行各个线程 20 pool.execute(t1); 21 pool.execute(t2); 22 pool.execute(t3); 23 pool.execute(t4); 24 pool.execute(t5); 25 pool.execute(t6); 26 // 关闭线程池 27 pool.shutdown(); 28 } 29 } 30 31 class MyRunnable implements Runnable { 32 private static AtomicLong aLong new AtomicLong(10000); // 原子量每个线程都可以自由操作 33 private String name; // 操作人 34 private int x; // 操作数额 35 36 MyRunnable(String name, int x) { 37 this.name name; 38 this.x x; 39 } 40 41 public void run() { 42 System.out.println(name 执行了 x 当前余额 aLong.addAndGet(x)); 43 } 44 }   运行结果 李四执行了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   从运行结果可以看出虽然使用了原子量但是程序并发访问还是有问题那究竟问题出在哪里了   这里要注意的一点是原子量虽然可以保证单个变量在某一个操作过程的安全但无法保证你整个代码块或者整个程序的安全性。因此通常还应该使用锁等同步机制来控制整个程序的安全性。   下面是对这个错误修正 1 import java.util.concurrent.ExecutorService;2 import java.util.concurrent.Executors;3 import java.util.concurrent.locks.Lock;4 import java.util.concurrent.locks.ReentrantLock;5 import java.util.concurrent.atomic.AtomicLong;6 7 /** 8 * Java线程新特征-原子量9 */ 10 public class Test { 11   public static void main(String[] args) { 12     ExecutorService pool Executors.newFixedThreadPool(2); 13 Lock lock new ReentrantLock(false); 14 Runnable t1 new MyRunnable(张三, 2000,lock); 15 Runnable t2 new MyRunnable(李四, 3600,lock); 16 Runnable t3 new MyRunnable(王五, 2700,lock); 17 Runnable t4 new MyRunnable(老张, 600,lock); 18 Runnable t5 new MyRunnable(老牛, 1300,lock); 19 Runnable t6 new MyRunnable(胖子, 800,lock); 20 //执行各个线程 21 pool.execute(t1); 22 pool.execute(t2); 23 pool.execute(t3); 24 pool.execute(t4); 25 pool.execute(t5); 26 pool.execute(t6); 27 //关闭线程池 28 pool.shutdown(); 29 } 30 } 31 32 class MyRunnable implements Runnable { 33   private static AtomicLong aLong new AtomicLong(10000); //原子量每个线程都可以自由操作 34 private String name; //操作人 35 privateint x; //操作数额 36 private Lock lock; 37 38 MyRunnable(String name, int x,Lock lock) { 39   this.name name; 40   this.x x; 41   this.lock lock; 42 } 43 44 public void run() { 45   lock.lock(); 46   System.out.println(name 执行了 x 当前余额 aLong.addAndGet(x)); 47   lock.unlock(); 48 } 49 }   执行结果 张三执行了2000当前余额12000 王五执行了2700当前余额14700 老张执行了600当前余额15300 老牛执行了1300当前余额16600 胖子执行了800当前余额17400 李四执行了3600当前余额21000 Process finished with exit code 0 这里使用了一个对象锁来控制对并发代码的访问。不管运行多少次执行次序如何最终余额均为21000这个结果是正确的。 有关原子量的用法很简单关键是对原子量的认识原子仅仅是保证变量操作的原子性但整个程序还需要考虑线程安全的。 Java线程新特征-障碍器 Java5中添加了障碍器类为了适应一种新的设计需求比如一个大型的任务常常需要分配好多子任务去执行只有当所有子任务都执行完成时候才能执行主任务这时候就可以选择障碍器了。 障碍器是多线程并发控制的一种手段用法很简单。下面给个例子 1 import java.util.concurrent.BrokenBarrierException;2 import java.util.concurrent.CyclicBarrier;3 4 /** 5 * Java线程新特征-障碍器6 */ 7 public class Test {8 public static void main(String[] args) {9 //创建障碍器并设置MainTask为所有定数量的线程都达到障碍点时候所要执行的任务(Runnable) 10 CyclicBarrier cb new CyclicBarrier(7,new MainTask()); 11 new SubTask(A, cb).start(); 12 new SubTask(B, cb).start(); 13 new SubTask(C, cb).start(); 14 new SubTask(D, cb).start(); 15 new SubTask(E, cb).start(); 16 new SubTask(F, cb).start(); 17 new SubTask(G, cb).start(); 18 } 19 } 20 21 /** 22 * 主任务 23 */ 24 class MainTask implements Runnable { 25 public void run() { 26 System.out.println(主任务执行了); 27 } 28 } 29 30 /** 31 * 子任务 32 */ 33 class SubTask extends Thread { 34   private String name; 35   private CyclicBarrier cb; 36 37   SubTask(String name, CyclicBarrier cb) { 38    this.name name; 39   this.cb cb; 40 } 41 42   public void run() { 43   System.out.println([子任务 name ]开始执行了); 44 for (int i 0; i 999999; i) ; //模拟耗时的任务 45   System.out.println([子任务 name ]开始执行完成了并通知障碍器已经完成); 46   try { 47  //通知障碍器已经完成 48 cb.await(); 49 } catch (InterruptedException e) { 50 e.printStackTrace(); 51 } catch (BrokenBarrierException e) { 52 e.printStackTrace(); 53 } 54     } 55 } 运行结果 [子任务E]开始执行了 [子任务E]开始执行完成了并通知障碍器已经完成 [子任务F]开始执行了 [子任务G]开始执行了 [子任务F]开始执行完成了并通知障碍器已经完成 [子任务G]开始执行完成了并通知障碍器已经完成 [子任务C]开始执行了 [子任务B]开始执行了 [子任务C]开始执行完成了并通知障碍器已经完成 [子任务D]开始执行了 [子任务A]开始执行了 [子任务D]开始执行完成了并通知障碍器已经完成 [子任务B]开始执行完成了并通知障碍器已经完成 [子任务A]开始执行完成了并通知障碍器已经完成 主任务执行了 Process finished with exit code 0 从执行结果可以看出所有子任务完成的时候主任务执行了达到了控制的目标。转载于:https://www.cnblogs.com/bpynfy/p/5548818.html
http://www.pierceye.com/news/97031/

相关文章:

  • 昆明网站空间好习惯网站
  • 做导航网站赚钱吗建立网站需要多少钱费用
  • 大同网站建设哪家好网站后台登录模板html
  • 网站建设过程中准备的工作手机制作网站
  • 做专业网站设计多少钱代理小企业网站建设
  • 怎样提升网站关键词免费的html模版下载
  • 栖霞网站定制三合一建站网站
  • 免费建立一个个人网站设计官网登录入口
  • 门户网站模板之家北京网上服务平台
  • 合肥网站优化方案东莞做网站那家好
  • 个人备案网站可以做论坛吗山东住房建设厅官网站首页
  • 寺院网站模板网站策划制作公司 北京
  • 昆山教育云平台网站建设宁晋县建设局网站
  • 廊坊网站公司dw做网站背景音乐
  • 阜南做网站搜索引擎优化seo多少钱
  • 贵州建设厅网站怎样查询电工证天津网站备案
  • 常州做网站的公司在盐城做网站的网络公司电话
  • seo站外推广如何用wampp 做网站
  • 怎样用手机做网站中企动力百度百科
  • 哪些网站可以做任务挣钱免费app软件
  • 国内简约网站平潭县机场建设网站
  • wordpress 全站通知wordpress怎样打开速度快
  • 广州市建设职业培训学校网站移除wordpress版本
  • 如何申请一个网站 新网动画制作大师
  • 动易后台 网站统计调查 报表类型怎样使用手机相册备份网站源码
  • 做网站放到百度上需要什么查看wordpress使用什么主题
  • 深圳企业网站seo郑州汉狮专业做网站公司
  • 广东网站建设多少钱辛集专业网站建设
  • 怎样做网站公司的销售小程序推广计划怎么赚钱
  • 网站开发文档编写wordpress小说站群