Lock锁以及死锁的形成与解决

上面学到的synchronized可以帮助我们在多线程环境下用作为线程安全的同步锁,接下来我们将会学习一个Lock接口的使用,Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构

可以使用Lock锁进行具体的锁定操作类 提供了具体的实现类:ReentrantLock

Lock锁中的方法:加锁(lock())并且去释放锁(unlock())

例如:

package com.westos.Lock;
 
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
public class MyLock implements Runnable{

//定义票的变量
private int tickets=7;
//定义Lock接口对象
private  Lock lock=new ReentrantLock();
@Override
public void run() {

while(true) {
//给这个循环语句整体try...catch一下以方便后面的释放锁
try {
//void lock()获取锁
lock.lock();
if(tickets>0) {
//当每个线程进来时让它睡眠0.1秒 
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
//void unlock()释放锁。
lock.unlock();
}
}
}
}

 

创建一个测试类:

package com.westos.Lock;
 
public class LockDome {
 
public static void main(String[] args) {

//创建资源类对象
MyLock ml=new MyLock();

//创建Thread类对象
Thread t1=new Thread(ml,"窗口1");
Thread t2=new Thread(ml,"窗口2");
Thread t3=new Thread(ml,"窗口3");

//启动线程
t1.start();
t2.start();
t3.start();
}
}
 
运行结果:
窗口1正在出售第7张票
窗口3正在出售第6张票
窗口2正在出售第5张票
窗口2正在出售第4张票
窗口1正在出售第3张票
窗口3正在出售第2张票
窗口2正在出售第1张票

 

上面介绍了Lock锁,下来我们看看死锁(DieLock)的引入:

package com.westos.DieLock;
 
public class MyThread {
 
//创建两个锁对象
public static  Object obj=new Object();
public static  Object obj2=new Object();
}
 
package com.westos.DieLock;
 
public class MyDieLock extends Thread{
 
//声明一个成员变量
private boolean flag;
 
public MyDieLock(boolean flag) {
super();
this.flag = flag;
}

@Override
public void run() {
if(flag) {
synchronized(MyThread.obj) {
System.out.println("if obj");
synchronized(MyThread.obj2) {
System.out.println("if.obj2");
}
}
}else {
synchronized(MyThread.obj2) {
System.out.println("else obj2");
synchronized(MyThread.obj) {
System.out.println("else obj");
}
}
}
}
}

 

 

package com.westos.DieLock;
 
public class LieLockDome {
 
public static void main(String[] args) {
//创建资源类对象
MyDieLock mdl=new MyDieLock(true);
MyDieLock mdl2=new MyDieLock(false);

//启动线程
mdl.start();
mdl2.start();
}
}
运行结果:
/*
第一种情况:
if obj
else obj2
 
第二种情况:
else obj2
if obj
 
第三种情况:这是一种理想状态
else obj2
else obj
if obj
if.obj2
 
 */

分析上述产生死锁的原因:

当你在run方法中的if...else..语句中假设首先进来的是mdl线程,那么他将会运行if语句中的代码,先执行obj锁,然后输出“if  obj”当它继续去执行锁obj2的时候,此时,mdl2线程也抢占到CPU进来了,那么他将会去执行else中的代码,首先执行了锁obj2然后输出了“else  obj2”,然后他想接着去执行锁obj,去输出“else  obj”时。却因为锁objif语句中还没有被释放掉,无法执行,所以在等待if语句中的锁obj释放掉。而同样if语句中执行完锁obj后想去执行锁obj2时,也发现锁obj2else语句中正在执行没有被释放,所以也在等待锁obj2的释放,所以二者都在互相等待对方释放锁,就这样形成了死锁。上面的第三种情况是一种理想状态,它是等一个线程进入if语句或者else语句中执行锁后然后又释放掉锁,此时另一个进程也进来执行另一个锁,并且释放掉它,就这样不会形成死锁

 

上面我们了解了死锁的形成,我们可以知道死锁会影响线程之间的通信,那么如何能够解决死锁这个问题呢?

这是就需要用到java中的生产消费者模式:

这个模式就是相当于一个早餐,生产者需要不断地去做早餐,做出来的早餐还需要被消费者所消费。但是当生产者没有生产出数据时,消费者在等待数据或者生产者产生数据,消费者却没有接受数据,这样就会容易造成死锁,所以我们需要生产者产生数据后去唤醒消费者来接受,当消费者需要数据时,就唤醒生产者来生产收据,这样就会很好的解决死锁的问题

例如:

先定义一个学生类并设置需要的属性:

package com.westos.Thread3;
 
public class Student {
 
//设置学生类属性
String name;
int age;
boolean flag;
}

 

再定义一个生产者类来生产数据:

package com.westos.Thread3;
 
public class SetThread implements Runnable{
//设置学生类对象
private Student  s;
private int x;
//设置该类的有参构造
public SetThread(Student s) {
super();
this.s = s;
}
@Override
public void run() {
while(true) {
//加入同步锁,可以解决线程之间的抢占CPU造成数据重复或者错位的现象
synchronized(s) {
//如果生产者中没有数据就需要等待生产者去生产数据
if(s.flag) {
try {
s.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//生产者在生产数据
if(x%2==0) {
s.name="迪丽热巴";
s.age=25;
}else {
s.name="井杰";
s.age=23;
}
x++;
//当生产好数据后,就需要唤醒消费者来消费数据
s.flag=true;
s.notify();
}
}
}
}

 

 

再定义一个消费者类来消费数据:

package com.westos.Thread3;
 
/**
 *消费者线程
 */
public class GetThread implements Runnable{
 
//创建学生类对象
private Student s;
//创建该类的有参构造
public GetThread(Student s) {
super();
this.s = s;
}
@Override
public void run() {
while(true) {

//设置同步锁
synchronized(s) {
//如果消费者还有数据,就需要等待先去消费掉它
if(!s.flag) {
try {
s.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(s.name+"="+s.age);
//如果消费者没有数据了,就需要唤醒生产者去生产数据
s.flag=false;
s.notify();
}

}
}
}

 

在创建一个测试类:

package com.westos.Thread3;
 
public class StudentDome {
 
public static void main(String[] args) {

//创建学生类对象
Student s=new Student();
//创建资源类对象
SetThread st=new SetThread(s);
GetThread gt=new GetThread(s);

//创建Thread类对象
Thread t1=new Thread(st);
Thread t2=new Thread(gt);
//启动线程
t1.start();
t2.start();
}
}
运行结果:
迪丽热巴=25
井杰=23
迪丽热巴=25
井杰=23
迪丽热巴=25
井杰=23
迪丽热巴=25
井杰=23
迪丽热巴=25
...

 

这样就不会造成线程错乱和一大片一大片出现线程的现象

你可能感兴趣的:(Lock锁以及死锁的形成与解决)