背景:并发知识是一个程序员段位升级的体现,同样也是进入BAT的必经之路,有必要把并发知识重新梳理一遍。
并发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次;
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 }
线程池:
1、固定线程数目的线程池newFixedThreadPool;
2、缓存线程数目的线程池newCachedThreadPool;
3、单一线程池newSingleThreadExecutor;
4、定时器线程池newScheduledThreadPool;
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 }