锁实现提供了比使用同步方法和语句可以获得的更广泛的锁操作。它们允许更灵活的结构,可能具有非常不同的属性,并且可能支持多个关联的条件对象。
使用模板:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
示例:
public class SaleTicket {
/**
* 三个售票员 卖出 30张票
*
* @param args
* @throws Exception 线程 操作 资源类,高内聚低耦合
*/
public static void main(String[] args) throws Exception {
Ticket ticket = new Ticket();
new Thread(()->{
for (int i = 1; i <= 40; i++) ticket.sale(); },"A售票员").start();
new Thread(()->{
for (int i = 1; i <= 40; i++) ticket.sale(); },"B售票员").start();
new Thread(()->{
for (int i = 1; i <= 40; i++) ticket.sale(); },"C售票员").start();
}
}
class Ticket {
private int number = 30;
private Lock lock = new ReentrantLock();
public void sale() {
lock.lock();
try {
if (number > 0) {
System.out.println(Thread.currentThread().getName()
+ "\t 卖出" + number-- + "号票\t还剩" + number
);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
private Lock lock = new ReentrantLock();//获取锁
private Condition cd = lock.newCondition();//获取钥匙
生产者消费者:
class ShareDataOne {
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition cd = lock.newCondition();
public void incr() throws InterruptedException {
lock.lock();
try {
//判断
while (number != 0) {
cd.await();
}
//干活
number++;
System.out.println(Thread.currentThread().getName() + "\t" + number);
//通知
cd.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decr() throws InterruptedException {
lock.lock();
try {
//判断
while (number != 1) {
cd.await();
}
//干活
number--;
System.out.println(Thread.currentThread().getName() + "\t" + number);
//通知
cd.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
/**
* 现在两个线程
* 操作一个初始值为0的变量
* 实现一个线程对变量增加1,一个线程对变量减少1
* 交替,来10轮
* 、线程 操作 资源类 2、高内聚低耦合
* 1、判断
* 2、干活
* 3、通知
*
* 注意多线程之间的虚假唤醒
*/
public class NotifyWaitDemo {
public static void main(String[] args) {
ShareDataOne shareDataOne = new ShareDataOne();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
shareDataOne.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "AA").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
shareDataOne.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "BB").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
shareDataOne.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "CC").start();
new Thread(() -> {
for (int i = 1; i <= 10; i++) {
try {
shareDataOne.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "DD").start();
}
}
引入:多线程之间按顺序调用,实现A->B->C
三个线程启动,要求如下:
AA打印5次,BB打印10次,CC打印15次
接着
AA打印5次,BB打印10次,CC打印15次
…来10轮
class ShareData{
private int num = 1;//标志判断位:1:AA,2:BB,3:CC
private Lock lock = new ReentrantLock();
Condition cd1 = lock.newCondition();
Condition cd2 = lock.newCondition();
Condition cd3 = lock.newCondition();
public void print5(int total){
lock.lock();
try {
//判断
while (num!=1){
cd1.await();//不会释放锁
}
//干活
for (int i = 1; i <= 5 ; i++) {
System.out.println(Thread.currentThread().getName()+
"\t "+total+"\t"+i);
}
//通知
num = 2;
cd2.signal();//唤醒线程2
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print10(int total){
lock.lock();
try {
//判断
while (num!=2){
cd2.await();//不会释放锁
}
//干活
for (int i = 1; i <= 10 ; i++) {
System.out.println(Thread.currentThread().getName()+
"\t "+total+"\t"+i);
}
//通知
num = 3;
cd3.signal();//唤醒线程3
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();//主动释放锁
}
}
public void print15(int total){
lock.lock();
try {
//判断
while (num!=3){
cd3.await();//不会释放锁
}
//干活
for (int i = 1; i <= 15 ; i++) {
System.out.println(Thread.currentThread().getName()+
"\t "+total+"\t"+i);
}
//通知
num = 1;
cd1.signal();//唤醒线程1
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class ThreadOrderAccess {
public static void main(String[] args) {
ShareData shareData = new ShareData();
new Thread(()->{
for (int i = 1; i <=10 ; i++) {
shareData.print5(i);
System.out.println("此轮结束");
}
},"AA").start();
new Thread(()->{
for (int i = 1; i <=10 ; i++) {
shareData.print10(i);
System.out.println("此轮结束");
}
},"BB").start();
new Thread(()->{
for (int i = 1; i <=10 ; i++) {
shareData.print15(i);
System.out.println("此轮结束");
}
},"CC").start();
}
}
1.首先synchronized是java内置关键字,在jvm层面,Lock是个java类;
2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
3.synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
4.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
5.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)
6.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。