Java线程常用类_java线程安全之Concurrent.util常用类(十六)

Concurrent.util常用类

CyclicBarrier使用:

假设有只有的一个场景:每个线程代表一个跑步运动员,当运动员都准备好后,才一起出发,只要有一个人没有准备好,大家都等待。

工作中场景:比如你多台机器想同时准备好了,再去干分布式计算等等。

案例:

public class UseCyclicBarrier {

static class Runner implements Runnable {

private CyclicBarrier barrier;

private String name;

public Runner(CyclicBarrier barrier, String name) {

this.barrier = barrier;

this.name = name;

}

@Override

public void run() {

try {

//随机休眠0-5秒

Thread.sleep(1000 * (new Random()).nextInt(5));

System.out.println(name + " 准备OK.");

//等待

barrier.await();

} catch (InterruptedException e) {

e.printStackTrace();

} catch (BrokenBarrierException e) {

e.printStackTrace();

}

System.out.println(name + " Go!!");

}

}

public static void main(String[] args) throws IOException, InterruptedException {

CyclicBarrier barrier = new CyclicBarrier(3);  // 3

//3个固定的线程池

ExecutorService executor = Executors.newFixedThreadPool(3);

executor.submit(new Thread(new Runner(barrier, "hfbin-01")));

executor.submit(new Thread(new Runner(barrier, "hfbin-02")));

executor.submit(new Thread(new Runner(barrier, "hfbin-03")));

executor.shutdown();

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

打印答案(注意看打印的顺序):

hfbin-01 准备OK.

hfbin-02 准备OK.

hfbin-03 准备OK.

hfbin-03 Go!!

hfbin-01 Go!!

hfbin-02 Go!!

1

2

3

4

5

6

7

CountDownLacth使用:

他经常用于监听某些初始化操作,等初始化执行完毕后,通知主线程继续工。

案例:

public class UseCountDownLatch {

public static void main(String[] args) {

final CountDownLatch countDown = new CountDownLatch(2);

Thread t1 = new Thread(new Runnable() {

@Override

public void run() {

try {

System.out.println("进入线程t1" + "等待其他线程处理完成...");

//等待countDown发出通知即可继续执行下面代码

countDown.await();

System.out.println("t1线程继续执行...");

} catch (InterruptedException e) {

e.printStackTrace();

}

}

},"t1");

Thread t2 = new Thread(new Runnable() {

@Override

public void run() {

try {

System.out.println("t2线程进行初始化操作...");

Thread.sleep(3000);

System.out.println("t2线程初始化完毕,通知t1线程继续...");

countDown.countDown();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

});

Thread t3 = new Thread(new Runnable() {

@Override

public void run() {

try {

System.out.println("t3线程进行初始化操作...");

Thread.sleep(4000);

System.out.println("t3线程初始化完毕,通知t1线程继续...");

countDown.countDown();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

});

t1.start();

t2.start();

t3.start();

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

打印答案:

进入线程t1等待其他线程处理完成...

t2线程进行初始化操作...

t3线程进行初始化操作...

t2线程初始化完毕,通知t1线程继续...

t3线程初始化完毕,通知t1线程继续...

t1线程继续执行...

1

2

3

4

5

6

7

t1使用 await() 这个方法会处于阻塞状态 需要等待t2 t3 countDown()执行完成后才能继续执行 t1 被阻塞后面的代码

也就是:t1 需要等 t2 t3 执行完成之后才能执行

注意:CountDownLatch countDown = new CountDownLatch(2); 这里用到多少个countDown(),CountDownLatch()里面参数就写多少个。

注意:CyclicBarrier和CountDownLacth的区别

CountDownLacth 属于一个线程等待,其他n个线程发出通知 ,然后再执行等待的线程。(针对一个线程)

CyclicBarrier 多个线程参与阻塞。(针对多个线程)

Callable和Future使用

这个例子其实是我们之前实现的Future 模式 。jdk给予我们一个实现的封装类,使用非常简单。

Future 模式非常适合在处理很耗时很长的业务逻辑时进行使用,可以有效的减少系统的响应时间,提高系统的吞吐量。

案例

public class UseFuture implements Callable{

private String para;

public UseFuture(String para){

this.para = para;

}

/**

* 这里是真实的业务逻辑,其执行可能很慢

*/

@Override

public String call() throws Exception {

//模拟执行耗时

Thread.sleep(5000);

String result = this.para + "处理完成";

return result;

}

//主控制函数

public static void main(String[] args) throws Exception {

String queryStr = "query-1";

String queryStr2 = "query-2";

//构造FutureTask,并且传入需要真正进行业务逻辑处理的类,该类一定是实现了Callable接口的类

FutureTask future = new FutureTask(new UseFuture(queryStr));

FutureTask future2 = new FutureTask(new UseFuture(queryStr2));

//创建一个固定线程的线程池且线程数为2,

ExecutorService executor = Executors.newFixedThreadPool(2);

//这里提交任务future,则开启线程执行RealData的call()方法执行

//submit和execute的区别: 第一点是submit可以传入实现Callable接口的实例对象, 第二点是submit方法有返回值

//执行完成后 f1 f2 都会为空

Future f1 = executor.submit(future);        //单独启动一个线程去执行的

Future f2 = executor.submit(future2);

System.out.println("f1 请求完毕  = "+f1.get());

System.out.println("请求完毕");

try {

//这里可以做额外的数据操作,也就是主程序执行其他业务逻辑

System.out.println("处理实际的业务逻辑...");

Thread.sleep(1000);

} catch (Exception e) {

e.printStackTrace();

}

//调用获取数据方法,如果call()方法没有执行完成,则依然会进行等待

System.out.println("数据:" + future.get());

System.out.println("数据:" + future2.get());

executor.shutdown();

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

打印结果

f1 请求完毕  = null

请求完毕

处理实际的业务逻辑...

数据:query-1处理完成

数据:query-2处理完成

1

2

3

4

5

6

Semaphore 使用

在Semaphone信号量非常适合高并发访问,新系统在上线之前,要对系统的访问量进行评估,当然这个值肯定不是随便拍拍脑袋就能想出来的,是经过以往的经验、数据、历年的访问量,己经推广力度讲行一个合理的评估,当然评估标推不能太大也不能太小,太大的话投入的资源达不到实际效果,纯粹浪费资源,太小的话,某个时间点一个高峰值的访问量上来直接可以压垮系统

相关概念:

PV(view)网站的总访何量,页面浏览量或点击量,用户每刷新一,次就会被记录一次。

UV (unique Visitor)访网网站的一台申客户端为一个访客,一般来讲,时间上以00:00-24:00之内相同ip的客户端只记录一次。

QPS(query per second)即每秒查询数,qps很大程度上代表了系统业务上的繁忙程度,每次请求的背后,可能对应着多次磁盘I/O,多次网络请求,多个cpu时间片等。我们通过qps可以非常直观的了解当前系统业务情况,一旦当前超过所设定的预警阀值,可以考虑加机器对集群扩容,以免压力过大导致宕机,可以根据前期的压力测试得到估值,在结合后期综合运维清况,估算出值。

RT (response time)即请求的响应时间,这个指标非常关键,直接说明前端用户的体验,因此任何系统都想降低rt时间。当然还涉及cpu、内存、网络、磁盘等情况,更细节的问很多,如select、update、delete/ps等数据库层面的统计。

容量评估:一般来说通过开发、运维、测试、以及业务等相关人员.綜合出系统的一系列阈值,然后我们根据关键阈值如qps、rt等,对系统讲行有效的变更。一般来讲.我们讲行多轮压力测试以后,可以对系统讲行峰值评估,采用所谓的80/20原则,即80%的访网请求将在20%的时间内达到。这样我们可以根据系统对应PV计算出峰值 qps。

峰值qps= (总PV ×80%)/(60 × 60× 24 ×20%)

然后在将总的峰值qps除以单台机器所能承受的最高的qps值,就是所需要机器的数量:机器数=总的峰值qps /压测得出的单机极限qps

当然不排除系统在上线前进行大型促销活动,或者双十一、双十二热点事件、遭受到DDos攻击等情况,系统的开发和运维人员急需要了解当前系统运行的状态和负载情况,一般都会有后台系统去维护。

Semaphone可以控制系统的流量:

拿到信号量的钱程可以讲入,否则就等待.通过acquire()和rekease()获取和释放访同许可。

案例:

public class UseSemaphore {

public static void main(String[] args) {

// 线程池

ExecutorService exec = Executors.newCachedThreadPool();

// 只能5个线程同时访问

final Semaphore semp = new Semaphore(5);

// 模拟20个客户端访问

for (int index = 0; index < 20; index++) {

final int NO = index;

Runnable run = new Runnable() {

public void run() {

try {

// 获取许可

semp.acquire();

System.out.println("Accessing: " + NO);

//模拟实际业务逻辑

Thread.sleep((long) (Math.random() * 10000));

// 访问完后,释放

semp.release();

} catch (InterruptedException e) {

}

}

};

exec.execute(run);

}

try {

Thread.sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

//System.out.println(semp.getQueueLength());

// 退出线程池

exec.shutdown();

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

打印的结果:

Accessing: 0

Accessing: 1

Accessing: 2

Accessing: 3

Accessing: 4

Accessing: 5

Accessing: 6

Accessing: 7

Accessing: 8

Accessing: 9

Accessing: 10

Accessing: 11

Accessing: 12

Accessing: 13

Accessing: 14

Accessing: 15

Accessing: 16

Accessing: 19

Accessing: 17

Accessing: 18

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

源代码:https://github.com/hfbin/Thread_Socket/tree/master/Thread/concurrent019

---------------------

作者:小彬彬~

来源:CSDN

原文:https://blog.csdn.net/qq_33524158/article/details/78582079

版权声明:本文为博主原创文章,转载请附上博文链接!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值