在多线程中,为了使线程安全,我们经常会使用synchronized和Lock进行代码同步和加锁

synchronized关键字

synchronized是Java中的关键字,是一种同步锁。synchronized可以保证方法或代码块在运行时,同一时刻只有一个线程可以进入到临界区(互斥性),同时它还保证了共享变量的内存可见性。

synchronized使用方式有三种
1、同步块

//key必须是一个对象
synchronized(key) {
			// 同步代码
}

2、普通方法上面加

public synchronized void method() {
        // 同步代码
}

如果某个方法都是有可能出现线程安全问题,则建议加载方法上面,该方法在充当锁的钥匙!!!

3、静态方法上面

public synchronized static void method(){
        // 同步代码
}

Java中的每个对象都可以作为锁:

  • 普通同步方法,锁是当前实例对象。
  • 静态同步方法,锁是当前类的class对象。
  • 同步代码块,锁是括号中的对象。

没有加同步锁时

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();
    }
}

java锁synchronized用法 java synchronized lock_同步锁


加上同步锁后

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();
    }
}

java锁synchronized用法 java synchronized lock_面试_02

Lock接口和ReentrantLock实现类

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();

    }
}

java锁synchronized用法 java synchronized lock_java_03

区别

1、synchronized是java关键字,而Lock是java中的一个接口

2、synchronized会自动释放锁,而Lock必须手动释放锁

3、synchronized是不可中断的,Lock可以中断也可以不中断

4、通过Lock可以知道线程有没有拿到锁,而synchronized不能

5、synchronized能锁住方法和代码块,而Lock只能锁住代码块

6、Lock可以使用读锁提高多线程读效率

7、synchronized是非公平锁,ReentranLock可以控制是否公平锁