开发者社区> 友德> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

高并发下Java多线程编程基础

简介:
+关注继续查看

Java线程同步与异步

线程池

无锁化的实现方案

分布锁的实现方案

分享的目的:

进一步掌握多线程编程和应用的技巧,希望对大家在平时的开发中应对高并发编程有所帮助

Java线程同步与异步

1. 同步相关的方法有

wait, notify, notifyAll

2. 关键字

synchronized

3. JDK锁的框架

AQS (AbstractQueuedSynchronizer)

4. AQS的实现类

java.util.concurrent.locks.ReentrantLock
java.util.concurrent.locks.ReentrantReadWriteLock
java.util.concurrent.CountDownLatch
java.util.concurrent.Semaphore

5. 例子——两个线程交替打印出100以内的奇数和偶数

主程序:

image.png
image.png

输出结果示例:

image.png
...
...
...
image.png

思考:

读者可以用两种其它方法实现,加深自己对Java线程同步和互斥的理解
用 ReentrantLock?
还是用wait和notify ?

线程池

作用:

控制线程并发数量,一般用在控制单机并发度上, 也是实现流控的一种方案;

实现原理:

1. 参数含义

corePoolSize: 核心线程的数量, 在CPU密集型和IO密集型的任务中,这个参数的设置不太一样:

在CPU密集型的应用中:

通常这个参数被设置为: 机器cpu核数-1, 例如机器有4个核,这个参数就被设置为3, 这样做的即兼顾了最大的并发度,又兼顾了其它非重要的核心任务的执行;

在IO密集的任务中:

通常这个参数被设置为机器cpu核数*(1.5 - 3),具体情况还需要根据实际业务情况进行压测比较,然后再给出最优的值;
maximumPoolSize: 最大核心线程的数量
poolSize: 当前线程的数量

当用户向线程池中新提交一个线程的时候,会有如下情况:

情况1.

如果当前线程池中线程的数量小于corePoolSize, 就会创建一个新的线程, 并添加到线程池中;

情况2.

如果当前线程池中线程的数量等于corePoolSize, 并且等待队列中还没有满,则把当前用户添加的线程对象放在等待队列中;

情况3.

如果当前线程池中线程的数量大于等于corePoolSize并且小于maximunPoolSize,并且等待队列已经满,则创建一个新的线程,并添加到线程池中;

情况4.

如果当前线程池中线程的数量等于maximunPoolSize, 则会根据线程创建线程时候的拒绝策略,进行相应的处理;

2. java线程对象中run方法和start方法的区别:

2.1 线程对象直接调用run方法,JVM是不会有感知,是不会直接产生一个新的线程, 此时程序运行的方式依然是串行的;
2.2 线程对象直接调用start方法,JVM才会有感知,会产生一个新的线程, 此时才会产生并发多线程;
线程池正是充分利用了run方法和start的区别来实现线程的复用;

3. 线程池的核心代码

下面均是以jdk1.6的线程池的源码,jdk1.7和jdk1.8线程池实现在上有些变化,但核心思想不变,有兴趣可以自己去研究

提交线程的核心代码:

image.png

执行用户任务的核心代码:

image.png

无锁化的实现方案

用线程池的方案

1. netty的reactor线程模型,参考netty官方或网上相关的资料

2. 异地机房数据库之间的数据同步:

用表名+主键名做hash ,hash值相同的记录被写到同一个Kafka的Partition中去,假设一个Partition用一个线程进行消费, 这样不同线程之间写入目标数据库的时候,就不会存在数据库行锁的竞争关系,间接实现了无锁化的操作, 即线程之间并行,线程内部串行, 如下图所示;
image.png

用CAS的命令

1. JDK中各种类型值的原子操作

AtomicInteger
AtomicLong
AtomicBoolean

2. jdk中各种锁的实现, 本质也是volitate变量+CAS

java.util.concurrent.locks.ReentrantLock
java.util.concurrent.Semaphore
java.util.concurrent.CountDownLatch

分布锁的实现方案

1. tair

incr和decr操作,相当于是乐观锁

2. Redis/memcache

setNx命令

3. Zookeeper

充分利用watcher机制,创建临时结点,谁创建成功,谁就获得当前的锁

4. 数据库:利用数据库的行锁

// 加锁SQL
update trade_base set status = 1 where trade_no=“XXX” and status = 0;
// 解锁SQL
update trade_base set status = 0 where trade_no=“XXX” and status = 1;
注意trade_base表上一要有trade_no的列的唯一索引

当然具体用那种分布锁,还需要结合业务自身的需要,一般来说,在并发量不是别大,数据库完全可以扛得住的情况下,用数据库实现分布锁最快,最方便,而且性能的损失也非常地小;
当然现在很多场景下,都是分库分表,并且加锁和解锁分别都只影响一行,对数据库来说,加锁和解锁的 sql也是非常轻量的sql操作,因此在性能损失上不用过多的担心;

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Java基础多线程
最近在回顾JavaSE部分的知识,对一些薄弱的知识进行记录,学习方式,通过视频和图书的进行学习,视频看B站韩顺平老师的【韩顺平讲Java】一天学会线程 Thread Synchronized 互斥锁 进程 并行 并发 死锁等,图书看Java核心技术 卷I 基础知识(原书第10版)。
20 0
Java基础之网络编程
Java基础之网络编程
31 0
Java网络编程之网络基础
现代的应用程序都离不开网络,网络编程是非常重要的技术。 Java SE提供java.net包,其中包含了网络 编程所需要的最基础一些类和接口。这些类和接口面向两个不同的层次:基于Socket的低层次网络编 程和基于URL的高层次网络编程。
29 0
Java并发编程之Java线程池
Java并发编程之Java线程池
32 0
Java并发编程之调度线程池
Java并发编程之调度线程池
58 0
Java基础——面向对象编程
Java基础——面向对象编程
36 0
java并发(一):线程基础篇
线程的创建很简单,一般是集成Thread类或者实现Runnable接口,我就不细说了。然后,要牢记多线程的3大特性:多线程的三个特性:原子性、可见性、有序性 原子性:是指一个操作是不可中断的。
864 0
JAVA多线程基础
一:线程与进程 1 线程:进程中负责程序执行的执行单元 线程本身依靠程序进行运行 线程是程序中的顺序控制流,只能使用分配给程序的资源和环境 2 进程:执行中的程序 一个进程至少包含一个线程 3 单线程:程序中只存在一个线程,实际上主方法就是一个主线程 4 多线程:在一个程序中运行多个任...
770 0
java多线程--多线程基础小结
什么是线程?    在同一个进程中可以执行多个任务,每一个任务可以看做一个线程.   线程是程序的执行单元,执行路径,使程序使用cpu的最基本单位   一个进程如果只有一条执行路径,那么就是单线程的 一个进程如果有多个执行路径,那么就是多线程的 多线程的意义:    ...
854 0
Java网络编程-序章(网络基础)
一、前言 序章只是讲解一下网络常识,有这个常识的同学可以略过序章, 二、网络基础 1、传统OSI层模型: 自下而上-物理层-数据链路层-网络层-传输层-会话层-表示层-应用层...
944 0
+关注
友德
本人菜鸟一个,来自天猫营销平台,比较喜欢数据库和中间件,来社区希望多一些机会和各位技术大神交流,相互学习
10
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载