JUC Lock锁
多线程编程步骤
运用了高内聚低耦合的方法
- 创建资源类,在资源类创建属性和方法
- 创建多个线程,调用资源类的操作方法
Lock接口实例
synchronized锁上锁和解锁的过程由jvm隐式的完成
Lock接口可以手动的实现上锁和解锁的过程
ReentrantLock(可重入锁)
ReentrantLock是lock接口的一个实现类,使用可重入锁的方法对线程的同步来进行控制
可重入锁:进入上锁退出解锁 主要的区别在于需要手动的上锁和解锁。
package com.xiaoxu.lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 可重入锁
* @author chenruxu
*/
//创建资源类
class LTicket{
//创建可重入锁
private final ReentrantLock lock = new ReentrantLock();
private int number=30;
//卖票的方法
public void sale(){
//上锁
lock.lock();
try {
//判读是否有票可卖
if(number>0){
System.out.println(Thread.currentThread().getName()+"卖出了第"+(+number--)+"张票");
}
}finally {
//解锁
lock.unlock();
}
}
}
public class LSaleTicket {
public static void main(String[] args) {
LTicket ticket = new LTicket();
//lamber表达式
/**
* AA线程
*/
new Thread(()->{
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"AA").start();
/**
* BB线程
*/
new Thread(()->{
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"BB").start();
/**
* CC线程
*/
new Thread(()->{
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"CC").start();
}
}
通过可重入锁多线程买票的并发问题,对卖票的过程加锁来解决了线程之间的不安全问题。
其中Lock是一个接口,在发生异常的时候不会和synchronized一样自动的释放锁,可能会引起死锁现象。
线程之间的通信
多线程编程的步骤2
-
创建资源类
-
在资源类中操作方法 判读 干活 通知。
-
创建多个线程,调用资源类操作方法,完成过程。
-
防止虚假唤醒的问题
实例一:a(add)线程 +1 b线程 -1
变量值为1的时候加1 0的时候a线程等待。
实现方式synchronized关键字实现和lock锁的实现方式
synchronized实现方式
package com.xiaoxu.synchroniz;
/**
* synchronized关键字实现
*
*/
//创建资源类
class Share{
//初始值
private int number = 0;
//+1的方法
public synchronized void incr() throws InterruptedException {
if (number!=0){ //判断number是否为0 等待
this.wait();
}
//如果number的值是0就加1操作
number++;
System.out.println(Thread.currentThread().getName()+"::"+number);
//通知其他的线程(个人理解就是解锁)
this.notifyAll();
}
/**
* -1的方法
*/
public synchronized void decr() throws InterruptedException {
if(number!=1){
this.wait();
}
//实现步骤
number--;
System.out.println(Thread.currentThread().getName()+"::"+number);
//通知其他的线程
this.notifyAll();
}
}
public class ThreadDemo1 {
public static void main(String[] args) {
Share share = new Share();
//创建多个线程
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.incr(); //+1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"AA").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.decr(); //-1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"BB").start();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tjw4cGRq-1638446376730)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20211202155350256.png)]
线程之间的通信虚假唤醒问题
产生虚假唤醒问题的代码
package com.xiaoxu.synchroniz;
/**
* synchronized关键字实现
*
*/
//创建资源类
class Share{
//初始值
private int number = 0;
//+1的方法
public synchronized void incr() throws InterruptedException {
if (number!=0){ //判断number是否为0 等待
this.wait();
}
//如果number的值是0就加1操作
number++;
System.out.println(Thread.currentThread().getName()+"::"+number);
//通知其他的线程(个人理解就是解锁)
this.notifyAll();
}
/**
* -1的方法
*/
public synchronized void decr() throws InterruptedException {
if(number!=1){
this.wait();
}
//实现步骤
number--;
System.out.println(Thread.currentThread().getName()+"::"+number);
//通知其他的线程
this.notifyAll();
}
}
public class ThreadDemo1 {
public static void main(String[] args) {
Share share = new Share();
//创建多个线程
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.incr(); //+1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"AA").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.decr(); //-1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"BB").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.incr(); //+1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"CC").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.decr(); //-1
} catch (InterruptedException e) {
e.printStakTrace();
}
}
},"DD").start();
}
}
产生虚假唤醒的原因:线程的虚假唤醒的问题。wait()方法释放锁之后,如果该线程继续抢到cpu的资源之后,会在wait()语句这里唤醒,if语句的判断会失效。解决的方法是将if语句换成对应的while语句解决虚假唤醒的问题。
下面是将原来的资源类中对资源进行操作的方法中进行锁条件判断语句中的if替换为while之后解决的过程。
package com.xiaoxu.synchroniz;
/**
* synchronized关键字实现
*
*/
//创建资源类
class Share{
//初始值
private int number = 0;
//+1的方法
public synchronized void incr() throws InterruptedException {
while (number!=0){ //判断number是否为0 等待
this.wait();
}
//如果number的值是0就加1操作
number++;
System.out.println(Thread.currentThread().getName()+"::"+number);
//通知其他的线程(个人理解就是解锁)
this.notifyAll();
}
/**
* -1的方法
*/
public synchronized void decr() throws InterruptedException {
while (number!=1){
this.wait();
}
//实现步骤
number--;
System.out.println(Thread.currentThread().getName()+"::"+number);
//通知其他的线程
this.notifyAll();
}
}
public class ThreadDemo1 {
public static void main(String[] args) {
Share share = new Share();
//创建多个线程
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.incr(); //+1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"AA").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.decr(); //-1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"BB").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.incr(); //+1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"CC").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.decr(); //-1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"DD").start();
}
}
Lock接口实现
package com.xiaoxu.lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* lock接口实现
*/
//创建资源类
class Share{
private int number = 0;
//创建lock
private Lock lock= new ReentrantLock();
private Condition condition=lock.newCondition();
//+1
public void incr() throws InterruptedException {
//加锁
lock.lock();
try {
//判断
while (number!=0){
condition.await();
}
//操作
number++;
System.out.println(Thread.currentThread().getName()+"::"+number);
//唤醒
condition.notify();
}finally {
//解锁
lock.unlock();
}
}
//-1
public void decr() throws InterruptedException {
//上锁
lock.lock();
try {
while (number!=1){
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"::"+number);
condition.notify();
}finally {
//解锁
lock.unlock();
}
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
Share share = new Share();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
线程的定制化通信
应用场景:
启动三个线程按照如下的要求进行
AA打印5次 BB打印10次 CC打印15次共执行10轮
使我们线程之间的顺序按照指定的顺序进行。思路为三个线程设置三个标志位。标志位1打印A5次 修改标志位为2通知B过程以此类推。最后一个CC线程的标志位改完1。完成操作。下面对代码进行实现。
package com.xiaoxu.lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//第一步 创建资源类
class ShareResource{
//定义标志位
private int flag = 1; //1 AA 2BB 3CC
//创建lock锁
private Lock lock = new ReentrantLock();
//创建三个condition
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
//打印5次
public void print5(int loop) throws InterruptedException {
lock.lock();
try{
//判断
while (flag!=1){
c1.await();
}
//干活
for (int i = 1; i <=5; i++) {
System.out.println(Thread.currentThread().getName()+"::"+i+"轮数"+loop);
}
//通知
flag = 2;
c2.signal();
}finally {
//释放锁
lock.unlock();
}
}
public void print10(int loop) throws InterruptedException {
//上锁操作
lock.lock();
try {
while (flag!=2){
c2.await();
}
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName()+"::"+i+"轮数"+loop);
}
//修改标志位 进行通知Cc线程
flag = 3;
c3.signal();
}finally {
//解锁
lock.unlock();
}
}
public void print15(int loop) throws InterruptedException {
//上锁操作
lock.lock();
try {
while (flag!=3){
c3.await();
}
for (int i = 1; i <= 15; i++) {
System.out.println(Thread.currentThread().getName()+"::"+i+"轮数"+loop);
}
//修改标志位 进行通知Cc线程
flag = 1;
c1.signal();
}finally {
//解锁
lock.unlock();
}
}
}
public class ThreadDemo3 {
public static void main(String[] args) {
ShareResource shareResource = new ShareResource();
//创建多线程
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
shareResource.print5(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"AA").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
shareResource.print10(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"BB").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
shareResource.print15(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"CC").start();
}
}