class="java" name="code"> package com.partner4java.itcast.util.thread; /** * 两种传统的线程创建方式 * * @author partner4java * */ public class TraditionalThread { // 创建线程的两种传统方式 // 在Thread子类覆盖的run方法中编写运行代码 // 涉及一个以往知识点:能否在run方法声明上抛出InterruptedException异常,以便省略run方法内部对Thread.sleep()语句的try…catch处理? // 在传递给Thread对象的Runnable对象的run方法中编写代码 // 总结:查看Thread类的run()方法的源代码,可以看到其实这两种方式都是在调用Thread对象的run方法,如果Thread类的run方法没有被覆盖,并且为该Thread对象设置了一个Runnable对象,该run方法会调用Runnable对象的run方法。 // 问题:如果在Thread子类覆盖的run方法中编写了运行代码,也为Thread子类对象传递了一个Runnable对象,那么,线程运行时的执行代码是子类的run方法的代码?还是Runnable对象的run方法的代码? // 涉及到的一个以往知识点:匿名内部类对象的构造方法如何调用父类的非默认构造方法。 public static void main(String[] args) { // threadTest1(); // threadTest2(); threadTest3(); } /** * 传统的方式1 */ private static void threadTest1() { Thread thread = new Thread() { @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(Thread.currentThread().getName() + i); // System.out.println(this.getName() + i); // 上面两个等效 } } }; thread.start(); } /** * 传统的方式2 */ private static void threadTest2() { Thread thread2 = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println(i + Thread.currentThread().getName()); } } }); thread2.start(); } /** * 问题:如果在Thread子类覆盖的run方法中编写了运行代码,也为Thread子类对象传递了一个Runnable对象, * 那么,线程运行时的执行代码是子类的run方法的代码?还是Runnable对象的run方法的代码?<br/> * 运行了子类的run方法 */ private static void threadTest3() { Thread thread = new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println("Runnable: " + i + Thread.currentThread().getName()); } } }) { @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println("Thread: " + Thread.currentThread().getName() + i); // System.out.println(this.getName() + i); // 上面两个等效 } // super.run();//如果添加Runnable也会被执行 } }; thread.start(); } }
package com.partner4java.itcast.util.thread; import java.util.Timer; import java.util.TimerTask; /** * 传统的定时器<br/> * 参考:<br/> * http://www.nongfu888.com/jforum/posts/list/59.page#137<br/> * http://blog.csdn.net/partner4java/article/details/6327661<br/> * Quartz调度器可以指定何时开始执行 * @author partner4java * */ public class TraditionalTimer { public static void main(String[] args) { testTimer1(); } /** * 传统定时器写法 */ private static void testTimer1() { new Timer().schedule(new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }, 5000, 1000); } }
package com.partner4java.itcast.util.syn; /** * 传统的线程互斥<br/> * 需要同一把锁,就会互斥 * @author partner4java * */ public class TraditionalThreadSynchronized { public static void main(String[] args) { test1(); } public static void test1(){ Thread thread1 = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } while(true){ new TraditionalThreadSynchronized().outPut4("------------小张杰"); } } }); thread1.start(); Thread thread2 = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } while(true){ new TraditionalThreadSynchronized().outPut4("+++++++++++小刘"); } } }); thread2.start(); } /** * 错误的方式,没有做任何互斥保护 * @param boy */ public static void outPut1(String boy){ int len = boy.length(); for(int i=0;i<len;i++){ System.out.print(boy.charAt(i)); } System.out.println(); // 打印出现了: // -小刘 // +++++++-----------小张杰 // -----++++小刘 // +++++++++++小刘 // +++++++++++小刘 // ++++++-------小张杰 // ------------小张杰 //下面这个没问题,上面的问题只是出在了线程交替排队的问题上(也就是指针本身变的可能性很小) // String boyBad = boy + "fuck"+boy; // System.out.println(boyBad); } /** * 错误的,boy并不是唯一的 * @param boy */ public static void outPut2(String boy){ int len = boy.length(); synchronized (boy) { for(int i=0;i<len;i++){ System.out.print(boy.charAt(i)); } System.out.println(); } } /** * 就可以了,保证一个唯一的对象安全(static方法唯一,字符串相同在jvm唯一) * @param boy */ public static void outPut3(String boy){ int len = boy.length(); String boyBad = ""; synchronized (boyBad) { for(int i=0;i<len;i++){ System.out.print(boy.charAt(i)); } System.out.println(); } } /** * 正确的,(字符串相同在jvm唯一) * @param boy */ public void outPut4(String boy){ int len = boy.length(); String boyBad = "Hello World"; synchronized (boyBad) { for(int i=0;i<len;i++){ System.out.print(boy.charAt(i)); } System.out.println(); } } /** * 错误的,this不唯一,可以new出来多个,在一个对象里面正确 * @param boy */ public void outPut5(String boy){ int len = boy.length(); String boyBad = ""; synchronized (this) { for(int i=0;i<len;i++){ System.out.print(boy.charAt(i)); } System.out.println(); } } /** * 保护整个方法,且,static确保唯一性(一个方法里面最好只有一个synchronized,否则方法里面再写个synchronized会出现死锁) * @param boy */ public synchronized static void outPut6(String boy){ int len = boy.length(); for(int i=0;i<len;i++){ System.out.print(boy.charAt(i)); } System.out.println(); } /** * 不正确,new出来多少个对象,这个方法就有多少个,在一个对象里面正确 * @param boy */ public synchronized void outPut7(String boy){ int len = boy.length(); for(int i=0;i<len;i++){ System.out.print(boy.charAt(i)); } System.out.println(); } //上面当两个线程调用这个类的不同方法,想要同步,那么同步的锁就需要是同一把锁,比如,jvm中唯一的string,或者类的字节码,TraditionalThreadSynchronized.class和静态方法都用的同一个类的字节码 }
package com.partner4java.itcast.util.comm; /** * 笔试题:<br/> * 子线程循环10次,接着主线程循环20,接着又回到子线程循环10次,接着再回到主线程又循环20,如此循环50次 * @author partner4java * */ public class TraditionalThreadCommunication { public static void main(String[] args) { final Bussiness bussiness = new Bussiness(); //子线程 new Thread(new Runnable() { @Override public void run() { for(int j=1;j<=50;j++){ bussiness.sub(j); } } }).start(); //主线程(没有再开一个线程,因为当前main也是一个线程) for(int j=1;j<=50;j++){ bussiness.main(j); } } } class Bussiness{ /** 是否是主线程应该执行 */ private boolean isMain = false; public synchronized void main(int j){ if(!isMain){//不是主线程该执行 try { this.wait();//等待 } catch (InterruptedException e) { e.printStackTrace(); } } for(int i=1;i<=20;i++){ System.out.print(" #主线程循环" + i + "次;" + "第" + j + "次迭代循环# "); } System.out.println(); System.out.println(); //能执行到这,也就是主线程已经执行完了,那么就应该子线程执行了 isMain = false; this.notify(); } public synchronized void sub(int j){ if(isMain){//是该主线程执行 try { this.wait();//等待执行完毕 } catch (InterruptedException e) { e.printStackTrace(); } } for(int i=1;i<=10;i++){ System.out.print(" *子线程循环" + i + "次;" + "第" + j + "次迭代循环* "); } System.out.println(); System.out.println(); //能执行到这,也就是子线程已经执行完了,那么就应该主线程执行了 isMain = true; this.notify(); } }
package com.partner4java.itcast.util.scope; import java.util.HashMap; import java.util.Map; import java.util.Random; /** * 多个线程访问共享对象和数据的方式<br/> * 如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做。<br/> * <br/> * 如果每个线程执行的代码不同,这时候需要用不同的Runnable对象, * 有如下两种方式来实现这些Runnable对象之间的数据共享:<br/> * 将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。 * 每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。<br/> * 将这些Runnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量, * 每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥和通信, * 作为内部类的各个Runnable对象调用外部类的这些方法。<br/> * 上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象身上去完成, * 对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。<br/> * 总之,要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥和通信。<br/> * <br/> * 极端且简单的方式,即在任意一个类中定义一个static的变量,这将被所有线程共享。 * @author partner4java * */ public class ThreadScopeShareData { private static int data = 0; private static Map<Thread, Integer> threadData = new HashMap<Thread, Integer>(); public static void main(String[] args) { // test1(); // test2(); // test3(); test42(); } /** * 打印结果:为什么除了"new:"所有的data都相同呢?不是获取两次么? */ private static void test1() { for(int i=0;i<2;i++){ new Thread(new Runnable(){ @Override public void run() { data = new Random().nextInt(); System.out.println("old:" + data); System.out.println("new:" + new Random().nextInt()); System.out.println(Thread.currentThread().getName() + " has put data :" + data); threadData.put(Thread.currentThread(), data); new A().get(); new B().get(); } }).start(); } // 后台打印: // old:395383668 // old:395383668 // new:343517543 // new:663490710 // Thread-0 has put data :395383668 // Thread-1 has put data :395383668 // A from Thread-0 get data :395383668 // A from Thread-1 get data :395383668 // B from Thread-0 get data :395383668 // B from Thread-1 get data :395383668 } /** * 感觉这样才对劲吧?为什么加个Thread.sleep(1000);才对头呢? * 因为,上面犯了线程安全的问题,或者说两个人同时拿了同一个东西。<br/> * 但是,这个特点,我们还可以拿来做一些特殊的需求。(或者称为“线程范围的共享变量”) */ private static void test2() { for(int i=0;i<2;i++){ new Thread(new Runnable(){ @Override public void run() { data = new Random().nextInt(); System.out.println("old:" + data); System.out.println("new:" + new Random().nextInt()); System.out.println(Thread.currentThread().getName() + " has put data :" + data); threadData.put(Thread.currentThread(), data); new A().get(); new B().get(); } }).start(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } // 后台打印: // old:266965663 // new:-1392595905 // Thread-0 has put data :266965663 // A from Thread-0 get data :266965663 // B from Thread-0 get data :266965663 // old:-60098112 // new:-969077517 // Thread-1 has put data :-60098112 // A from Thread-1 get data :-60098112 // B from Thread-1 get data :-60098112 } /** * 不用全局data,这样data感觉就对了。 */ private static void test3() { for(int i=0;i<2;i++){ new Thread(new Runnable(){ @Override public void run() { //单独声明 int data = new Random().nextInt(); System.out.println("old:" + data); System.out.println("new:" + new Random().nextInt()); System.out.println(Thread.currentThread().getName() + " has put data :" + data); threadData.put(Thread.currentThread(), data); new A().get(); new B().get(); } }).start(); } // 后台打印: // old:834065409 // old:-212821024 // new:911874988 // new:812856603 // Thread-0 has put data :834065409 // Thread-1 has put data :-212821024 // A from Thread-0 get data :834065409 // A from Thread-1 get data :-212821024 // B from Thread-1 get data :-212821024 // B from Thread-0 get data :834065409 } static class A{ public void get(){ int data = threadData.get(Thread.currentThread()); System.out.println("A from " + Thread.currentThread().getName() + " get data :" + data); } } static class B{ public void get(){ int data = threadData.get(Thread.currentThread()); System.out.println("B from " + Thread.currentThread().getName() + " get data :" + data); } } //********************上面应该可以看出来全局变量的线程安全问题。******************** private int dataInt = 0; private void test41(){ dataInt = new Random().nextInt(); System.out.println("old:" + dataInt); System.out.println("new:" + new Random().nextInt()); System.out.println(Thread.currentThread().getName() + " has put data :" + dataInt); threadData.put(Thread.currentThread(), dataInt); new C().get(); new D().get(); } private static void test42(){ for(int i=0;i<2;i++){ new Thread(new Runnable(){ @Override public void run() { new ThreadScopeShareData().test41(); } }).start(); } // 打印: // old:-1905454578 // old:1110237950 // new:629927948 // new:1149015511 // Thread-0 has put data :-1905454578 // Thread-1 has put data :1110237950 // A from Thread-0 get data :-1905454578 // A from Thread-1 get data :1110237950 // B from Thread-0 get data :-1905454578 // B from Thread-1 get data :1110237950 } static class C{ public void get(){ int data = threadData.get(Thread.currentThread()); System.out.println("A from " + Thread.currentThread().getName() + " get data :" + data); } } static class D{ public void get(){ int data = threadData.get(Thread.currentThread()); System.out.println("B from " + Thread.currentThread().getName() + " get data :" + data); } } //********************上面表明全局变量,非静态,在多个新建对象里面还是安全的,不过,不是废话么?非static每次都会随之新建的。但是你要是data改成static,无论你再怎么new出来新对象,也是无事于补的,其实想想也是废话,因为static加载一次。但是,当你用的时候,偏偏有些人就忘了这个问题。******************** }
package com.partner4java.itcast.util.threadlocal; import java.util.Random; /** * ThreadLocal实现线程范围的共享变量<br/> * ThreadLocal的作用和目的:用于实现线程内的数据共享,即对于相同的程序代码, * 多个模块在同一个线程中运行时要共享一份数据,而在另外线程中运行时又共享另外一份数据。<br/> * 每个线程调用全局ThreadLocal对象的set方法,就相当于往其内部的map中增加一条记录, * key分别是各自的线程,value是各自的set方法传进去的值。在线程结束时可以调用ThreadLocal.clear()方法, * 这样会更快释放内存,不调用也可以,因为线程结束后也可以自动释放相关的ThreadLocal变量。<br/> * * <b>ThreadLocal的应用场景:</b><br/> * 订单处理包含一系列操作:减少库存量、增加一条流水台账、修改总账,这几个操作要在同一个事务中完成, * 通常也即同一个线程中进行处理,如果累加公司应收款的操作失败了,则应该把前面的操作回滚, * 否则,提交所有操作,这要求这些操作使用相同的数据库连接对象,而这些操作的代码分别位于不同的模块类中。<br/> * 银行转账包含一系列操作: 把转出帐户的余额减少,把转入帐户的余额增加,这两个操作要在同一个事务中完成, * 它们必须使用相同的数据库连接对象,转入和转出操作的代码分别是两个不同的帐户对象的方法。<br/> * 例如Strut2的ActionContext,同一段代码被不同的线程调用运行时,该代码操作的数据是每个线程各自的状态和数据, * 对于不同的线程来说,getContext方法拿到的对象都不相同,对同一个线程来说, * 不管调用getContext方法多少次和在哪个模块中getContext方法,拿到的都是同一个。<br/> * <br/> * * 实验案例:定义一个全局共享的ThreadLocal变量,然后启动多个线程向该ThreadLocal变量中存储一个随机值, * 接着各个线程调用另外其他多个类的方法,这多个类的方法中读取这个ThreadLocal变量的值,就可以看到多个类在同一个线程中共享同一份数据。<br/> * <br/> * * 实现对ThreadLocal变量的封装,让外界不要直接操作ThreadLocal变量。<br/> * 对基本类型的数据的封装,这种应用相对很少见。<br/> * 对对象类型的数据的封装,比较常见,即让某个类针对不同线程分别创建一个独立的实例对象。<br/> * @author partner4java * */ public class ThreadLocalTest { private static ThreadLocal<Integer> x = new ThreadLocal<Integer>(); private static ThreadLocal<MyThreadScopeData> myThreadScopeData = new ThreadLocal<MyThreadScopeData>(); public static void main(String[] args) { for(int i=0;i<2;i++){ new Thread(new Runnable(){ @Override public void run() { int data = new Random().nextInt(); System.out.println(Thread.currentThread().getName() + " has put data :" + data); x.set(data); /* MyThreadScopeData myData = new MyThreadScopeData(); myData.setName("name" + data); myData.setAge(data); myThreadScopeData.set(myData);*/ MyThreadScopeData.getThreadInstance().setName("name" + data); MyThreadScopeData.getThreadInstance().setAge(data); new A().get(); new B().get(); } }).start(); } } static class A{ public void get(){ int data = x.get(); System.out.println("A from " + Thread.currentThread().getName() + " get data :" + data); /* MyThreadScopeData myData = myThreadScopeData.get();; System.out.println("A from " + Thread.currentThread().getName() + " getMyData: " + myData.getName() + "," + myData.getAge());*/ MyThreadScopeData myData = MyThreadScopeData.getThreadInstance(); System.out.println("A from " + Thread.currentThread().getName() + " getMyData: " + myData.getName() + "," + myData.getAge()); } } static class B{ public void get(){ int data = x.get(); System.out.println("B from " + Thread.currentThread().getName() + " get data :" + data); MyThreadScopeData myData = MyThreadScopeData.getThreadInstance(); System.out.println("B from " + Thread.currentThread().getName() + " getMyData: " + myData.getName() + "," + myData.getAge()); } } } class MyThreadScopeData{ private MyThreadScopeData(){} public static /*synchronized*/ MyThreadScopeData getThreadInstance(){ MyThreadScopeData instance = map.get(); if(instance == null){ instance = new MyThreadScopeData(); map.set(instance); } return instance; } //private static MyThreadScopeData instance = null;//new MyThreadScopeData(); private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>(); private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
package com.partner4java.itcast.java5; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** * 线程池<br/> * * 线程池的概念与Executors类的应用:<br/> * 创建固定大小的线程池<br/> * 创建缓存线程池<br/> * 创建单一线程池<br/> * <br/> * 关闭线程池:shutdown与shutdownNow的比较<br/> * <br/> * 用线程池启动定时器:<br/> * 调用ScheduledExecutorService的schedule方法,返回的ScheduleFuture对象可以取消任务。<br/> * 支持间隔重复任务的定时方式,不直接支持绝对定时方式,需要转换成相对时间方式。 * @author partner4java * */ public class ThreadPoolTest { public static void main(String[] args) { ExecutorService threadPool = Executors.newCachedThreadPool();//创建缓存线程池 //Executors.newFixedThreadPool(2);//创建固定大小的线程池 //Executors.newSingleThreadExecutor();//创建单一线程池 for(int i=1;i<=10;i++){ final int task = i; threadPool.execute(new Runnable() { @Override public void run() { System.out.println("currentThreadName:" + Thread.currentThread().getName() + " ; i=" + task); } }); } System.out.println("all of 10 tasks have committed! "); // threadPool.shutdown(); // threadPool.shutdownNow(); Executors.newScheduledThreadPool(2).scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println("scheduleAtFixedRate-bombing!"); } }, 3, 2, TimeUnit.SECONDS); } } //下面给出了一个网络服务的简单结构,这里线程池中的线程作为传入的请求。它使用了预先配置的 Executors.newFixedThreadPool(int) 工厂方法: //class NetworkService { // private final ServerSocket serverSocket; // private final ExecutorService pool; // // public NetworkService(int port, int poolSize) throws IOException { // serverSocket = new ServerSocket(port); // pool = Executors.newFixedThreadPool(poolSize); // } // // public void serve() { // try { // for (;;) { // pool.execute(new Handler(serverSocket.accept())); // } // } catch (IOException ex) { // pool.shutdown(); // } // } // } // // class Handler implements Runnable { // private final Socket socket; // Handler(Socket socket) { this.socket = socket; } // public void run() { // // read and service request // } //}
package com.partner4java.itcast.java5; import java.util.concurrent.Callable; import java.util.concurrent.CompletionService; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * Callable&Future<br/> * Future取得的结果类型和Callable返回的结果类型必须一致,这是通过泛型来实现的。<br/> * <br/> * Callable要采用ExecutorSevice的submit方法提交,返回的future对象可以取消任务。<br/> * <br/> * CompletionService用于提交一组Callable任务,其take方法返回已完成的一个Callable任务对应的Future对象。<br/> * * @author partner4java * */ public class CallableAndFuture { public static void main(String[] args) { testFuture(); testCompletionService(); } private static void testFuture() { ExecutorService threadPool = Executors.newSingleThreadExecutor(); Future<String> future = threadPool.submit(new Callable<String>() { @Override public String call() throws Exception { Thread.sleep(2000); return "Hello World!"; } }); System.out.println("等待结果-testFuture"); try { System.out.println("拿到结果:" + future.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } private static void testCompletionService() { ExecutorService threadPools = Executors.newFixedThreadPool(2); CompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(threadPools); for(int i=1;i<=10;i++){ final int seq = i; completionService.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { Thread.sleep(1000); return seq; } }); } System.out.println("等待结果-testCompletionService"); for(int i =0;i<10;i++){ try { System.out.println("拿到结果:" + completionService.take().get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } } } //Future: // Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。计算完成后只能使用 get 方法来检索结果,如有必要,计算完成前可以阻塞此方法。取消则由 cancel 方法来执行。还提供了其他方法,以确定任务是正常完成还是被取消了。一旦计算完成,就不能再取消计算。如果为了可取消性而使用 Future但又不提供可用的结果,则可以声明 Future<?> 形式类型、并返回 null 作为基础任务的结果。 // // 用法示例(注意,下列各类都是构造好的。) // // // interface ArchiveSearcher { String search(String target); } // class App { // ExecutorService executor = ... // ArchiveSearcher searcher = ... // void showSearch(final String target) throws InterruptedException { // Future<String> future = executor.submit(new Callable<String>() { // public String call() { return searcher.search(target); } // }); // displayOtherThings(); // do other things while searching // try { // displayText(future.get()); // use future // } catch (ExecutionException ex) { cleanup(); return; } // } // } // FutureTask 类是 Future 的一个实现,Future 可实现 Runnable,所以可通过 Executor 来执行。例如,可用下列内容替换上面带有 submit 的构造: // FutureTask<String> future = // new FutureTask<String>(new Callable<String>() { // public String call() { // return searcher.search(target); // }}); // executor.execute(future); //CompletionService: // 将生产新的异步任务与使用已完成任务的结果分离开来的服务。生产者 submit 执行的任务。使用者 take 已完成的任务,并按照完成这些任务的顺序处理它们的结果。例如,CompletionService 可以用来管理异步 IO ,执行读操作的任务作为程序或系统的一部分提交,然后,当完成读操作时,会在程序的不同部分执行其他操作,执行操作的顺序可能与所请求的顺序不同。 // // 通常,CompletionService 依赖于一个单独的 Executor 来实际执行任务,在这种情况下,CompletionService 只管理一个内部完成队列。ExecutorCompletionService 类提供了此方法的一个实现。
package com.partner4java.itcast.java5; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象。两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象。 * @author partner4java * */ public class LockTest { public static void main(String[] args) { // test1(); test2(); } private static void test1() { final LockTest lockTest = new LockTest(); ExecutorService threadPool = Executors.newFixedThreadPool(2); for(int i=0;i<100;i++){ threadPool.execute(new Runnable() { @Override public void run() { lockTest.add1(); } }); } for(int i=0;i<100;i++){ threadPool.execute(new Runnable() { @Override public void run() { lockTest.reduce1(); } }); } threadPool.shutdown(); System.out.println("count:" + count); // 后台打印: // count:17 } private static int count = 0; public void add1(){ count++; } public void reduce1(){ count--; } private static void test2() { final LockTest lockTest = new LockTest(); ExecutorService threadPool = Executors.newFixedThreadPool(2); for(int i=0;i<100;i++){ threadPool.execute(new Runnable() { @Override public void run() { lockTest.add2(); } }); } for(int i=0;i<100;i++){ threadPool.execute(new Runnable() { @Override public void run() { lockTest.reduce2(); } }); } threadPool.shutdown(); System.out.println("count:" + count2); // 后台打印: // count:0 } private static int count2 = 0; Lock lock = new ReentrantLock(); public void add2(){ try { lock.lock(); count++; } catch (Exception e) { e.printStackTrace(); }finally{ lock.unlock(); } } public void reduce2(){ count--; } } //Lock: // public interface LockLock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。 // // 锁定是控制多个线程对共享资源进行访问的工具。通常,锁定提供了对共享资源的独占访问。一次只能有一个线程获得锁定,对共享资源的所有访问都需要首先获得锁定。不过,某些锁定可能允许对共享资源并发访问,如 ReadWriteLock 的读取锁定。 // // synchronized 方法或语句的使用提供了对与每个对象相关的隐式监视器锁定的访问,但却强制所有锁定获取和释放均要出现在一个块结构中:当获取了多个锁定时,它们必须以相反的顺序释放,且必须在与所有锁定被获取时相同的词法范围内释放所有锁定。 // // 虽然 synchronized 方法和语句的范围机制使得使用监视器锁定编程方便了很多,而且还帮助避免了很多涉及到锁定的常见编程错误,但有时也需要以更为灵活的方式使用锁定。例如,某些遍历并发访问的数据结果的算法要求使用 "hand-over-hand" 或 "chain locking":获取节点 A 的锁定,然后再获取节点 B 的锁定,然后释放 A 并获取 C,然后释放 B 并获取 D,依此类推。Lock 接口的实现允许锁定在不同的作用范围内获取和释放,并允许以任何顺序获取和释放多个锁定,从而支持使用这种技术。 // // 随着灵活性的增加,也带来了更多的责任。不使用块结构锁定就失去了使用 synchronized 方法和语句时会出现的锁定自动释放功能。在大多数情况下,应该使用以下语句: // // Lock l = ...; // l.lock(); // try { // // access the resource protected by this lock // } finally { // l.unlock(); // } // 锁定和取消锁定出现在不同作用范围中时,必须谨慎地确保保持锁定时所执行的所有代码用 try-finally 或 try-catch 加以保护,以确保在必要时释放锁定。 // Lock 实现提供了使用 synchronized 方法和语句所没有的其他功能,包括提供了一个非块结构的获取锁定尝试 (tryLock())、一个获取可中断锁定的尝试 (lockInterruptibly()) 和一个获取超时失效锁定的尝试 (tryLock(long, TimeUnit))。 // // Lock 类还可以提供与隐式监视器锁定完全不同的行为和语义,如保证排序、非重入用法或死锁检测。如果某个实现提供了这样特殊的语义,则该实现必须对这些语义加以记录。 // // 注意,Lock 实例只是普通的对象,其本身可以在 synchronized 语句中作为目标使用。获取 Lock 实例的监视器锁定与调用该实例的任何 lock() 方法没有特别的关系。为了避免混淆,建议除了在其自身的实现中之外,决不要以这种方式使用 Lock 实例。 // // 除非另有说明,否则为任何参数传递 null 值都将导致抛出 NullPointerException。 // // 内存同步 // 所有 Lock 实现都必须 实施与内置监视器锁定提供的相同内存同步语义: // // 成功的 lock 操作与成功的 monitorEnter 操作类似 // 而成功的 unlock 操作则与成功的 monitorExit 操作类似 // 不成功的锁定与取消锁定操作以及重入锁定/取消锁定操作都不需要任何内存同步效果。 // 实现注意事项 // 三种形式的锁定获取(可中断、不可中断和定时)在其性能特征、排序保证或其他实现质量上可能会有所不同。而且,对于给定的 Lock 类,可能没有中断正在进行的 锁定获取的能力。因此,并不要求实现为所有三种形式的锁定获取定义相同的保证或语义,也不要求其支持中断正在进行的锁定获取。实现必需清楚地对每个锁定方法所提供的语义和保证进行记录。还必须遵守此接口中定义的中断语义,以便为锁定获取中断提供支持:完全支持中断,或仅在进入方法时支持中断。 // // 由于中断通常意味着取消,而通常又很少进行中断检查,因此,相对于普通方法返回而言,实现可能更喜欢响应某个中断。即使出现在另一个操作后的中断可能会释放线程锁定时也是如此。实现应记录此行为。 // //ReentrantLock: // public class ReentrantLockextends Objectimplements Lock, Serializable一个可重入的互斥锁定 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁定相同的一些基本行为和语义,但功能更强大。 // // ReentrantLock 将由最近成功获得锁定,并且还没有释放该锁定的线程所拥有。当锁定没有被另一个线程所拥有时,调用 lock 的线程将成功获取该锁定并返回。如果当前线程已经拥有该锁定,此方法将立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法来检查此情况是否发生。 // // 此类的构造方法接受一个可选的公平 参数。当设置为 true 时,在多个线程的争用下,这些锁定倾向于将访问权授予等待时间最长的线程。否则此锁定将无法保证任何特定访问顺序。与采用默认设置(使用不公平锁定)相比,使用公平锁定的程序在许多线程访问时表现为很低的总体吞吐量(即速度很慢,常常极其慢),但是在获得锁定和保证锁定分配的均衡性时差异较小。不过要注意的是,公平锁定不能保证线程调度的公平性。因此,使用公平锁定的众多线程中的一员可能获得多倍的成功机会,这种情况发生在其他活动线程没有被处理并且目前并未持有锁定时。还要注意的是,未定时的 tryLock 方法并没有使用公平设置。因为即使其他线程正在等待,只要该锁定是可用的,此方法就可以获得成功。 // // 建议总是 立即实践,使用 try 块来调用 lock,在之前/之后的构造中,最典型的代码如下: // // class X { // private final ReentrantLock lock = new ReentrantLock(); // // ... // // public void m() { // lock.lock(); // block until condition holds // try { // // ... method body // } finally { // lock.unlock() // } // } // } // 除了实现 Lock 接口,此类还定义了 isLocked 和 getLockQueueLength 方法,以及一些相关的 protected 访问方法,这些方法对检测和监视可能很有用。 // // 该类的序列化与内置锁定的行为方式相同:一个反序列化的锁定处于解除锁定状态,不管它被序列化时的状态是怎样的。 // // 此锁定最多支持同一个线程发起的 2147483648 个递归锁定。
package com.partner4java.itcast.java5; import java.util.Random; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * 读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁! * @author partner4java * */ public class ReadWriteLockTest { public static void main(String[] args) { final Queue3 q3 = new Queue3(); for(int i=0;i<3;i++) { new Thread(){ public void run(){ while(true){ q3.get(); } } }.start(); new Thread(){ public void run(){ while(true){ q3.put(new Random().nextInt(10000)); } } }.start(); } } } class Queue3{ private Object data = null;//共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。 ReadWriteLock rwl = new ReentrantReadWriteLock(); public void get(){ rwl.readLock().lock(); try { System.out.println(Thread.currentThread().getName() + " be ready to read data!"); Thread.sleep((long)(Math.random()*1000)); System.out.println(Thread.currentThread().getName() + "have read data :" + data); } catch (InterruptedException e) { e.printStackTrace(); }finally{ rwl.readLock().unlock(); } } public void put(Object data){ rwl.writeLock().lock(); try { System.out.println(Thread.currentThread().getName() + " be ready to write data!"); Thread.sleep((long)(Math.random()*1000)); this.data = data; System.out.println(Thread.currentThread().getName() + " have write data: " + data); } catch (InterruptedException e) { e.printStackTrace(); }finally{ rwl.writeLock().unlock(); } } } //ReadWriteLock: //public interface ReadWriteLockReadWriteLock 维护了一对相关的锁定,一个用于只读操作,另一个用于写入操作。只要没有 writer,读取锁定可以由多个 reader 线程同时保持。写如锁定是独占的。 // //与互斥锁定相比,读-写锁定允许对共享数据进行更高级别的并发访问。虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程),读-写锁定利用了这一点。从理论上讲,与互斥锁定相比,使用读-写锁定所允许的并发性增强将带来更大的性能提高。在实践中,只有在多处理器上并且只在访问模式适用于共享数据时,才能完全实现并发性增强。 // //与互斥锁定相比,使用读-写锁定能否提升性能则取决于读写操作期间读取数据相对于修改数据的频率,以及数据的争用——即在同一时间试图对该数据执行读取或写入操作的线程数。例如,某个最初用数据填充并且之后不经常对其进行修改的 collection,因为经常对其进行搜索(比如搜索某种目录),所以这样的 collection 是使用读-写锁定的理想候选者。但是,如果数据更新变得频繁,数据在大部分时间都被独占锁定,这时,就算存在并发性增强,也是微不足道的。更进一步地说,如果读取操作所用时间太短,则读-写锁定实现(它本身就比互斥锁定复杂)的开销将成为主要的执行成本,在许多读-写锁定实现仍然通过一小段代码将所有线程序列化时更是如此。最终,只有通过分析和测量,才能确定应用程序是否适合使用读-写锁定。 // //尽管读-写锁定的基本操作是直截了当的,但实现仍然必须作出许多决策,这些决策可能会影响给定应用程序中读-写锁定的效果。这些策略的例子包括: // //在 writer 释放写入锁定时,reader 和 writer 都处于等待状态,在这时要确定是授予读取锁定还是授予写入锁定。Writer 优先比较普遍,因为预期写入所需的时间较短并且不那么频繁。Reader 优先不太普遍,因为如果 reader 正如预期的那样频繁和持久,那么它将导致对于写入操作来说较长的时延。公平或者“按次序”实现也是有可能的。 //在 reader 处于活动状态而 writer 处于等待状态时,确定是否向请求读取锁定的 reader 授予读取锁定。Reader 优先会无限期地延迟 writer,而 writer 优先会减少可能的并发。 //确定是否重新进入锁定:可以使用带有写入锁定的线程重新获取它吗?可以在保持写入锁定的同时获取读取锁定吗?可以重新进入写入锁定本身吗? //可以将写入锁定在不允许其他 writer 干涉的情况下降级为读取锁定吗?可以优先于其他等待的 reader 或 writer 将读取锁定升级为写入锁定吗? //当评估给定实现是否适合您的应用程序时,应该考虑所有这些情况。
package com.partner4java.itcast.java5; 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; /** * 在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。 对于大多数应用程序,这带来的实际影响很小,因为 Condition * 应该总是在一个循环中被等待, 并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒, * 但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。<br/> * 一个锁内部可以有多个Condition,即有多路等待和通知, 可以参看jdk1.5提供的Lock与Condition实现的可阻塞队列的应用案例, * 从中除了要体味算法,还要体味面向对象的封装。在传统的线程机制中一个监视器对象上只能有一路等待和通知, * 要想实现多路等待和通知,必须嵌套使用多个同步监视器对象。 * (如果只用一个Condition,两个放的都在等,一旦一个放的进去了,那么它通知可能会导致另一个放接着往下走。) * * @author partner4java * */ public class ConditionCommunication { public static void main(String[] args) { // test1(); test2(); } /** * 这样会让吃饭和唱歌“同时进行”,那么就会喷饭 */ private static void test1() { final ConditionTest communication = new ConditionTest(); ExecutorService executorService = Executors.newFixedThreadPool(2); executorService.execute(new Runnable() { @Override public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 20; i++) { communication.eat1(); } } }); executorService.execute(new Runnable() { @Override public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } for (int i = 0; i < 20; i++) { communication.sing1(); } } }); executorService.shutdown(); } // ----------------------------------- /** * 这样该吃饭吃饭,该唱歌唱歌 */ private static void test2() { final ConditionTest communication = new ConditionTest(); // ExecutorService executorService = Executors.newFixedThreadPool(2); // executorService.execute(new Runnable() { // @Override // public void run() { // for (int i = 0; i < 20; i++) { // communication.eat4(); // } // } // }); // executorService.execute(new Runnable() { // @Override // public void run() { // for (int i = 0; i < 20; i++) { // communication.sing3(); // } // } // }); // executorService.shutdown(); new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 20; i++) { communication.sing3(); } } }).start(); for (int i = 0; i < 20; i++) { communication.eat4(); } } } class ConditionTest{ public void sing1() { for (int i = 0; i < 10; i++) { System.out.println("sing1 " + i); } } public void eat1() { for (int i = 0; i < 10; i++) { System.out.println("eat1 " + i); } } final Lock lock = new ReentrantLock(); final Condition conditionSing = lock.newCondition(); final Condition conditionEat = lock.newCondition(); private boolean isSing = true; public void sing3() { lock.lock(); try { while (!isSing) { conditionSing.await(); } for (int i = 0; i < 10; i++) { System.out.println("sing1 " + i); } isSing = false; // conditionEat.notify(); conditionEat.signal(); // conditionSing.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void eat4() { lock.lock(); try { while (isSing) { conditionEat.await();//FUCK,刚才笔误写成wait了,报错"java.lang.IllegalMonitorStateException",找了半天没错啊,天杀的笔误 // conditionSing.await(); } for (int i = 0; i < 10; i++) { System.out.println("eat1 " + i); } isSing = true; // conditionSing.notify(); conditionSing.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } } // Condition: // public interface ConditionCondition 将 Object 监视器方法(wait、notify 和 // notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set // (wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。 // // 条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true // 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁定与该条件相关联。等待提供一个条件的主要属性是:以原子方式 // 释放相关的锁定,并挂起当前线程,就像 Object.wait 做的那样。 // // Condition 实例实质上被绑定到一个锁定上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。 // // 作为一个示例,假定有一个绑定的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行 take // 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行 put 操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待 // set 中保存 put 线程和 take 线程,这样就可以在缓冲区中的项或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个 Condition // 实例来做到这一点。 // // class BoundedBuffer { // final Lock lock = new ReentrantLock(); // final Condition notFull = lock.newCondition(); // final Condition notEmpty = lock.newCondition(); // // final Object[] items = new Object[100]; // int putptr, takeptr, count; // // public void put(Object x) throws InterruptedException { // lock.lock(); // try { // while (count == items.length) // notFull.await(); // items[putptr] = x; // if (++putptr == items.length) putptr = 0; // ++count; // notEmpty.signal(); // } finally { // lock.unlock(); // } // } // // public Object take() throws InterruptedException { // lock.lock(); // try { // while (count == 0) // notEmpty.await(); // Object x = items[takeptr]; // if (++takeptr == items.length) takeptr = 0; // --count; // notFull.signal(); // return x; // } finally { // lock.unlock(); // } // } // } // (ArrayBlockingQueue 类提供了这项功能,因此没有理由去实现这个示例类。) // Condition 实现可以提供不同于 Object // 监视器方法的行为和语义,比如受保证的通知排序,或者在执行通知时不需要保持一个锁定。如果某个实现提供了这样特殊的语义,则该实现必须记录这些语义。 // // 注意,Condition 实例只是一些普通的对象,它们自身可以用作 synchronized 语句中的目标,并且可以调用自己的 wait 和 // notification 监视器方法。获取 Condition 实例的监视器锁定或者使用其监视器方法,与获取和该 Condition 相关的 Lock // 或使用其 waiting 和 signalling 方法没有什么特定的关系。为了避免混淆,建议除了在其自身的实现中之外,切勿以这种方式使用 // Condition 实例。 // // 除非另行说明,否则为任何参数传递 null 值将导致抛出 NullPointerException。 // // 实现注意事项 // 在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition // 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。 // // 三种形式的条件等待(可中断、不可中断和超时)在一些平台上的实现以及它们的性能特征可能会有所不同。尤其是它可能很难提供这些特性和维护特定语义,比如排序保证。更进一步地说,中断线程实际挂起的能力在所有平台上并不是总是可行的。 // // 因此,并不要求某个实现为所有三种形式的等待定义完全相同的保证或语义,也不要求其支持中断线程的实际挂起。 // // 要求实现清楚地记录每个等待方法提供的语义和保证,在某个实现不支持中断线程的挂起时,它必须遵从此接口中定义的中断语义。 // // 由于中断通常意味着取消,而又通常很少进行中断检查,因此实现可以先于普通方法的返回来对中断进行响应。即使出现在另一个操作后的中断可能会释放线程锁定时也是如此。实现应记录此行为。
package com.partner4java.itcast.java5; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; /** * Semaphore实现信号灯<br/> * <br/> * Semaphore可以维护当前访问自身的线程个数,并提供了同步机制。 * 使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。<br/> * ·Semaphore实现的功能就类似厕所有5个坑,假如有十个人要上厕所, * 那么同时能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中的任何一个人让开后, 其中在等待的另外5个人中又有一个可以占用了。<br/> * ·另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会, 这取决于构造Semaphore对象时传入的参数选项。<br/> * <br/> * 单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”, 再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。 * * @author partner4java * */ public class SemaphoreTest { public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); final Semaphore semaphore = new Semaphore(3); for (int i = 0; i < 10; i++) { Runnable runnable = new Runnable() { @Override public void run() { try { semaphore.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程" + Thread.currentThread().getName() + "进入,当前已有" + (3-semaphore.availablePermits()) + "个并发"); try { Thread.sleep((long)(Math.random()*10000)); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程" + Thread.currentThread().getName() + "即将离开"); semaphore.release(); // 下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元 System.out.println("线程" + Thread.currentThread().getName() + "已离开,当前已有" + (3 - semaphore.availablePermits()) + "个并发"); System.out.println(); } }; executorService.execute(runnable); } } }
package com.partner4java.itcast.java5; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * CyclicBarrier<br/> * 表示大家彼此等待,大家集合好后才开始出发,分散活动后又在指定地点集合碰面, 这就好比整个公司的人员利用周末时间集体郊游一样,先各自从家出发到公司集合后, * 再同时出发到公园游玩,在指定地点集合后再同时开始就餐,…。 * @author partner4java * */ public class CyclicBarrierTest { public static void main(String[] args) { ExecutorService service = Executors.newCachedThreadPool(); final CyclicBarrier cb = new CyclicBarrier(3); for (int i = 0; i < 3; i++) { Runnable runnable = new Runnable() { public void run() { try { Thread.sleep((long) (Math.random() * 10000)); System.out.println("线程" + Thread.currentThread().getName() + "即将到达集合地点1,当前已有" + (cb.getNumberWaiting() + 1) + "个已经到达," + (cb.getNumberWaiting() == 2 ? "都到齐了,继续走啊" : "正在等候")); cb.await(); Thread.sleep((long) (Math.random() * 10000)); System.out.println("线程" + Thread.currentThread().getName() + "即将到达集合地点2,当前已有" + (cb.getNumberWaiting() + 1) + "个已经到达," + (cb.getNumberWaiting() == 2 ? "都到齐了,继续走啊" : "正在等候")); cb.await(); Thread.sleep((long) (Math.random() * 10000)); System.out.println("线程" + Thread.currentThread().getName() + "即将到达集合地点3,当前已有" + (cb.getNumberWaiting() + 1) + "个已经到达," + (cb.getNumberWaiting() == 2 ? "都到齐了,继续走啊" : "正在等候")); cb.await(); } catch (Exception e) { e.printStackTrace(); } } }; service.execute(runnable); } service.shutdown(); } }
package com.partner4java.itcast.java5; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * CountDownLatch<br/> * 犹如倒计时计数器,调用CountDownLatch对象的countDown方法就将计数器减1, * 当计数到达0时,则所有等待者或单个等待者开始执行。 * 这直接通过代码来说明CountDownLatch的作用,这样学员的理解效果更直接。<br/> * 可以实现一个人(也可以是多个人)等待其他所有人都来通知他, * 这犹如一个计划需要多个领导都签字后才能继续向下实施。 * 还可以实现一个人通知多个人的效果,类似裁判一声口令, * 运动员同时开始奔跑。用这个功能做百米赛跑的游戏程序不错哦! * @author partner4java * */ public class CountdownLatchTest { public static void main(String[] args) { test1(); } /** * demo,模拟给宝宝喂饭,喂一下,宝宝要嚼三下才会咽。 */ private static void test1() { ExecutorService executorService = Executors.newFixedThreadPool(3); while(true){ final CountDownLatch chew = new CountDownLatch(3); final CountDownLatch fed = new CountDownLatch(1); for(int i=1;i<=3;i++){ final int count = i; executorService.execute(new Runnable() { @Override public void run() { try { if(count == 1){ System.out.println("宝宝张嘴等着勺子"); } fed.await(); System.out.println("宝宝开始咀嚼"); chew.countDown(); if(chew.getCount() == 0){ System.out.println("宝宝吃完了"); } } catch (Exception e) { e.printStackTrace(); } } }); } try { Thread.sleep(5000); System.out.println(); System.out.println("喂饭"); fed.countDown(); chew.await(); } catch (InterruptedException e) { e.printStackTrace(); } } } }
package com.partner4java.itcast.java5; import java.util.concurrent.Exchanger; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Exchanger<br/> * 用于实现两个人之间的数据交换,每个人在完成一定的事务后想与对方交换数据, * 第一个先拿出数据的人将一直等待第二个人拿着数据到来时,才能彼此交换数据。 * @author partner4java * */ public class ExchangerTest { public static void main(String[] args) { test1(); } /** * 给你讲解一下什么是爱情 */ private static void test1() { ExecutorService executorService = Executors.newFixedThreadPool(2); final Exchanger<String> exchanger = new Exchanger<String>(); final ExchangerTest exchangerTest = new ExchangerTest(); executorService.execute(new Runnable() { @Override public void run() { try { exchangerTest.boy(exchanger); } catch (InterruptedException e) { e.printStackTrace(); } } }); executorService.execute(new Runnable() { @Override public void run() { try { exchangerTest.girl(exchanger); } catch (InterruptedException e) { e.printStackTrace(); } } }); executorService.shutdown(); } /** * 等待爱情的寂寞男孩 * * @param exchanger * @throws InterruptedException */ private static void boy(Exchanger<String> exchanger) throws InterruptedException { System.out.println("拼命赚钱,拼命的追女孩子"); Thread.sleep(2000); System.out.println("还是一直追不到,他没发现他没钱!只要一颗心是不够的!"); System.out.println("继续赚钱!"); Thread.sleep(5000); System.out.println("赚了很多钱!开始交换了!"); Thread.sleep(2000); String dream = exchanger.exchange("信用卡!"); System.out.println("金钱换到了:" + dream); } /** * 天天去做SPA的人造美女 * @param exchanger * @throws InterruptedException */ private static void girl(Exchanger<String> exchanger) throws InterruptedException { System.out.println("美女在做SPA"); System.out.println("一直在等待那个结账的人"); String dream = exchanger.exchange("肉-100斤美女的肉!"); System.out.println("肉体换到了:" + dream); } } //总结:这个程序虽然简单,但实实在在解决了一种应用问题, //这就是新技术带来的价值,新技术的价值就犹如一个亿万富豪得了不治之症, //目前没有什么药品可以医治,你发现了一种新药专门治这种病,你对他说, //把他的亿万家产给你,你就把药片给他,你说他干不干?他绝对会干! //这就是新药和新技术的威力嘛!新技术在关键时刻总能发挥特殊的作用, //就看你遇到没遇到这种关键的时刻,一旦遇到,那就能产生很大价值了。
package com.partner4java.itcast.java5; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; /** * BlockingQueue<br/> * 阻塞队列与Semaphore有些相似,但也不同,阻塞队列是一方存放数据, * 另一方释放数据,Semaphore通常则是由同一方设置和释放信号量。 * @author partner4java * */ public class BlockingQueueTest { public static void main(String[] args) { final BlockingQueue queue = new ArrayBlockingQueue(3); for(int i=0;i<2;i++){ new Thread(){ public void run(){ while(true){ try { Thread.sleep((long)(Math.random()*1000)); System.out.println(Thread.currentThread().getName() + "准备放数据!"); queue.put(1); System.out.println(Thread.currentThread().getName() + "已经放了数据," + "队列目前有" + queue.size() + "个数据"); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } new Thread(){ public void run(){ while(true){ try { //将此处的睡眠时间分别改为100和1000,观察运行结果 Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "准备取数据!"); queue.take(); System.out.println(Thread.currentThread().getName() + "已经取走数据," + "队列目前有" + queue.size() + "个数据"); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } } //public interface BlockingQueue<E>extends Queue<E>支持两个附加操作的 Queue,这两个操作是:检索元素时等待队列变为非空,以及存储元素时等待空间变得可用。 // //BlockingQueue 不接受 null 元素。试图 add、put 或 offer 一个 null 元素时,某些实现会抛出 NullPointerException。null 被用作指示 poll 操作失败的警戒值。 // //BlockingQueue 可以是限定容量的。它在任意给定时间都可以有一个 remainingCapacity,超出此容量,便无法无阻塞地 put 额外的元素。没有任何内部容量约束的 BlockingQueue 总是报告 Integer.MAX_VALUE 的剩余容量。 // //BlockingQueue 实现主要用于生产者-使用者队列,但它另外还支持 Collection 接口。因此,举例来说,使用 remove(x) 从队列中移除任意一个元素是有可能的。然而,这种操作通常不 会有效执行,只能有计划地偶尔使用,比如在取消排队信息时。 // //BlockingQueue 实现是线程安全的。所有排队方法都可以使用内部锁定或其他形式的并发控制来自动达到它们的目的。然而,大量的 Collection 操作(addAll、containsAll、retainAll 和 removeAll)没有 必要自动执行,除非在实现中特别说明。因此,举例来说,在只添加了 c 中的一些元素后,addAll(c) 有可能失败(抛出一个异常)。 // //BlockingQueue 实质上不 支持使用任何一种“close”或“shutdown”操作来指示不再添加任何项。这种功能的需求和使用有依赖于实现的倾向。例如,一种常用的策略是:对于生产者,插入特殊的 end-of-stream 或 poison 对象,并根据使用者获取这些对象的时间来对它们进行解释。 // //以下是基于典型的生产者-使用者场景的一个用例。注意,BlockingQueue 可以安全地与多个生产者和多个使用者一起使用。 // // class Producer implements Runnable { // private final BlockingQueue queue; // Producer(BlockingQueue q) { queue = q; } // public void run() { // try { // while(true) { queue.put(produce()); } // } catch (InterruptedException ex) { ... handle ...} // } // Object produce() { ... } // } // // class Consumer implements Runnable { // private final BlockingQueue queue; // Consumer(BlockingQueue q) { queue = q; } // public void run() { // try { // while(true) { consume(queue.take()); } // } catch (InterruptedException ex) { ... handle ...} // } // void consume(Object x) { ... } // } // // class Setup { // void main() { // BlockingQueue q = new SomeQueueImplementation(); // Producer p = new Producer(q); // Consumer c1 = new Consumer(q); // Consumer c2 = new Consumer(q); // new Thread(p).start(); // new Thread(c1).start(); // new Thread(c2).start(); // } // } // 此接口是 Java Collections Framework 的成员。
package com.partner4java.itcast.java5; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.concurrent.CopyOnWriteArrayList; /** * 同步集合<br/> * 传统方式下用Collections工具类提供的synchronizedCollection方法来获得同步集合, * 分析该方法的实现源码。传统方式下的Collection在迭代集合时,不允许对集合进行修改。<br/> * <br/> * <b>并发 Collection</b><br/> * 除队列外,此包还提供了几个设计用于多线程上下文中的 Collection 实现: * ConcurrentHashMap、CopyOnWriteArrayList 和 CopyOnWriteArraySet。 <br/> * <br/> * 此包中与某些类一起使用的“Concurrent&rdquo前缀;是一种简写,表明与类似的“同步”类有所不同。 * 例如,java.util.Hashtable 和 Collections.synchronizedMap(new HashMap()) 是同步的, * 但 ConcurrentHashMap 则是“并发的”。并发集合是线程安全的,但是不受单个排他锁定的管理。 * 在 ConcurrentHashMap 这一特定情况下,它可以安全地允许进行任意数目的并发读取, * 以及数目可调的并发写入。需要通过单个锁定阻止对集合的所有访问时,“同步”类是很有用的, * 其代价是较差的可伸缩性。在期望多个线程访问公共集合的其他情况中,通常“并发”版本要更好一些。 * 当集合是未共享的,或者仅保持其他锁定时集合是可访问的情况下,非同步集合则要更好一些。 <br/> * <br/> * 大多数并发 Collection 实现(包括大多数 Queue)与常规的 java.util 约定也不同, * 因为它们的迭代器提供了弱一致的,而不是快速失败的遍历。弱一致的迭代器是线程安全的, * 但是在迭代时没有必要冻结集合,所以它不一定反映自迭代器创建以来的所有更新。 <br/> * @author partner4java * */ public class CollectionModifyExceptionTest { public static void main(String[] args) { Collection users = // new CopyOnWriteArrayList(); new ArrayList(); users.add(new User("张三",28)); users.add(new User("李四",25)); users.add(new User("王五",31)); Iterator itrUsers = users.iterator(); while(itrUsers.hasNext()){ System.out.println("aaaa"); User user = (User)itrUsers.next(); if("王五".equals(user.getName())){ users.remove(user); //itrUsers.remove(); } else { System.out.println(user); } } // 用ArrayList报错:在迭代集合的过程中,不能对集合进行修改(但是,修改李四没问题,因为,李四正好是第二个,游标没有继续) // public boolean hasNext() { // return cursor != size(); // } // aaaaException in thread "main" java.util.ConcurrentModificationException // at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372) // at java.util.AbstractList$Itr.next(AbstractList.java:343) // at cn.itcast.heima2.CollectionModifyExceptionTest.main(CollectionModifyExceptionTest.java:18) } }