互斥同步
同步是指在多个此案成并发访问共享数据时,保证共享数据在同一个时刻只被一个线程使用。互斥是实现同步的一种手段。除此之外的手段还有:临界区,互斥量和信号量。
Java中,最基本的互斥同步手段就是synchronized关键字,如果Java程序中明确指定了对象参数,那么锁的对象就是这个对象的引用。如果没有指定,则根据synchronized修饰的是实例方法还是类方法,去取对应的对象实例或Class对象来作为锁对象。
因为synchronized锁是阻塞式的,且要阻塞或唤醒一个线程,都需要操作系统来帮忙完成,这就需要从用户态切换到核心态,这会耗费很多时间。所以说synchronized是一个重型锁。
java.util.concurrent包中的重入锁(ReentrantLock)也可以实现同步,基本用法上与synchronized相似,但相比synchronized,ReentrantLock增加了一些新的高级功能:等待可中断,可实现公平锁,可以绑定多个条件。
等待可中断即当持有锁的线程长时间不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。
公平锁即当多个线程在等待一个锁时,必须按照申请的时间顺序来依次获得锁;synchronized是非公平的,ReentrantLock默认也是非公平的,但可以通过构造函数来使用公平锁。需要强调的是公平锁在高并发的情境下是极其损耗性能的。
//创建一个非公平锁,默认是非公平锁
Lock lock = new ReentrantLock();
Lock lock = new ReentrantLock(false);
//创建一个公平锁,构造传参true
Lock lock = new ReentrantLock(true);
锁绑定多个条件是指一个ReentrantLock对象可以同时绑定多个Condition对象,而synchronized在实现多个判断条件时,需要新建多个锁。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestDemo7 {
static class NumberWrapper{
public int value = 1;
}
public static void main(String[] args) {
//初始化可重入锁
final Lock lock = new ReentrantLock();
//第一个条件当屏幕上输出到3
final Condition reachThreeCondition = lock.newCondition();
//第二个条件当屏幕上输出到6
final Condition reachSixCondition = lock.newCondition();
//NumberWrapper只是为了封装一个数字,一边可以将数字对象共享,并可以设置为final
//注意这里不要用Integer, Integer 是不可变对象
final NumberWrapper num = new NumberWrapper();
//初始化A线程
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
//需要先获得锁
lock.lock();
try {
System.out.println("threadA start write");
//A线程先输出前3个数
while (num.value <= 3) {
System.out.println(num.value);
num.value++;
}
//输出到3时要signal,告诉B线程可以开始了
reachThreeCondition.signal();
} finally {
lock.unlock();
}
lock.lock();
try {
//等待输出6的条件
reachSixCondition.await();
System.out.println("threadA start write");
//输出剩余数字
while (num.value <= 9) {
System.out.println(num.value);
num.value++;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
try {
lock.lock();
while (num.value <= 3) {
//等待3输出完毕的信号
reachThreeCondition.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
try {
lock.lock();
//已经收到信号,开始输出4,5,6
System.out.println("threadB start write");
while (num.value <= 6) {
System.out.println(num.value);
num.value++;
}
//4,5,6输出完毕,告诉A线程6输出完了
reachSixCondition.signal();
} finally {
lock.unlock();
}
}
});
//启动两个线程
threadB.start();
threadA.start();
}
}
网友评论