文章目录
  1. 1. synchronized(同步锁)
  2. 2. ReentrantLock(重入锁)

众所周知、对于JAVA程序员而言,线程安全是并发编程中的一个重大关注点,所谓线程安全就是要保证在多个线程间读写共享数据保证正确性,如果不能保证,那就不是线程安全的了。 synchronized、ReentrantLock都是用来实现线程间同步,访问同步的代码需要先获得锁,保证每次都只能有一个线程进入同步块,代码执行完毕后释放锁,从而保证了线程间的安全。

锁藏在什么地方?

每个类都有一把锁,暂且叫类锁,所有这个类的实例都可以获取这个类的锁;
每个实例也都有一把锁,暂且叫实例锁,类锁和实例锁不是相同的,只有实例自己才能获得自己的实例锁,不同的实例获得实例锁是不一样的,这个很关键。

谁的锁,锁的谁?

synchronized(同步锁)

关键字synchronized的用法

  • 用在实例方法上
1
2
3
public synchornized void someMethod(){
//...
}

谁的锁:当前实例的实例锁,不同实例获得的实例锁在这里不是同一把锁;
锁的谁:锁的是这个方法代码块,即线程要进入这个方法就必须先获得这个实例的锁,同一个实例互斥。

  • 用在静态方法上
1
2
3
public static synchornized someMethod(){
//...
}

谁的锁:当前类的类锁,即该类的所有实例共享的锁
锁的谁:锁的是这个方法代码块,即线程要进入这个方法就必须先获得这个类的锁,该类的所有实例互斥。

  • 用在表达式上
1
2
3
synchronized(表达式){
//...
}

谁的锁:表达式如果是个实例,则是该实例的实例锁,同一个实例互斥;如果表达式是this,则是当前类的实例的实例锁,等同于方式用在实例方法上,当前类的同一个实例互斥;如果表达式是个Class,则是这个类的锁,即线程要进入这个同步代码块需要先获取这个类的锁,该类的所有实例互斥;
锁的谁:锁的是synchronized里面的代码块,根据表达式的不同,实现不同的互斥目的。

所以以下几种情况都是错误的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Item {
private int value;
public Item(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
  • 完全没有同步
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class LockTest implements Runnable {
private static final Item item = new Item(0);
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
increase();
}
}
private void increase() {
int value = item.getValue();
value++;
item.setValue(value);
}
public static void main(String[] args) throws InterruptedException {
LockTest test = new LockTest();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(item.getValue());
}
}
  • 表面上是加锁了,但是同一个实例,锁不住的,这相当于一个线程各种拿各自的实例锁,线程间不是同步互斥! 只需要把increase改成静态方法的就ok了,用类锁,达到线程互斥效果。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class LockTest implements Runnable {
private static final Item item = new Item(0);
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
increase();
}
}
private synchronized void increase() {
int value = item.getValue();
value++;
item.setValue(value);
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread( new LockTest());
Thread t2 = new Thread( new LockTest());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(item.getValue());
}
}
  • 表面上是加锁了,但是同一个实例,锁不住的!效果更上面的是一样的!修改为用同一个实例创建线程就好了,用实例锁搞定!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class LockTest implements Runnable {
private static final Item item = new Item(0);
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
increase();
}
}
private void increase() {
synchronized (this) {
int value = item.getValue();
value++;
item.setValue(value);
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new LockTest());
Thread t2 = new Thread(new LockTest());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(item.getValue());
}
}

ReentrantLock(重入锁)

ReentrantLock,重入锁,是synchronized的升级版,比synchronized功能更强大一些,在使用上是可以完全替代synchronized. 性能上,在JDK6开始,synchronized做了大量优化,二者性能相差不大了,但在JDK5重入锁要大大的高于synchronized。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class LockerTest implements Runnable {
private static final Item item = new Item(0);
private static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
try {
lock.lock();
increase();
} finally {
lock.unlock();
}
}
}
private void increase() {
int value = item.getValue();
value++;
item.setValue(value);
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new LockTest());
Thread t2 = new Thread(new LockTest());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(item.getValue());
}
}

ReentrantLock是静态实例,则说明该lock是类的锁,所有该类的实例同步互斥;否则是实例锁,只有同一个实例拥有这个锁,同实例访问lock和unlock之间的代码块是同步互斥的。相比synchronized、重入锁有如下几个 优势

  • 1.可以反复进入,同一个线程获得几个锁,在释放的时候也需要同等释放,要不然会死锁的;
  • 2.支持锁中断lockInterruptiblity(),防死锁,优先响应中断;
  • 3.支持限时获取锁,lock.tryLock(int,TimeUnit);在给定的时间范围捏尝试获取锁;
  • 4.支持尝试获取锁,tryLock不带参数,如果获取不到则立刻返回false,而不等待锁;
  • 5.公平锁ReentrantLock(boolean fair),排队获取锁,性能相对低下;
  • 6.配合Condition,可以让线程在合适的时间等待,得到通知后继续执行。
文章目录
  1. 1. synchronized(同步锁)
  2. 2. ReentrantLock(重入锁)