锁(Lock,Synchronized)
公平锁:必须先来后到
非公平锁:十分不公平,可以插队,(默认使用非公平锁)
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
//非公平锁
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
//公平锁
sync = fair ? new FairSync() : new NonfairSync();
}
Synchronized
public class SynchronizedDemo {
public static void main(String[] args) {
SynchronizedTicket ticket=new SynchronizedTicket();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 60; i++) {
ticket.sale();
}
},"C").start();
}
}
//资源类OOP
class SynchronizedTicket{
//属性,方法
private int number=50;
//synchronized:队列,锁
public synchronized void sale(){
if (number>0){
System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"张票,剩余"+number);
}
}
}
Lock
public class LockDemo {
public static void main(String[] args) {
LockTicket ticket=new LockTicket();
new Thread(()->{for (int i = 0; i < 60; i++) ticket.sale();},"A").start();
new Thread(()->{for (int i = 0; i < 60; i++) ticket.sale();},"B").start();
new Thread(()->{for (int i = 0; i < 60; i++) ticket.sale();},"C").start();
}
}
//资源类OOP
/*
* Lock三部曲
*1,new ReentrantLock();
* 2,lock.lock();加锁
* */
class LockTicket{
Lock lock=new ReentrantLock();
//属性,方法
private int number=50;
public void sale(){
//加锁
lock.lock();
try {
if (number>0){
System.out.println(Thread.currentThread().getName()+"卖出了"+(number--)+"张票,剩余"+number);
}
}catch (Exception e){
e.printStackTrace();
}finally {
//解锁
lock.unlock();
}
}
}
Synchronized和Lock的区别
- Synchronized 是java的关键字,Lock是一个类
- Synchronzied 无法获取锁的状态,Lock可以获取锁的状态
- Synchronzied 可以自动释放锁,Lock不可以自动释放,需要手动加锁,手动释放锁,
可能会遇到死锁!
- Synchronzied 线程1(获得锁,阻塞),线程2(等待,因为线程1阻塞,会一直等待),Lock不会一直等待,Lock会有一个trylock方法,尝试获取锁,不会造长时间的等待
- Synchronzied 是可重入锁,不可以中断非公平的;Lock可重入,可以判断,可以自己设置公平或者非公平
- 生产者和消费者的关系
1)Synchronzied 版本
package com.marchsoft.juctest;
/**
* Description:
*
* @author jiaoqianjin
* Date: 2020/8/10 22:33
**/
public class ConsumeAndProduct {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
}
}
class Data {
private int num = 0;
// +1
public synchronized void increment() throws InterruptedException {
// 判断等待
if (num != 0) {
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName() + "=>" + num);
// 通知其他线程 +1 执行完毕
this.notifyAll();
}
// -1
public synchronized void decrement() throws InterruptedException {
// 判断等待
if (num == 0) {
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName() + "=>" + num);
// 通知其他线程 -1 执行完毕
this.notifyAll();
}
}
2)存在问题(虚假唤醒)
问题,如果有四个线程,会出现虚假唤醒
解决方式 ,if 改为while即可,防止虚假唤醒
结论:就是用if判断的话,唤醒后线程会从wait之后的代码开始运行,但是不会重新判断if条件,直接继续运行if代码块之后的代码,而如果使用while的话,也会从wait之后的代码运行,但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait。
这也就是为什么用while而不用if的原因了,因为线程被唤醒后,执行开始的地方是wait之后
package com.marchsoft.juctest;
/**
* Description:
*
* @author jiaoqianjin
* Date: 2020/8/10 22:33
**/
public class ConsumeAndProduct {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "D").start();
}
}
class Data {
private int num = 0;
// +1
public synchronized void increment() throws InterruptedException {
// 判断等待
while (num != 0) {
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName() + "=>" + num);
// 通知其他线程 +1 执行完毕
this.notifyAll();
}
// -1
public synchronized void decrement() throws InterruptedException {
// 判断等待
while (num == 0) {
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName() + "=>" + num);
// 通知其他线程 -1 执行完毕
this.notifyAll();
}
}
Lock版
-
Synchronized
-
notify
-
wait
-
Lock
-Condition -
await
-
signal
package com.Pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ConsumeAndProductLock {
public static void main(String[] args) {
Data2 data=new Data2();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.increment();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.decrement();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.increment();
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.decrement();
}
},"D").start();
}
}
class Data2{
private int number = 0;
Lock lock=new ReentrantLock();
Condition condition = lock.newCondition();
//等待
//condition.await();
// 唤醒全部
//condition.signalAll();
public void increment(){
lock.lock();
try {
//业务代码
while (number!=0){
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭锁
lock.unlock();
}
}
public void decrement(){
lock.lock();
try{
while (number==0){
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
Condition
精准的通知和唤醒线程
如果我们要指定通知的下一个进行顺序怎么办呢? 我们可以使用Condition来指定通知进程~
package com.Pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author xianing
* 顺序执行A,B,C
* */
public class B {
public static void main(String[] args) {
Data3 data=new Data3();
new Thread(()->{for (int i = 0; i < 10; i++) data.PrintA();},"A").start();
new Thread(()->{for (int i = 0; i < 10; i++) data.PrintB();},"B").start();
new Thread(()->{for (int i = 0; i < 10; i++) data.PrintC();},"C").start();
}
}
class Data3{//资源类
private Lock lock=new ReentrantLock();
//同步监视器
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int number=1;
public void PrintA(){
lock.lock();
try {
//判断-》逻辑执行-》通知
while (number!=1){
//等待
condition1.await();
}
System.out.println(Thread.currentThread().getName()+"=>AAAAAAAAA");
//唤醒指定的人
number=2;
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()+"=>BBBBBBBBB");
//唤醒指定C
number=3;
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()+"=>CCCCCCCCC");
//唤醒指定的人
number=1;
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}