首页 > 编程知识 正文

数据库高并发数据重复,多线程如何保证数据的同步

时间:2023-05-05 02:08:38 阅读:162981 作者:1592

这两天,我整理了安卓的路由框架。 在组织过程中,发现在路由消息接发的过程中,会产生大量的传输载波类。 对这个运营商类别来说,他们自己是可复用的,不需要大量制作、大量丢弃。 因此,我们打算引入客体池,解决类的重复创建问题。 对象生命周

这两天,我整理了安卓的路由框架。 在组织过程中,发现在路由消息接发的过程中,会产生大量的传输载波类。 对这个运营商类别来说,他们自己是可复用的,不需要大量制作、大量丢弃。 因此,我们打算引入客体池,解决类的重复创建问题。

对象的生命周期

Java对象的生命周期包括三个阶段:创建对象、使用对象和清除对象。 因此,对象的生命周期长度可由以下公式表示: T=T1 T2 T3。 其中,T1表示对象的创建时间,T2表示对象的使用时间,T3表示其清除时间。 由此可知,只有T2是真正有效的时间,T1、T3是对象自身的开销。 因此,避免或减少T1和T3的时间可以有效地提高程序的性能。

象池

在EffectJava的第五条提案中,明确表示了“避免制作不必要的对象”这样的提案。 对于可复用的对象,不应该频繁创建和废弃,而应该反复利用。

要重用对象,必须创建对象池,将所有可重用对象合并到池中,并向外部提供obtain ) )方法以检索对象。 通常使用静态对象数组实现对象池。

大象池初始化策略分为两种,一种是统一初始化,即在特定时间统一初始化池中的所有对象,首次使用时有一定的开销。 另一种是即时初始化,在使用中进行初始化,每次池内满之前都有一定的开销。

高并发性

创建目标池后,需要考虑如何分配和回收目标池的目标。 因为是路由系统的载体类,所以需要多线程访问,同时也要考虑线程的安全性和并发性。

对于线程安全,通常使用同步关键字进行线程同步。 但是,这样做会对obtain () )函数产生悲观的锁定。 这样可以有效地防止同步问题。 此外,synchronized关键字也多次通过JVM优化性能,但无法满足高并发性要求。

此时,我们可以参考ConcurrentHashMap,利用CAS算法(无阻塞同步算法)实现乐观锁定。 在类中创建了名为isIdle的AtomicBoolean变量。 缺省值为true (空闲),在多个线程争夺一个对象时调用is idle.compareandset (true,false )。 此方法返回布尔型结果。isIdle设置为false (占用),如果返回false,则表示该类已被另一个线程占用,并重新生成另一个对象,直到该对象的isIdle竞争成功

相关代码如下。

//顺序计数器

privatestaticatomicintegersindex

//偶像标记

AtomicBoolean isIdle;

//计数器复位参数

privatestaticfinalintreset _ num=1000;

publicstaticconcurrentrouterrequestobtain {

//获取下一个计数器的值

intindex=sindex.getandincrement (;

//计数器太大时定为0

if (索引重置_ num ) {

sindex.compareandset(index,0 );

if (索引reset _ num *2) {

sindex.set(0;

}

}

//计算计数器对应于对象数组的对象下标

intnum=index(length-1;

//获取对应的对象

concurrentrouterrequesttarget=table [ num ];

//冲突对象的空闲id。 第一个参数true是希望的值,第二个参数是应该设置的值false。

//如果设定成功,则直接返回target,如果失败,则继续寻找下一个空对象。

target.is idle.compareandset (true,false ) )。

返回目标;

}else{

return obtain (;

}

}

本节介绍了目标池如何分配目标,但它采用顺序分配,每次检索时在目标池中向后排序一个数量级。 另外,客体池的长度默认为2的n次方,这具有通过以位为单位进行计算,可以快速找到模仿HashMap的Hash值记忆的客体池内的数组下标的优点。

测试比较

模拟条件为100个线程的并发请求,每个线程1000次,共10万次并发请求。

时间开销

内存开销

新建对象

220ms

1.53MB

利用对象池

180ms

1.25KB

第一种情况是直接用new关键字新建对象,处理请求,最后的结果是耗时220ms,内存消耗100000*16(byte)=1.53MB。

第二种情况是使用对象池处理请求,最后耗时180ms,内存消耗为64*20(byte)=1.25KB。

通过对比我们可以看出,首先,由于减少了对象的创建,虽然多了同步,不过在时间性能上,对象池还是优于新建对象,大概提升了20%左右的性能。该时间还不包括GC回收的开销,如果加上的话,性能提升会更明显。其次,在内存上面,两者的差距是1000倍左右的,极大地节省了内存的开销,减少了GC的触发。

对象池实现

下面放一个纯Java实现的高并发对象池

对象池:

public final class ObjectPool {

private OBJECT[] mTable;

private AtomicInteger mOrderNumber;

public static int RESET_NUM = 1000;

public ObjectPool(OBJECT[] inputArray) {

mOrderNumber = new AtomicInteger(0);

mTable = inputArray;

if (mTable == null) {

throw new NullPointerException("The input array is null.");

}

int length = inputArray.length;

if ((length & length - 1) != 0) {

throw new RuntimeException("The length of input array is not 2^n.");

}

}

public void recycle(OBJECT object) {

object.isIdle.set(true);

}

public OBJECT obtain() {

int index = mOrderNumber.getAndIncrement();

if (index > RESET_NUM) {

mOrderNumber.compareAndSet(index, 0);

if (index > RESET_NUM * 2) {

mOrderNumber.set(0);

}

}

int num = index & (mTable.length - 1);

OBJECT target = mTable[num];

if (target.isIdle.compareAndSet(true, false)) {

return target;

} else {

// 注意:此处可能会因为OBJECT回收不及时,而导致栈溢出。

// 请增加retryTime参数,以及retryTime过多后的判断。

// 具体思路请参考

// https://github.com/SpinyTech/ModularizationArchitecture/blob/master/macore/src/main/java/com/spinytech/macore/router/RouterRequest.java

// 中的obtain()及obtain(int retryTime);方法

return obtain();

}

}

public abstract static class RecyclableObject {

AtomicBoolean isIdle = new AtomicBoolean(true);

}

}

测试类:

public class Test {

public static void main(String[] args) {

TestObject[] array = new TestObject[32];

for(int i = 0 ; i < 32 ;i++){

array[i] = new TestObject();

}

final ObjectPool objectPool = new ObjectPool<>(array);

for(int i = 0 ; i < 50; i++){

new Thread("Thread:"+i){

@Override

public void run() {

super.run();

for(int j = 0 ; j < 50 ; j++){

TestObject testObject = objectPool.obtain();

testObject.print(getName(),"--index:"+j);

objectPool.recycle(testObject);

}

}

}.start();

}

}

static class TestObject extends ObjectPool.RecyclableObject{

public TestObject(){

}

public void print(String thread, String index){

System.out.println(thread+index);

}

}

}

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。