录入新业务数据,在业务开始前查询判断数据库是否有相同记录,查询无相同记录才执行业务步骤,在最后将数据插入库中,最终在数据库中发现了重复数据,为并发操作导致,采取的方案是在最终插入前再进行一次查询且对该查询和最终的插入加锁。Java中加锁可利用关键字synchronized和ReentrantLock类来实现,ReentrantLock类在扩展功能上更强大,在使用上也更灵活,但synchronized在此处已经可以,所以最后用synchronized加锁。
现对synchronized关键字的用法作一下简单总结:
(主要参考《Java多线程编程核心技术》)
一、修饰方法
1、修饰非静态方法
public synchronized void method() {
//
}
synchronized关键字加到非static方法上是给对象上锁,持有该方法所属对象的锁Lock。
2、修饰静态方法
public static synchronized void method() {
//
}
synchronized关键字加到static静态方法上是给Class类上锁,对类的所有对象实例起作用。
注意:同步不能继承,子类覆盖父类的synchronized方法,不具有同步效果,必须显示的在子类方法中添加synchronized关键字才能达到同步效果。
二、修饰代码块
1、锁实例对象
synchronized (this) {
}
与synchronized 非静态方法一样,是锁定当前对象的。当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同个object中所有其他synchronized(this)同步代码块的访问将被阻塞 即synchronized使用的 对象监视器 是一个。
2、锁class对象
synchronized (this.class) {
}
与synchronized static方法的作用一样,是给Class类上锁,对类的所有对象实例起作用
3、锁任意(非this)对象
Object lock = new Object();
//或使用 byte[] lock = new byte[0];
synchronized (lock) {
}
这个任意(非this)对象大多数是实例变量及方法的参数,对该非this对象上锁。
注意:synchronized(string) 与String联合使用时,常量池带来的一些例外。
String aString = "lock";
String bString = "lock";
synchronized (aString) {
//代码块1
}
synchronized (bString) {
//代码块2
}
因为String常量池的原因导致代码块1和代码块2使用的是同一个对象锁,而非预想的两个对象锁。因此大多数情况下,同步synchronized代码块不使用String作为锁对象,而改用其他,如new Object()实例化一个Object对象,作为对象锁。
PS:关键字volatile 只修饰变量,解决的是变量在多个线程之间的可见性,synchronized关键字解决的是多个线程之间访问资源的同步性。