死锁
注:开发时要避免同步嵌套----易产生死锁
/**
死锁
*/
class TicketDemo implements Runnable{ //extends Thread{
private int tickets = 100;
Object obj = new Object();
//定义一个标签
boolean flag = true;
//使用同步代码块
public void run(){
if(flag){
while(true){
synchronized(obj){//obj锁
sale();//this锁
}
}
}
else{
while(true){
sale();//this锁
}
}
}
//同步函数
public synchronized void sale(){//this锁
synchronized(obj){//obj锁
if(tickets>0){
System.out.println(Thread.currentThread().getName()+"-------"+"function-----"+tickets--);
}
}
}
}
public class DieLock{
public static void main(String args[]) throws InterruptedException{
//new一个对象
TicketDemo t = new TicketDemo();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
//开启2个线程
t1.start();
//使线程可以执行同步代码块里的内容,而不是马上切换flag为false,防止代码块中的内容无法执行
Thread.sleep(10);
t.flag = false;
t2.start();
}
}
程序第一次运行可能会出现比较和谐的输出,不要气馁,多试几次~
有时候电脑太给力也不太好嘛 ~
这不就锁住了吗
死锁示例
可能你会比较疑惑,死锁这玩意儿我们开发时躲都躲不及,还特地去写它,are you kidding me ?
NO NO NO 在面试时,坐你对面的面试官很有可能要你现场手撸一个会发生死锁的程序
这么…emmm的要求???必须得满足不是!!
/**
死锁示例
*/
class DieLock{
//构建两个锁
public static final Object LOCKA = new Object();
public static final Object LOCKB = new Object();
}
class Lock implements Runnable{
//设置标志
private boolean flag;
//构造函数,初始化
Lock(boolean flag){
this.flag = flag;
}
//覆盖run方法,使锁A里有锁B,锁B里有锁A
public void run(){
if(flag){
synchronized(DieLock.LOCKA){
System.out.println("---if----locka");
synchronized(DieLock.LOCKB){
System.out.println("---if----lockb");
}
}
}else{
synchronized(DieLock.LOCKB){
System.out.println("---else----lockb");
synchronized(DieLock.LOCKA){
System.out.println("---else----locka");
}
}
}
}
}
public class DieLockDemo{
public static void main(String args[]){
//创建对象,并进行初始化。因是Boolean型,所以可以new2个,其他数据类型则不行
Lock a = new Lock(true);
Lock b = new Lock(false);
//创建线程
//Thread t1 = new Thread(a);
//Thread t2 = new Thread(b);
//启动线程,还记得之前的匿名对象的写法吗~
new Thread(a).start();
new Thread(b).start();
}
}
如果电脑性能太好,程序锁不住的话(虽然我不相信~~),但以防万一,可以再加上两个while,如此一来,必须锁得住。杠杠滴!
等待唤醒机制
- 前提:在同步中使用,即同个锁里
- wait():该方法使线程处于冻结状态,并将线程临时储存在线程池中。
- notify():唤醒指定线程池中的任意一个线程。
- notifyAll();唤醒指定线程池中的所有线程。
- 使用方法:使用时必须标识它们所属于的锁
- 标识方式:锁对象.wait()
话不多说,直接上代码
//资源
class Res{
private String name;//商品名称
private int count = 1 ;//商品数量
//生产者set
public synchronized void set(String name){
this.name = name+"-----"+count;
count++;
System.out.println(Thread.currentThread().getName()+"-----生产者------"+this.name);
}
//消费者get
//使用同步函数,使生产者和消费者遵循先生产再消费的顺序
public synchronized void get(){
System.out.println(Thread.currentThread().getName()+"-----消费者------"+this.name);
}
}
//生产者
class Producer implements Runnable{
private Res r;
Producer(Res r){
this.r = r;
}
public void run(){
//生产
//while(true){
r.set("大饼");
//}
}
}
//消费者
class Costomer implements Runnable{
private Res r;
Costomer(Res r){
this.r = r;
}
public void run(){
//消费
//while(true){
r.get();
//}
}
}
public class ProduCustom{
public static void main(String args[]) throws InterruptedException{
//创建资源
Res r = new Res();
Producer pro = new Producer(r);
Costomer cos = new Costomer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(cos);
t1.start();
Thread.sleep(10);
t2.start();
}
}
错误输出1
错误输出2
最终代码
//资源
class Res{
private String name;//商品名称
private int count = 1 ;//商品数量
//设置一个标志
private boolean flag ;
//生产者set
public synchronized void set(String name) {
if(flag){//默认false,false时生产,true时等待
try{wait();}catch(InterruptedException e) {}//捕获异常
this.name = name+"-----"+count;
count++;
System.out.println(Thread.currentThread().getName()+"-----生产者------"+this.name);
}
//生产完毕,修改标签,唤醒消费者
flag = true;
notify();
}
//消费者get
//使用同步函数,使生产者和消费者遵循先生产再消费的顺序
public synchronized void get() {
if(!flag) {//true时消费,false等待
try{wait();}catch(InterruptedException e) {}//捕获异常
System.out.println(Thread.currentThread().getName()+"-----消费者------"+this.name);
}
flag = false;
notify();
}
}
//生产者
class Producer implements Runnable{
private Res r;
Producer(Res r){
this.r = r;
}
public void run(){
//生产
while(true){
r.set("大饼");
}
}
}
//消费者
class Costomer implements Runnable{
private Res r;
Costomer(Res r){
this.r = r;
}
public void run(){
//消费
while(true){
r.get();
}
}
}
public class ProduCustom{
public static void main(String args[]) throws InterruptedException{
//创建资源
Res r = new Res();
Producer pro = new Producer(r);
Costomer cos = new Costomer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(cos);
t1.start();
t2.start();
}
}
输出简直是一派和谐,无比和谐~
多生产多消费问题
假设以上例子开启多个线程处理生产和消费,即多生产多消费,那么会出现输出的异常,原因是因为线程池唤醒的随机性会导致线程没有经过判断就进入代码块执行输出,解决办法是将if判断改成while判断,但随之而来的现象是会产生死锁,原因是本方线程若唤醒的是本方线程,那么就会导致所有线程都处于等待状态,解决办法是使用notifyAll唤醒线程池中的全部线程,线程执行前都需要判断标记,从而避免了生产了未消费或消费了未生产的情况。搞定!
//资源
class Res{
private String name;//商品名称
private int count = 1 ;//商品数量
//设置一个标志
private boolean flag ;
//生产者set
public synchronized void set(String name) {
//if改为while
while(flag){//默认false,false时生产,true时等待
try{wait();}catch(InterruptedException e) {}//捕获异常
this.name = name+"-----"+count;
count++;
System.out.println(Thread.currentThread().getName()+"-----生产者------"+this.name);
}
//生产完毕,修改标签,唤醒消费者
flag = true;
//使用notifyAll唤醒线程池中所有线程
this.notifyAll();
}
//消费者get
//使用同步函数,使生产者和消费者遵循先生产再消费的顺序
public synchronized void get() {
//if改为while
while(!flag) {//true时消费,false等待
try{wait();}catch(InterruptedException e) {}//捕获异常
System.out.println(Thread.currentThread().getName()+"-----消费者------"+this.name);
}
flag = false;
//使用notifyAll唤醒线程池中所有线程
this.notifyAll();
}
}
//生产者
class Producer implements Runnable{
private Res r;
Producer(Res r){
this.r = r;
}
public void run(){
//生产
while(true){
r.set("大饼");
}
}
}
//消费者
class Costomer implements Runnable{
private Res r;
Costomer(Res r){
this.r = r;
}
public void run(){
//消费
while(true){
r.get();
}
}
}
public class ProduCustom{
public static void main(String args[]) throws InterruptedException{
//创建资源
Res r = new Res();
//创建对象
Producer pro = new Producer(r);
Costomer cos = new Costomer(r);
//创建线程
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(cos);
Thread t4 = new Thread(cos);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
文章为学习笔记,如有不足之处还请指正