【区别】synchronized:对象锁,调用synchronized方法的线程取得该对象锁后,其他线程需要等待该线程释放该对象锁后才能进入该方法。若是持有不同的对象的线程,则可以同时进入该方法。注意:类锁(全局锁)synchronized(A.getClass)与对象锁synchronized(this)的区别【特点】隐式锁,锁的范围是整个方法或代码块中,离开该方法/块后自动释放锁
2.重入锁:ReentrantLock:更灵活的锁机制,提供可轮询和可中断、可定时的锁获取机制。持有该锁的线程可以重复进入该方法(count加1,但只执行一次Method),可以实现更细微的锁颗粒。【特点】需要显式的获取锁和释放锁
使用事例:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds <span style="font-family: Arial, Helvetica, sans-serif;">一次只能有一个线程进入,锁的颗粒较大</span>
try {
// ... method body
} finally {
lock.unlock()
}
}
}
锁的颗粒对并发的控制作用是明显的,例如两个线程对相同的一个属性进行操作,A转账B的两个线程中,一个线程需要等待另一个线程释放该锁才能进入方法,这样是没有问题的。但是两个线程一个是A转账B,一个是C转账D,上面的锁的方法也会等待一个线程执行完毕释放锁后,另一个线程才能获取锁然后执行,这样就比较慢了,线程只能排队执行,不能并发执行。因此需要改进锁实现方法,实现线程合理的并发控制。实现方法(属性上加锁操作,减少锁的颗粒):
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author : hd
* @Date : 2016/8/18 16:27
* @Version : 1.0.0
*/
/**
* 分段锁,系统提供一定数量的原始锁,根据传入对象的哈希值获取对应的锁并加锁
* 注意:要锁的对象的哈希值如果发生改变,有可能导致锁无法成功释放!!!
*/
public class SegmentLock<T> {
private Integer segments = 16;//默认分段数量
private final Map<Integer, ReentrantLock> lockMap = new HashMap<>();
public SegmentLock() {
init(null, false);
}
public SegmentLock(Integer counts, boolean fair) {
init(counts, fair);
}
private void init(Integer counts, boolean fair) {
if (counts != null) {
segments = counts;
}
for (int i = 0; i < segments; i++) {
lockMap.put(i, new ReentrantLock(fair));
}
}
public void lock(T key) {
ReentrantLock lock = lockMap.get(key.hashCode() % segments);
lock.lock();
}
public void unlock(T key) {
ReentrantLock lock = lockMap.get(key.hashCode() % segments);
lock.unlock();
}
}
另一个事例:三个线程同时进入该锁的情况public class Test {
//public static volatile int i=10;
static int i=10;
@org.junit.Test
public void testReenTrant(){
final SegmentLock<String> segmentLock=new SegmentLock<String>();
final String key="2016";
for(int j=0;j<3;j++){
new Thread(new Runnable() {
@Override
public void run() {
try{
segmentLock.lock(key);
i=i-1;
System.out.println("lock i:"+i);
}finally {
segmentLock.unlock(key);
}
System.out.println("unlock i:"+i);
}
}).start();
}
}
}
/**
结果:lock i:9
unlock i:9
lock i:8
unlock i:8
根据结果可知:一个线程获取锁进入方法执行后,释放了该锁,另外两个线程重复获取到了锁synchronized例子:
class Sync {
public synchronized void test() {
System.out.println("test开始..");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("test结束..");
}
}
class MyThread extends Thread {
public void run() {
Sync sync = new Sync();
sync.test();
}
}
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
Thread thread = new MyThread();
thread.start();
}
}
}
运行结果://持有不同对象锁 的线程同时进入
test开始..
test开始..
test开始..
test结束..
test结束..
test结束..
class MyThread extends Thread {
private Sync sync;
public MyThread(Sync sync) {
this.sync = sync;
}
public void run() {
sync.test();
}
}
public class Main {
public static void main(String[] args) {
Sync sync = new Sync();
for (int i = 0; i < 3; i++) {
Thread thread = new MyThread(sync);
thread.start();
}
}
}
运行结果://持有相同对象锁 的线程要等另一个线程的对象锁释放才能进入
test开始..
test结束..
test开始..
test结束..
test开始..
test结束..
class Sync {
public void test() {
synchronized (Sync.class) {
System.out.println("test开始..");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("test结束..");
}
}
}
class MyThread extends Thread {
public void run() {
Sync sync = new Sync();
sync.test();
}
}
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
Thread thread = new MyThread();
thread.start();
}
}
}
运行结果://对象类锁,无论创建了多少对象都是属于这个类,需要等待这个线程释放锁,其他线程才能进入,全局锁
test开始..
test结束..
test开始..
test结束..
test开始..
test结束..
【总结】开发中,常常需要对锁的粒度对象细化来实现符合逻辑并发控制,ReentrantLock实现了区别于Synchronized的另一种锁的机制,正如api解释的一样
A reentrant mutual exclusion Lock with the same basic behavior and semantics as the implicit monitor lock accessed using synchronized <span style="font-family: Arial, Helvetica, sans-serif;">methods and statements, but with extended capabilities.</span>
和使用synchronized(隐式监视器锁)和语句访问具有相同的基本行为和语义的重入的互斥锁,但功能更强大。