多线程高并发线程安全性问题解决方案

解决高并发的线程安全性问题,一般要根据实际业务逻辑来处理,一切脱离业务谈技术都是耍流氓。例如有一个场景:设计一个抢票程序,同时有1000个人要抢100张票。首先我们要考虑如何设计这个场景,如果你采用的是单机版的多线程方案,那么在抢票环节访问数据库层使用synchronized修饰方法或者lock保证线程的执行顺序;如果你设计的是高可用集群部署加多线程的方案,那么就需要使用分布式锁来保证线程安全,一般常用的是基于redis的分布式锁,你也可以使用基于数据库的分布式锁和基于Zookeeper的分布式锁。

单机版使用synchronized或者lock:

synchronized关键字可以用于修饰类的实例方法、静态方法和代码块。

举个例子:

public class Counter {
	private int count;
	public synchronized void incr() {
		count++;
	}
	public synchronized int getCount() {
		return count;
	}
}
 

Counter是一个简单的计数器类,加了synchronized后,方法内的代码就变成了原子操作(要执行就一起执行,不执行就都不执行),当多个线程并发更新同一个Counter对象时,也不会出错。

同样,使用lock后,能使该代码块按照指定的顺序执行,被lock这块代码已经被其中一个线程访问了,那么另外一个线程只能等待:

private object o = new object();//创建一个对象
public void Work()
{
  lock(o)//锁住这个对象
  {
    //做一些必须按照顺序做的事情
  }
}

高可用集群部署使用redis分布式锁:

redis命令说明:

(1)setnx命令:set if not exists,当且仅当 key 不存在时,将 key 的值设为 value。若给定的 key 已经存在,则 SETNX 不做任何动作。

返回1,说明该进程获得锁,将 key 的值设为 value
返回0,说明其他进程已经获得了锁,进程不能进入临界区。
命令格式:setnx lock.key lock.value

(2)get命令:获取key的值,如果存在,则返回;如果不存在,则返回nil

命令格式:get lock.key

(3)getset命令:该方法是原子的,对key设置newValue这个值,并且返回key原来的旧值。

命令格式:getset lock.key newValue

(4)del命令:删除redis中指定的key

命令格式:del lock.key
 

猜你喜欢

转载自blog.csdn.net/lwpoor123/article/details/130218061