在多线程中,为了使线程安全,我们经常会使用synchronized和Lock进行代码同步和加锁
synchronized是Java中的关键字,是一种同步锁。synchronized可以保证方法或代码块在运行时,同一时刻只有一个线程可以进入到临界区(互斥性),同时它还保证了共享变量的内存可见性。
synchronized使用方式有三种:
1、同步块
//key必须是一个对象
synchronized(key) {
// 同步代码
}
2、普通方法上面加
public synchronized void method() {
// 同步代码
}
如果某个方法都是有可能出现线程安全问题,则建议加载方法上面,该方法在充当锁的钥匙!!!
3、静态方法上面
public synchronized static void method(){
// 同步代码
}
Java中的每个对象都可以作为锁:
没有加同步锁时
public class TestThread04 implements Runnable{
private int count;
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
count++;
}
System.out.println(Thread.currentThread().getName() + "count = " + count);
}
public static void main(String[] args) {
TestThread04 task = new TestThread04();
new Thread(task).start();
new Thread(task).start();
}
}
加上同步锁后
public class TestThread04 implements Runnable{
private int count;
// Object obj = new Object();
@Override
public synchronized void run() {
for (int i = 0; i < 10000; i++) {
// ++ -- 不具备原子性
// synchronized (obj){
// count++; Thread-0count = 19401 ,Thread-1count = 20000
// }
// count++;
}
System.out.println(Thread.currentThread().getName() + "count = " + count);
}
public static void main(String[] args) {
TestThread04 task = new TestThread04();
new Thread(task).start();
new Thread(task).start();
}
}
synchronized同步锁在jdk7之前,默认调用系统锁(重量级锁)
基于如上原因,jdk5,JUC包诞生,提供了一种全新的锁——Lock锁
Lock锁:
lock锁,是jdk5.0提供的锁、是一个可重入锁(ReentrantLock)
可以充当公平锁、也可以是不公平锁
一旦加锁,最后必须释放该锁,否则会出现死锁现象。
将释放锁的代码一定要放在finally中!!!
Lock接口
public interface Lock {
// 加锁
void lock();
// 能够响应中断
void lockInterruptibly() throws InterruptedException;
// 非阻塞获取锁
boolean tryLock();
// 非阻塞超时获取锁
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
// 解锁
void unlock();
// 定义阻塞条件
Condition newCondition();
}
lock():用来获取锁,如果锁被其他线程获得则进行等待,需要和unlock方法配合主动释放锁。发生异常时,不会主动释放锁,所以释放锁的操作放在finally块中
lockInterruptibly():当通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。也就使说,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程
tryLock():用来尝试获取锁,如果获取成功,则返回true。如果获取失败则返回false。也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待
tryLock(long time, TimeUnit unit):和tryLock()类似。只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果一开始拿到锁或者在等待期间内拿到了锁,则返回true
unlock():解锁,也就是释放锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock extends Thread {
private int count;
private Lock lock = new ReentrantLock();
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
try {
// 加锁
lock.lock();
count++;
} finally {
// 一定要记得释放锁!!
// 建议将释放锁的代码一定要放在finally中!!!
lock.unlock();
}
}
System.out.println(Thread.currentThread().getName()+ ":count = "+ count);
}
public static void main(String[] args) {
TestLock task = new TestLock();
new Thread(task).start();
new Thread(task).start();
}
}
1、synchronized是java关键字,而Lock是java中的一个接口
2、synchronized会自动释放锁,而Lock必须手动释放锁
3、synchronized是不可中断的,Lock可以中断也可以不中断
4、通过Lock可以知道线程有没有拿到锁,而synchronized不能
5、synchronized能锁住方法和代码块,而Lock只能锁住代码块
6、Lock可以使用读锁提高多线程读效率
7、synchronized是非公平锁,ReentranLock可以控制是否公平锁