1、JUC(lock和synchronized比较,生产者消费者
1、Lock锁
Synchronized和Lock的区别:参考
1、synchronized是内置关键字,Lock是一个类(接口)
2、synchronized无法判断获取锁的状态,Lock可以判断是否获取到了锁
3、synchronized会自动释放锁,lock必须要手动释放,(如果不释放,会形成死锁)
4、synchronized中线程1如果获得锁,线程2就会一直等待,直到线程1释放;lock锁就不会,tryLcok();
5、synchronized可重入锁,不可以中断,非公平锁;Lock锁可重入锁,可以判断锁,默认非公平(可以设置)
6、适合锁少量的代码同步问题;lock适合锁大量的同步代码(灵活度高)
锁是什么?如何判断锁的是谁?
synchronized锁的对象是方法的调用者!(对象)
如果是静态同步方法,则锁的是Class(类)
2、生产者和消费者问题
生产者和消费者问题(线程通信)(使用synchronized、wait()、notifyAll())
拓展问题:java中的notify和notifyAll有什么区别
package com.company.ThreadTest;
/**
* 线程之间的通信问腿:生产者和消费者
* 线程交替执行 A B 操作同一个变量 num=0
* A num+1
* B num-1
*/
public class TestProductorAndConsumer {
public static void main(String[] args) {
Data data = new Data();
//A线程
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
//B线程
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
/**
* 1、判断等待
* 2、业务
* 3、通知
*/
class Data{ //资源类
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
//判断等待
if (number!=0){
//等待
this.wait();
}
//业务
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知B线程可以消费了,我+1完毕
this.notifyAll();
}
//+1
public synchronized void decrement() throws InterruptedException {
//判断等待
if (number==0){
//等待
this.wait();
}
//业务
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知A线程可以生产了,我-1完毕
this.notifyAll();
}
}
结果:
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
A=>1
B=>0
问题:上述示例如果存在ABCD四个线程!结果可能出现如下:(虚假唤醒)
什么是虚假唤醒:当一个条件满足时,很多线程都被唤醒了,但是只有其中部分是有用的唤醒,其它的唤醒都是无用功。比如说买货,如果商品本来没有货物,突然进了一件商品,这是所有的线程都被唤醒了,但是只能一个人买,所以其他人都是假唤醒,获取不到对象的锁
package com.company.ThreadTest;
/**
* 线程之间的通信问腿:生产者和消费者
* 线程交替执行 A B 操作同一个变量 num=0
* A num+1
* B num-1
*/
public class TestProductorAndConsumer {
public static void main(String[] args) {
Data data = new Data();
//A线程
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
//B线程
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
//C线程
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
//D线程
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
/**
* 1、判断等待
* 2、业务
* 3、通知
*/
class Data{ //资源类
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
//判断等待
if (number!=0){
//等待
this.wait();
}
//业务
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知B线程可以消费了,我+1完毕
this.notifyAll();
}
//+1
public synchronized void decrement() throws InterruptedException {
//判断等待
if (number==0){
//等待
this.wait();
}
//业务
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知A线程可以生产了,我-1完毕
this.notifyAll();
}
}
A=>1
B=>0
C=>1
A=>2
C=>3
B=>2
B=>1
B=>0
C=>1
A=>2
C=>3
B=>2
C=>3
D=>2
D=>1
D=>0
A=>1
D=>0
A=>1
D=>0
虚假唤醒的解决办法:
只需将if (number!=0){} 换成while(number!=0){}
因为if只会执行一次,执行完会接着向下执行if()外边的;而while不会,直到条件满足才会向下执行while()外边的
JUC版的生产者和消费者问题:
JUC版的用法:
1605515700(1).jpg
package com.company.ThreadTest;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 线程之间的通信问腿:生产者和消费者
* 线程交替执行 A B 操作同一个变量 num=0
* A num+1
* B num-1
*/
public class TestJUCProductorAndConsumer {
public static void main(String[] args) {
Data2 data = new Data2();
//A线程
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
//B线程
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
//C线程
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
//D线程
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
/**
* 1、判断等待
* 2、业务
* 3、通知
*/
class Data2{ //资源类
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
//condition.await();//等待
//condition.signalAll();//唤醒全部
//+1
public void increment() throws InterruptedException {
lock.lock();//上锁
try {
//业务代码
//判断等待
while (number!=0){
//等待
condition.await();
}
//业务
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知B线程可以消费了,我+1完毕
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); //释放锁
}
}
//+1
public void decrement() throws InterruptedException {
lock.lock();//上锁
try {
//判断等待
while (number==0){
//等待
condition.await();
}
//业务
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
//通知A线程可以生产了,我-1完毕
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();//释放锁
}
}
}
结果:
A=>1
B=>0
C=>1
B=>0
C=>1
B=>0
C=>1
B=>0
C=>1
B=>0
C=>1
D=>0
A=>1
D=>0
A=>1
D=>0
A=>1
D=>0
A=>1
D=>0
思考:上述结果为随机状态,如何让ABCD变得有序
Condition(按ABC的顺序打印)
package com.company.ThreadTest;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 线程之间的通信问腿:生产者和消费者
* 线程交替执行 A B 操作同一个变量 num=0
* A num+1
* B num-1
*/
public class TestJUCProductorAndConsumer2 {
public static void main(String[] args) {
Data3 data = new Data3();
//A线程
new Thread(()->{
for (int i = 0; i < 5; i++) {
data.printA();
}
},"A").start();
//B线程
new Thread(()->{
for (int i = 0; i < 5; i++) {
data.printB();
}
},"B").start();
//C线程
new Thread(()->{
for (int i = 0; i < 5; i++) {
data.printC();
}
},"C").start();
}
}
/**
* 1、判断等待
* 2、业务
* 3、通知
*/
class Data3{ //资源类
private int number = 1;
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public void printA(){
lock.lock();
try {
while (number!=1){
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"-->AAAAA");
number = 2;
//唤醒指定的人B
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while (number!=2){
condition2.await();
}
System.out.println(Thread.currentThread().getName()+"-->BBBBB");
number = 3;
//唤醒指定的人C
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while (number!=3){
condition3.await();
}
System.out.println(Thread.currentThread().getName()+"-->CCCCC");
number = 1;
//唤醒指定的人
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
结果:
A-->AAAAA
B-->BBBBB
C-->CCCCC
A-->AAAAA
B-->BBBBB
C-->CCCCC
A-->AAAAA
B-->BBBBB
C-->CCCCC
A-->AAAAA
B-->BBBBB
C-->CCCCC
A-->AAAAA
B-->BBBBB
C-->CCCCC