
文章目录
众所周知、对于JAVA程序员而言,线程安全是并发编程中的一个重大关注点,所谓线程安全就是要保证在多个线程间读写共享数据保证正确性,如果不能保证,那就不是线程安全的了。 synchronized、ReentrantLock都是用来实现线程间同步,访问同步的代码需要先获得锁,保证每次都只能有一个线程进入同步块,代码执行完毕后释放锁,从而保证了线程间的安全。
锁藏在什么地方?
每个类都有一把锁,暂且叫类锁,所有这个类的实例都可以获取这个类的锁;
每个实例也都有一把锁,暂且叫实例锁,类锁和实例锁不是相同的,只有实例自己才能获得自己的实例锁,不同的实例获得实例锁是不一样的,这个很关键。
谁的锁,锁的谁?
关键字synchronized的用法
1 2 3
|
public synchornized void (){ }
|
谁的锁:当前实例的实例锁,不同实例获得的实例锁在这里不是同一把锁;
锁的谁:锁的是这个方法代码块,即线程要进入这个方法就必须先获得这个实例的锁,同一个实例互斥。
1 2 3
|
public static synchornized (){ }
|
谁的锁:当前类的类锁,即该类的所有实例共享的锁
锁的谁:锁的是这个方法代码块,即线程要进入这个方法就必须先获得这个类的锁,该类的所有实例互斥。
谁的锁:表达式如果是个实例,则是该实例的实例锁,同一个实例互斥;如果表达式是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); 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); 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); 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(); 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,可以让线程在合适的时间等待,得到通知后继续执行。
近期评论