并发concurrent---2

Posted taojietaoge

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并发concurrent---2相关的知识,希望对你有一定的参考价值。

背景:并发知识是一个程序员段位升级的体现,同样也是进入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 }

 

以上是关于并发concurrent---2的主要内容,如果未能解决你的问题,请参考以下文章

如何从设置中获取数据并发送到此片段

Swift新async/await并发中利用Task防止指定代码片段执行的数据竞争(Data Race)问题

Swift新async/await并发中利用Task防止指定代码片段执行的数据竞争(Data Race)问题

golang代码片段(摘抄)

全栈编程系列SpringBoot整合Shiro(含KickoutSessionControlFilter并发在线人数控制以及不生效问题配置启动异常No SecurityManager...)(代码片段

JUC并发编程 共享模式之工具 JUC CountdownLatch(倒计时锁) -- CountdownLatch应用(等待多个线程准备完毕( 可以覆盖上次的打印内)等待多个远程调用结束)(代码片段