一、定义
synchronized:是个关键字,是一种可重入的锁。在进行加锁时,可以直接将synchronized写入方法名中,或者使用synchronized代码块。synchronized在进行加锁时,会记录获取锁的次数和释放锁的次数。
ReentrantLock:是个类,是一种可重入的锁。所以用该类进行加锁时,需要先创建其对象,使用对象调用方法进行加锁。ReentrantLock在获取锁时,可以调用tryLock()方法尝试获取锁,给其设定一个时间,超过这个时间没有获取到锁,就不再等待,可以去做别的处理。
二、代码实现案例
synchronized三种加锁方式
1、定义一个公共的对象充当锁
public class Demo02 {
public static void main(String[] args) throws InterruptedException {
//创建2个线程对象
Thread t1 = new AddThread();
Thread t2 = new DecThread();
//启动线程
t1.start();
t2.start();
//插队到Main线程之前
t1.join();
t2.join();
//输出count变量的值
System.out.println(Counter.count);
}
}
//计数器类
class Counter{
//公共的静态成量变量
public static int count = 0;
//公共的锁对象
public static final Object lock = new Object();
}
//递增线程
class AddThread extends Thread{
@Override
public void run() {
for(int i=0;i<10000;i++) {
//两个线程使用同一把锁(使用同一个对象)
synchronized (Counter.lock) {
//对count变量加1
Counter.count += 1;
}
}
}
}
//递减线程
class DecThread extends Thread{
@Override
public void run() {
for(int i=0;i<10000;i++) {
//两个线程使用同一把锁(使用同一个对象)
synchronized (Counter.lock) {
//对count变量减1
Counter.count -= 1;
}
}
}
}
2、使用当前this对象(必须是同一对象调用多个线程)
这种情况下既可以使用synchronized代码块,传入this对象进行加锁,也可以直接将synchronized写在方法名上,也表示的是使用this对象。但是这两种方法它们作用的范围不一样,第一种加锁只作用在Counter1.count += 1;这一句上,第二种加锁是对整个方法起作用。
public class Counter1 {
// 用于计数的公共变量
public static int count = 0;
// 递增
public void add() {
for (int i = 0; i < 10000; i++) {
//当前this对象
synchronized (this) {
Counter1.count += 1;
}
}
}
// 递减
public synchronized void dec() {
for (int i = 0; i < 10000; i++) {
//当前this对象
synchronized (this) {
Counter1.count -= 1;
}
}
}
}
public class Test1 {
public static void main(String[] args) throws InterruptedException {
//创建计数器类对象
Counter1 counter1 = new Counter1();
//使用当前(this)对象进行加锁
Thread t1 = new Thread() {
@Override
public void run() {
//必须是同一对象调用线程任务
counter1.add();
}
};
//使用当前(this)对象进行加锁
Thread t2 = new Thread() {
@Override
public void run() {
//必须是同一对象调用线程任务
counter1.dec();
}
};
//启动线程
t1.start();
t2.start();
//插入到Main线程前执行
t1.join();
t2.join();
//输出count变量的值
System.out.println(Counter1.count);
}
}
3、使用当前对象的class对象
public class Counter2 {
// 用于计数的公共变量
public static int count = 0;
// 递增
public void add() {
for (int i = 0; i < 10000; i++) {
//当前class对象
synchronized (Counter2.class) {
Counter2.count += 1;
}
}
}
// 递减
public void dec() {
for (int i = 0; i < 10000; i++) {
//当前class对象
synchronized (Counter2.class) {
Counter2.count -= 1;
}
}
}
}
public class Test2 {
public static void main(String[] args) throws InterruptedException {
//创建两个对象
Counter2 counterOps01 = new Counter2();
Counter2 counterOps02 = new Counter2();
//线程1
//两个对象调用不同的线程,使用当前类的class对象进行加锁
Thread t1 = new Thread() {
@Override
public void run() {
//不同的对象调用不同的线程任务
counterOps01.add();
}
};
//线程2
Thread t2 = new Thread() {
@Override
public void run() {
//不同的对象调用不同的线程任务
counterOps02.dec();
}
};
//启动线程
t1.start();
t2.start();
//插入到Main线程前执行
t1.join();
t2.join();
//输出count变量的值
System.out.println(Counter2.count);
}
}
ReentrantLock类加锁方式
public class Demo01 {
public static void main(String[] args) throws InterruptedException {
//创建对象
Counter counter = new Counter();
//线程1
Thread t1 = new Thread() {
@Override
public void run() {
counter.add();
}
};
//线程2
Thread t2 = new Thread() {
@Override
public void run() {
counter.dec();
}
};
//启动线程
t1.start();
t2.start();
//插入到Main线程前执行
t1.join();
t2.join();
//输出count变量的值
System.out.println(Counter.count);
}
}
//计算器类
class Counter {
//公共的静态成量变量
public static int count = 0;
//创建公共的锁对象
private final ReentrantLock lock = new ReentrantLock();
//递增
public void add() {
for(int i=0;i<10000;i++){
//加锁(调用lock()方法)
lock.lock();
try {
Counter.count += 1;
//为啥不将释放锁写入这里?
//因为如果这里的代码一旦抛出异常就会导致锁释放不了
//而finally代码块中的代码无论何种情况都会执行
//lock.unlock();
}finally {
//释放锁(调用lock()方法)
lock.unlock();
}
}
}
//递减
public void dec() {
for(int i=0;i<10000;i++){
//加锁
lock.lock();
try {
Counter.count -= 1;
}finally {
//释放锁
lock.unlock();
}
}
}
}
ReentrantLock获取锁的两种策略:一是非公平策略:线程每次尝试获取锁时,不会让等待久的线程获得锁。二是公平策略:线程每次尝试获取锁时,都会让等待最久的线程优先获得锁。
可以通过创建对象时,传入的参数确定采用哪种策略,传入true表示为公平策略,否则为非公平策略(默认采用非公平策略)。
三、相同点和不同点
1、相同点:都是一种可重入的锁。
2、不同点:synchronized获取锁的方式是线程之间进行抢占,而ReentrantLock获取锁的方式是尝试获取锁。synchronized释放锁是自动释放,而ReentrantLock释放锁是调用unlock()。synchronized获取锁的策略是非公平策略,而ReentrantLock获取锁的策略是非公平策略和公平策略。