1、原始构成
- sync是关键字属于JVM层面,
monitorenter(底层通过monitor对象来完成,其实wait/notify等方法也依赖monitor对象,只有在同步代码块或方法中才能调用wait/notify等方法)
montitorexit - Lock是具体类(java.util.concurrent.locks.Lock)是API层面的锁
2、使用方法
- sync不需要手动去释放锁,当sync代码完成后系统会自动让线程释放对锁的占用,不会产生死锁。
- ReentrantLock 则需要手动去释放锁,若没有即使释放锁则会产生死锁。需要lock()和unlock()方法配合try/finally语句块来完成。
3、等待是否可中断
- sync不可中断,除非抛出异常或者正常执行完成。
- ReentrantLock 可中断,可以设置超时方法tryLock(long timeout,TimeUnit unit)、lockInterruptibly()放代码块中,调用interrupt()方法可中断。
4、加锁是否公平
- sync默认就是非公平锁。
- ReentrantLock 两者都可以,默认非公平锁,通过构造方法传入false即为公平锁。
5、锁绑定多个条件Condition
- sync没有
- ReentrantLock 用来实现分组唤醒需要唤醒的线程们,可以达到精准唤醒,而不是像sync随机唤醒或者全部唤醒。
附:当使用 this 加锁时,无论是同一个对象还是多个对象,使用的是同一把锁, 使用class加锁时,只有是同一个对象时才使用同一把锁,不同的对象之间的锁是不同的。
public static void main(String[] args) {
SynchronizedExample synchronizedExample = new SynchronizedExample();
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@SneakyThrows
@Override
public void run() {
//同一个对象 执行当前类 加的锁 synchronized (SynchronizedExample.class)
// synchronizedExample.classMethod();
// 不同对象 执行当前类 加的锁 synchronized (SynchronizedExample.class)
// SynchronizedExample synchronizedExample1 = new SynchronizedExample();
// synchronizedExample1.classMethod();
//同一个对象 执行当前类 加的锁 synchronized (this)
//synchronizedExample.thisMethod();
//不同对象对象 执行当前类 加的锁 synchronized (this)
SynchronizedExample synchronizedExample1 = new SynchronizedExample();
synchronizedExample1.thisMethod();
}
}).start();
}
}
public void classMethod() throws InterruptedException {
synchronized (SynchronizedExample.class){
System.out.println(String.format("当前执行线程:%s,执行时间:%s",Thread.currentThread().getName(),new Date()));
TimeUnit.SECONDS.sleep(1);
}
}
public void thisMethod() throws InterruptedException {
synchronized (this){
System.out.println(String.format("当前执行线程:%s,执行时间:%s",Thread.currentThread().getName(),new Date()));
TimeUnit.SECONDS.sleep(1);
}
}
针对第五条的例子:
AA线程打印5次,接着BB线程打印10次,接着CC线程打印 15次 ... 依次类推,循环10次。
可以使用sync实现,但是比较麻烦,下面为使用Lock实现:
package solo.juc_.blockqueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 使用 Condition 实现功能:
* AA线程打印5次,接着BB线程打印10次,接着CC线程打印 15次
* ...
* 依次类推,循环10次。
*/
class ShareResource {
private int number = 1; // AA 1 BB 2 CC 3
private Lock lock = new ReentrantLock();
//返回Condition实例支持相同的用途为做Object种监视器方法( wait , notify和notifyAll与使用时)内置监视器锁定。
Condition c1 = lock.newCondition();
Condition c2 = lock.newCondition();
Condition c3 = lock.newCondition();
public void print5() throws InterruptedException {
lock.lock();
try {
//防止虚假唤醒,不能使用if
while (number != 1) {
c1.await();
}
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
number = 2;
c2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print10() throws InterruptedException {
lock.lock();
try {
//防止虚假唤醒,不能使用if
while (number != 2) {
c2.await();
}
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
number =3;
c3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print15() throws InterruptedException {
lock.lock();
try {
//防止虚假唤醒,不能使用if
while (number != 3) {
c3.await();
}
for (int i = 1; i <= 15; i++) {
System.out.println(Thread.currentThread().getName() + "\t" + i);
}
number = 1;
c1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public class ConditionDemo_ {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
for (int i = 0; i < 10; i++) {
new Thread(()->{
try {
shareResource.print5();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"AA").start();
}
for (int i = 0; i < 10; i++) {
new Thread(()->{
try {
shareResource.print10();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"BB").start();
}
for (int i = 0; i < 10; i++) {
new Thread(()->{
try {
shareResource.print15();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"CC").start();
}
}
}
生产者消费者模型
V2.0
package solo.juc_.blockqueue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 使用 ReentrantLock 写生产者消费者模式
* 传统 模式是使用 synchronized进行加锁
*/
//线程操作资源类
class ShareData {
private int number = 0;
Lock lock = new ReentrantLock();
//返回Condition实例支持相同的用途为做Object种监视器方法( wait , notify和notifyAll与使用时)内置监视器锁定。
Condition condition = lock.newCondition();
public void increment() throws Exception {
// 同步代码块,加锁
try {
lock.lock();
// 多个 生产者 消费者的时候 不允许用 if, 必须用while
while (number != 0) {
//不允许生产
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName() + "number:\t" + number);
System.out.println(System.currentTimeMillis());
//通知 唤醒
condition.signalAll();
} finally {
lock.unlock();
}
}
public void decrement() throws Exception {
// 同步代码块,加锁
try {
lock.lock();
while (number == 0) {
//等待 不能消费
condition.await();
System.out.println(Thread.currentThread().getName() + "number:\t" + number);
}
number--;
System.out.println(Thread.currentThread().getName() + "number:\t" + number);
System.out.println(System.currentTimeMillis());
//通知 唤醒
condition.signalAll();
} finally {
lock.unlock();
}
}
}
public class ProdConsumerTraditionDemo {
public static void main(String[] args) {
ShareData shareData = new ShareData();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
shareData.increment();
} catch (Exception e) {
e.printStackTrace();
}
}
}, "AA").start();
new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
shareData.decrement();
} catch (Exception e) {
e.printStackTrace();
}
}
}, "BB").start();
}
}
V3.0
package solo.juc_.blockqueue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 生产者消费者 阻塞队列版 无需加锁
* 使用:volatile、CAS、atomicInteger、BlockQueue、线程交互、原子引用
*/
public class ProdConsumerBlockingQueueDemo_ {
public static void main(String[] args) {
// 传入具体的实现类, ArrayBlockingQueue
MyResource1 myResource = new MyResource1(new ArrayBlockingQueue<String>(10));
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t 生产线程启动");
try {
myResource.myProd();
System.out.println("");
System.out.println("");
} catch (Exception e) {
e.printStackTrace();
}
}, "prod1").start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t 生产线程启动");
try {
myResource.myProd();
System.out.println("");
System.out.println("");
} catch (Exception e) {
e.printStackTrace();
}
}, "prod2").start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t 消费线程启动");
System.out.println();
System.out.println();
try {
myResource.myConsumer();
} catch (Exception e) {
e.printStackTrace();
}
}, "consumer1").start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t 消费线程启动");
System.out.println();
System.out.println();
try {
myResource.myConsumer();
} catch (Exception e) {
e.printStackTrace();
}
}, "consumer2").start();
// 5秒后,停止生产和消费
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("");
System.out.println("");
System.out.println("5秒中后,生产和消费线程停止,线程结束");
myResource.stop();
}
}
class MyResource1 {
// 默认开启,进行生产消费
// 这里用到了volatile是为了保持数据的可见性,也就是当TLAG修改时,要马上通知其它线程进行修改
private volatile boolean FLAG = true;
// 使用原子包装类,而不用number++
private AtomicInteger atomicInteger = new AtomicInteger();
// 这里不能为了满足条件,而实例化一个具体的SynchronousBlockingQueue
BlockingQueue<String> blockingQueue = null;
// 而应该采用依赖注入里面的,构造注入方法传入
public MyResource1(BlockingQueue<String> blockingQueue) {
this.blockingQueue = blockingQueue;
// 查询出传入的class是什么
System.out.println(blockingQueue.getClass().getName());
}
/**
* 生产
* @throws Exception
*/
public void myProd() throws Exception{
String data = null;
boolean retValue;
// 多线程环境的判断,一定要使用while进行,防止出现虚假唤醒
// 当FLAG为true的时候,开始生产
while(FLAG) {
data = atomicInteger.incrementAndGet() + "";
// 2秒存入1个data
retValue = blockingQueue.offer(data, 2L, TimeUnit.SECONDS);
if(retValue) {
System.out.println(Thread.currentThread().getName() + "\t 插入队列:" + data + "成功" );
} else {
System.out.println(Thread.currentThread().getName() + "\t 插入队列:" + data + "失败" );
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "\t 停止生产,表示FLAG=false,生产介绍");
}
/**
* 消费
* @throws Exception
*/
public void myConsumer() throws Exception{
String retValue;
// 多线程环境的判断,一定要使用while进行,防止出现虚假唤醒
// 当FLAG为true的时候,开始生产
while(FLAG) {
// 2秒存入1个data
retValue = blockingQueue.poll(2L, TimeUnit.SECONDS);
if(retValue != null && retValue != "") {
System.out.println(Thread.currentThread().getName() + "\t 消费队列:" + retValue + "成功" );
} else {
FLAG = false;
System.out.println(Thread.currentThread().getName() + "\t 消费失败,队列中已为空,退出" );
// 退出消费队列
return;
}
}
}
/**
* 停止生产的判断
*/
public void stop() {
this.FLAG = false;
}
}