1.多个线程之间共享数据的方式探讨
1、如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做。
2、如果每个线程执行的代码不同,这时候需要用不同的Runnable对象,有如下两种方式来实现这些Runnable对象之间的数据共享:
- 将共享数据封装在另外一个对象中,然后将这个对象逐一传递给各个Runnable对象。每个线程对共享数据的操作方法也分配到那个对象身上去完成,这样容易实现针对该数据进行的各个操作的互斥和通信。
- 将这些Runnable对象作为某一个类中的内部类,共享数据作为这个外部类中的成员变量,每个线程对共享数据的操作方法也分配给外部类,以便实现对共享数据进行的各个操作的互斥和通信,作为内部类的各个Runnable对象调用外部类的这些方法。
上面两种方式的组合:将共享数据封装在另外一个对象中,每个线程对共享数据的操作方法也分配到那个对象身上去完成,对象作为这个外部类中的成员变量或方法中的局部变量,每个线程的Runnable对象作为外部类中的成员内部类或局部内部类。
总之,要同步互斥的几段代码最好是分别放在几个独立的方法中,这些方法再放在同一个类中,这样比较容易实现它们之间的同步互斥和通信。
极端且简单的方式,即在任意一个类中定义一个static的变量,这将被所有线程共享。
示例代码
1 package com.chunjiangchao.thread; 2 /** 3 * 多线程之间数据共享 4 * @author chunjiangchao 5 * 6 */ 7 public class MultiThreadShareDataDemo { 8 9 public static void main(String[] args) { 10 Data data = new Data(); 11 new Thread(new IncrementRunnable(data)).start(); 12 new Thread(new DecrementtRunnable(data)).start(); 13 14 final Data data2 = new Data(); 15 new Thread(new Runnable() { 16 17 @Override 18 public void run() { 19 data2.increment(); 20 } 21 }).start(); 22 new Thread(new Runnable() { 23 24 @Override 25 public void run() { 26 data2.decrement(); 27 } 28 }).start(); 29 } 30 //对共享数据进行增加 31 private static class IncrementRunnable implements Runnable{ 32 private Data data ; 33 public IncrementRunnable(Data data){ 34 this.data = data; 35 } 36 public void run() { 37 data.increment(); 38 } 39 } 40 //对共享数据进行减少 41 private static class DecrementtRunnable implements Runnable{ 42 private Data data ; 43 public DecrementtRunnable(Data data){ 44 this.data = data; 45 } 46 public void run() { 47 data.decrement(); 48 } 49 } 50 51 52 //共享数据 53 private static class Data{ 54 private int temp=0; 55 public synchronized void increment(){ 56 temp++; 57 System.out.println(Thread.currentThread()+"中temp的值为:"+temp); 58 } 59 public synchronized void decrement(){ 60 temp--; 61 System.out.println(Thread.currentThread()+"中temp的值为:"+temp); 62 } 63 } 64 65 }
2.java5线程并发库的应用(Executors)
static ExecutorService newFixedThreadPool(int nThreads) 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。(创建固定线程池)
如果在这个线程池里面,创建的线程为3个线程,但是交给的任务时10个任务的话,那么,线程池里面的线程就会运行完3个线程后,接着运行3个线程,直到所有的线程运行完毕。
List<Runnable> shutdownNow()试图停止所有正在执行的活动任务,暂停处理正在等待的任务,并返回等待执行的任务列表。
shutdown()启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
static ExecutorService newCachedThreadPool():
创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。(动态创建线程池,有多少任务,自动创建多少线程)
static ExecutorService newSingleThreadExecutor():创建单个线程,如果线程死掉了,它会自动找个替补线程补上去。(如何实现线程死掉之后重新启动)?
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize):创建一个定时线程池
实例代码:
1 package com.chunjiangchao.thread; 2 3 import java.util.Date; 4 import java.util.concurrent.ExecutorService; 5 import java.util.concurrent.Executors; 6 import java.util.concurrent.TimeUnit; 7 8 /** 9 * 线程并发库,线程池的使用 10 * @author chunjiangchao 11 * 12 */ 13 public class ExecuterDemo { 14 15 public static void main(String[] args) { 16 // ExecutorService threadPool = Executors.newFixedThreadPool(3);//开了固定的三个线程 17 // ExecutorService threadPool = Executors.newCachedThreadPool();//开了10个线程 18 ExecutorService threadPool = Executors.newSingleThreadExecutor();//开了一个固定的线程 19 for(int i=0;i<10;i++){ 20 final int loop = i; 21 threadPool.execute(new Runnable(){ 22 public void run() { 23 try { 24 Thread.sleep(1000); 25 } catch (InterruptedException e) { 26 // e.printStackTrace(); 27 } 28 System.out.println(Thread.currentThread().getName()+" outer "+loop); 29 } 30 31 }); 32 } 33 /* 34 shutdownNow执行的结果为: 35 pool-1-thread-3 outer 36 pool-1-thread-1 outer 37 pool-1-thread-2 outer * */ 38 // threadPool.shutdownNow(); 39 /*shutdown会执行完所有已经提交的任务,不会处理shutdown后提交的任务,而且在后面提交Runnable的时候, 40 * 会抛出异常java.util.concurrent.RejectedExecutionException*/ 41 threadPool.shutdown(); 42 // threadPool.execute(new Runnable(){ 43 // 44 // @Override 45 // public void run() { 46 // System.out.println("不会进行处理"); 47 // } 48 // 49 // }); 50 //实现定时器效果 51 Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(new Runnable(){ 52 53 @Override 54 public void run() { 55 System.out.println("执行定时器结果"+new Date().toLocaleString()); 56 } 57 58 }, 2, 4, TimeUnit.SECONDS);//每隔4s玩一次 59 } 60 61 }
3.Callable&Future
Future取得的结果类型和Callable返回的结果类型必须一致,这是通过泛型来实现的。
Callable要采用ExecutorSevice的submit方法提交,返回的future对象可以取消任务。
CompletionService用于提交一组Callable任务,其take方法返回已完成的一个Callable任务对应的Future对象。
take() 获取并移除表示下一个已完成任务的 Future,如果目前不存在这样的任务,则等待
示例代码
1 package com.chunjiangchao.thread; 2 3 import java.util.Date; 4 import java.util.concurrent.Callable; 5 import java.util.concurrent.ExecutionException; 6 import java.util.concurrent.Executor; 7 import java.util.concurrent.ExecutorCompletionService; 8 import java.util.concurrent.ExecutorService; 9 import java.util.concurrent.Executors; 10 import java.util.concurrent.Future; 11 12 /** 13 * Callable&Future的使用 14 * @author chunjiangchao 15 * 16 */ 17 public class CallableAndFutureDemo { 18 19 public static void main(String[] args) { 20 ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor(); 21 //提交单一任务 22 Future<String> submit = newSingleThreadExecutor.submit(new Callable<String>(){ 23 24 @Override 25 public String call() throws Exception { 26 printTime(); 27 mSleep(3000); 28 printTime(); 29 return "我这有返回值,你看看是不是"; 30 } 31 32 }); 33 mSleep(500); 34 try { 35 String string = submit.get(); 36 System.out.println(string); 37 } catch (InterruptedException | ExecutionException e) { 38 e.printStackTrace(); 39 } 40 // submit.cancel(true);//可以对任务进行取消 41 //提交多个任务 42 Executor executor = Executors.newCachedThreadPool(); 43 ExecutorCompletionService<String> completionService = new ExecutorCompletionService<>(executor); 44 for(int i=0;i<10;i++){ 45 final int loop = i; 46 completionService.submit(new Callable<String>(){ 47 48 @Override 49 public String call() throws Exception { 50 mSleep(1000*loop); 51 return "提交多任务有返回结果"+loop; 52 } 53 54 }); 55 } 56 for(int i=0;i<10;i++){ 57 try { 58 Future<String> result = completionService.take(); 59 printTime(); 60 System.out.println(result.get()); 61 } catch (InterruptedException e) { 62 e.printStackTrace(); 63 } catch (ExecutionException e) { 64 e.printStackTrace(); 65 } 66 } 67 /* 68 * 打印 结果如下 69 2016-4-18 11:57:46 70 2016-4-18 11:57:49 71 我这有返回值,你看看是不是 72 2016-4-18 11:57:49 73 提交多任务有返回结果0 74 2016-4-18 11:57:50 75 提交多任务有返回结果1 76 2016-4-18 11:57:51 77 提交多任务有返回结果2 78 2016-4-18 11:57:52 79 提交多任务有返回结果3 80 2016-4-18 11:57:53 81 提交多任务有返回结果4 82 2016-4-18 11:57:54 83 提交多任务有返回结果5 84 2016-4-18 11:57:55 85 提交多任务有返回结果6 86 2016-4-18 11:57:56 87 提交多任务有返回结果7 88 2016-4-18 11:57:57 89 提交多任务有返回结果8 90 2016-4-18 11:57:58 91 提交多任务有返回结果9 92 */ 93 94 } 95 private static void mSleep(long time){ 96 try { 97 Thread.sleep(time); 98 } catch (InterruptedException e) { 99 e.printStackTrace(); 100 } 101 } 102 private static void printTime(){ 103 System.out.println(new Date().toLocaleString()); 104 } 105 106 }
4.java5的线程锁技术
Lock的使用