背景:并发知识是一个程序员段位升级的体现,一样也是进入BAT的必经之路,有必要把并发知识从新梳理一遍。java
并发concurrent:程序员
使用ThreadLocal能够实现线程范围内共享变量,线程A写入的值和线程B获取到的结果一致;ReentrantReadWriteLock容许多个读线程或多个写线程同时进行,但不容许写线程和读线程同时进行;使用Callable能够获得线程执行的返回结果;Exchanger能够相互交换家线程执行的结果;这些使用方法大体都同样,JDk参考文档里面哪里不会点哪里,下面写个ThreadLocal实现线程范围内变量共享,里面还用到了一下饿汉模式:面试
执行结果如图中控制台打印,使用ThreadLocal保证了线程0和线程1读取到的值与写入的一致。算法
1 import java.util.Random; 2 import java.util.concurrent.locks.ReentrantReadWriteLock; 3 import lombok.Data; 4
5 public class ThreadLocalTest { 6 //ThreadLocal 实现线程范围内共享变量
7 private static ThreadLocal<Integer> x = new ThreadLocal<Integer>(); 8 private static ThreadLocal<MyThreadScopeData> myThreadScopeData = new ThreadLocal<MyThreadScopeData>(); 9 public static void main(String[] args) { 10 new ReentrantReadWriteLock(); 11 for(int i = 0; i<2; i++) { 12 new Thread(new Runnable() { 13 @Override 14 public void run() { 15 int data = new Random().nextInt(); 16 System.out.println(Thread.currentThread().getName() 17 +" has put data: "+ data); 18 x.set(data); // 存的时候与当前线程相关 取的时候也是与当前线程相关 19 //MyThreadScopeData.getInstance()拿到与本线程实例相关的对象: 不用反复new对象来getter/setter
20 MyThreadScopeData.getThreadInstance().setName("name: "+data); 21 MyThreadScopeData.getThreadInstance().setAge(data); 22 new A().get(); 23 new B().get(); 24 } 25 }).start(); 26 } 27 } 28
29 static class A{ 30 public void get() { 31 int data = x.get(); 32 System.out.println("A from "+Thread.currentThread().getName()+" get data: "+data); 33 //MyThreadScopeData.getInstance()拿到与本线程实例相关的对象
34 MyThreadScopeData myData = MyThreadScopeData.getThreadInstance(); 35 System.out.println("A from "+Thread.currentThread().getName() 36 +" getMyData: "+myData.getName() +","+myData.getAge()); 37 } 38 } 39
40 static class B{ 41 public void get() { 42 int data = x.get(); 43 System.out.println("B from "+Thread.currentThread().getName()+" get data: "+data); 44 //MyThreadScopeData.getInstance()拿到与本线程实例相关的对象
45 MyThreadScopeData myData = MyThreadScopeData.getThreadInstance(); 46 System.out.println("B from "+Thread.currentThread().getName() 47 +" getMyData: "+myData.getName() +","+myData.getAge()); 48 } 49 } 50
51 //设计本身线程范围内变量的共享,不须要建立对象,只需调用线程便可用到线程内的变量
52 @Data 53 static class MyThreadScopeData{ 54 //构造方法私有化,外部没发直接调用,可是能够调用里面的静态方法
55 private MyThreadScopeData() { } 56 public static /*synchronized*/ MyThreadScopeData getThreadInstance() { 57 MyThreadScopeData instance = map.get(); 58 if (instance == null) { 59 //饿汉模式 : 第一次来建立
60 instance = new MyThreadScopeData(); 61 map.set(instance); 62 } 63 return instance; 64 } 65 // private static MyThreadScopeData instance = null; // new MyThreadScopeData();
66 private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>(); 67 private String name; 68 private int age; 69 } 70
71 }
网上还有一个多线程面试颇有趣的题目:子线程执行10次,主线程执行100次,接着子线程再执行10次,主线程继续再执行100次,往复循环50次;spring
1 //Java多线程面试: 子线程执行10次,主线程执行100次,接着子线程再10次,主线程再执行100次,往复循环50次
2 public class ThreadCommunication { 3 public static void main(String[] args) { 4 Business business = new Business(); 5 new Thread( 6 new Runnable() { 7 @Override 8 public void run() { 9 for (int i = 1; i <= 50; i++) { 10 business.sub(i); 11 } 12 } 13 }).start(); 14
15 for (int i = 1; i <= 50; i++) { 16 business.main(i); 17 } 18 } 19
20 //把主线程和自线程执行的方法归结到一个类(共同算法的若干方法),巧妙设计,好维护高聚合,健壮性;
21 public static class Business{ 22 //子线程方法
23 private boolean bShouldSub = true; 24 public synchronized void sub(int i) { 25 while(!bShouldSub) { 26 //用while比if更好,能够防止线程被伪唤醒
27 try { 28 this.wait(); // 若是不是子线程方法该执行的,则令其等待
29 } catch (InterruptedException e) { 30 e.printStackTrace(); 31 } 32 } 33 for(int j = 1; j<= 10; j++) { 34 System.out.println("sub thread sequence of "+j + ",loop of "+ i); 35 } 36 bShouldSub = false; 37 this.notify(); //唤醒主线程方法
38 } 39
40 //主线程方法
41 public synchronized void main(int i) { 42 while(!bShouldSub) { 43 try { 44 this.wait(); // 若是不是主线程方法该执行的,则令其等待
45 } catch (InterruptedException e) { 46 e.printStackTrace(); 47 } 48 } 49 for(int j = 1; j<= 100; j++) { 50 System.out.println("main thread sequence of "+j + ",loop of "+ i); 51 } 52 bShouldSub = true; 53 this.notify(); // 唤醒子线程方法
54 } 55 } 56 }
线程池:缓存
一、固定线程数目的线程池newFixedThreadPool;多线程
二、缓存线程数目的线程池newCachedThreadPool;并发
三、单一线程池newSingleThreadExecutor;dom
四、定时器线程池newScheduledThreadPool;ide
1 package com.xinyan.springcloud.controller; 2
3 import java.util.concurrent.ExecutorService; 4 import java.util.concurrent.Executors; 5 import java.util.concurrent.TimeUnit; 6
7 public class ThreadPoolTest { 8 public static void main(String[] args) { 9 //固定线程数目的线程池 3个
10 ExecutorService threadPool = Executors.newFixedThreadPool(3); 11 //缓存线程数目的线程池即动态变化 当任务过来了,线程池内部会自动增长线程,空闲后线程又被回收,线程数目不定 12 //ExecutorService threadPool = Executors.newCachedThreadPool(); 13 //单一线程池 14 //ExecutorService threadPool = Executors.newSingleThreadExecutor(); 15 //往线程池中放入10个任务:
16 for(int i = 1; i<= 10; i++) { 17 final int task = i; // task被final修饰不能变了,可是i 能够变
18 threadPool.execute(new Runnable() { 19 @Override 20 public void run() { 21 for(int j =1; j<=10; j++) { 22 try { 23 Thread.sleep(200); 24 } catch (InterruptedException e) { 25 e.printStackTrace(); 26 } 27 //System.out.println(Thread.currentThread().getName()+" loop of "+ j +" task is "+ task);
28 } 29 } 30 }); 31 } 32 System.out.println("全部的10个任务已经所有提交。"); //任务都提交了,交由线程池去搞
33 threadPool.shutdown(); //没有任务后关闭线程 34 //threadPool.shutdownNow(); //还有任务没有给执行完毕就当即关闭线程 35
36 //定时器线程池: 3个线程
37 System.out.println("敌军还有5秒到达战场."); 38 Executors.newScheduledThreadPool(3).schedule(new Runnable() { 39 @Override 40 public void run() { 41 System.out.println("敌军抵达战场,碾碎她们。"); 42 } 43 //5秒后执行线程池内run方法
44 }, 5, TimeUnit.SECONDS); 45 //Executors.newScheduledThreadPool(3)scheduleAtFixedRate(command, initialDelay, period, unit) 46 //scheduleAtFixedRate 定时循环执行线程池内方法
47 } 48 }