Java多线程-Lock锁的使用,以及生产者和消费者的实现

Posted Frank Q

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java多线程-Lock锁的使用,以及生产者和消费者的实现相关的知识,希望对你有一定的参考价值。

本文中将主要介绍Java多线程编程基础中的Lock锁对象的使用,以及如何一步一步实现Java代码的生产者与消费者;

1、Java中如何使用Lock锁以及死锁问题的描述
2、Java实现生产者与消费者的过程(一步一步优化的步骤)


1、Java中如何使用Lock锁以及死锁问题的描述
LOCK锁的出现:为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象LOCK;
Lock锁中最重要的个方法:
void lock()
void unlock()
下面就是Lock锁的一个简单的演示Demo,之后的死锁问题也会使用Lock锁来进行表现;


1)例子一:利用Lock对象实现售票机制:


SellTicket.java

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class SellTicket implements Runnable 

    // 定义票
    private int tickets = 100;

    // 定义锁对象
    private Lock lock = new ReentrantLock();

    @Override
    public void run() 
        while (tickets > 0) 
            // 之所以不带catch是因为保证在出错的情况下保证锁的释放
            try
                // 加锁
                lock.lock();

                if (tickets > 0) 
                    try 
                        Thread.sleep(100);
                     catch (InterruptedException e) 
                        e.printStackTrace();
                    

                    System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets-- + "张票" );
                
             finally 
                // 释放锁
                lock.unlock();
            
        
    

SellTicketDemo.java

public class SellTicketDemo 

    public static void main(String[] args) 

        // 创建资源对象
        SellTicket st = new SellTicket();

        // 创建窗口
        Thread thread1 = new Thread(st, "窗口一");
        Thread thread2 = new Thread(st, "窗口二");
        Thread thread3 = new Thread(st, "窗口三");

        // 开启线程
        thread1.start();
        thread2.start();
        thread3.start();
       

运行效果:

可以看见线程在运行的时候,基本上都是成片运行的,并没有比较好的交叉运行


2)例子二:利用Lock实现死锁机制


MyLock.java

 /**
 * 创建两把锁可以直接调用
 * @author YQ
 *
 */
public class MyLock 
    //创建两把锁对象
    public static final Object objA = new Object();
    public static final Object objB = new Object();

DieLock.java

public class DieLock extends Thread 

    private boolean flag;

    public DieLock(boolean flag) 
        this.flag = flag;
    

    @Override
    public void run() 
        if (flag) 
            synchronized (MyLock.objA) 
                System.out.println("if ObjA");
                synchronized (MyLock.objB) 
                    System.out.println("if ObjB");
                
            
         else 
            synchronized (MyLock.objB) 
                System.out.println("else ObjB");
                synchronized (MyLock.objA) 
                    System.out.println("else ObjA");
                
            
        
    

DieLockDemo.java

/**
 * 同步的弊端:
 *  A:效率低
 *  B:容易产生死锁
 * 死锁:
 *  两个或者两个以上的线程在争夺资源的过程中,发生的一种互相等待的现象
 * @author YQ
 */
public class DieLockDemo 
    public static void main(String[] args) 
        DieLock dieLock1 = new DieLock(true);
        DieLock dieLock2 = new DieLock(false);
        dieLock1.start();
        dieLock2.start();
    

运行效果:


2、Java实现生产者与消费者的过程(一步一步优化的步骤)


1)初始实现,定义一个Student的JavaBean的类,然后通过set保存Student的数据作为生产者,之后通过get取出Student的数据作为消费者,但是以下的实现仅仅只有一次!
Student.java

public class Student 

    private String name;
    private int age;

    public String getName() 
        return name;
    
    public void setName(String name) 
        this.name = name;
    
    public int getAge() 
        return age;
    
    public void setAge(int age) 
        this.age = age;
    

    @Override
    public String toString() 
        return "Student [name=" + name + ", age=" + age + "]";
    

SetThread.java

public class SetThread extends Thread

    private Student student;

    public SetThread(Student student) 
        super();
        this.student = student;
    

    @Override
    public void run() 
        student.setName("admin");
        student.setAge(24);
    

GetThread.java

public class GetThread extends Thread 

    private Student student;

    public GetThread(Student student) 
        super();
        this.student = student;
    

    @Override
    public void run() 
        System.out.println(student.getName() + "====" + student.getAge());
    

StudentDemo.java

public class StudentDemo 

    public static void main(String[] args) 

        // 创建两个线程的共用资源
        Student student = new Student();
        // 创建生产者和消费者
        SetThread thread1 = new SetThread(student);
        GetThread thread2 = new GetThread(student);

        //开启生产者和消费者线程
        thread1.start();
        thread2.start();
    

运行效果:


2)进一步的循环实现多次生产多次消费,使用的是同步的方式
Student.java代码同上!


GetThread.java

public class GetThread extends Thread 

    private Student student;

    public GetThread(Student student) 
        super();
        this.student = student;
    

    @Override
    public void run() 
        while (true) 
            synchronized (student) 
                System.out.println(student.getName() + "====" + student.getAge());
            
        
    

SetThread.java

public class SetThread extends Thread

    private Student student;

    private int x = 0;

    public SetThread(Student student) 
        super();
        this.student = student;
    

    @Override
    public void run() 
        while (true) 
            //必须是相同的同一把锁
            synchronized (student) 
                if (x%2 == 0) 
                    student.setName("admin");
                    student.setAge(24);
                 else 
                    student.setName("manager");
                    student.setAge(28);
                
                x++;
                   
         
    

StudentDemo.java

public class StudentDemo 

    public static void main(String[] args) 

        // 创建两个线程的共用资源
        Student student = new Student();
        // 创建生产者和消费者
        SetThread thread1 = new SetThread(student);
        GetThread thread2 = new GetThread(student);

        //开启生产者和消费者线程
        thread1.start();
        thread2.start();
    

运行效果(停止的时候出现的ad没有完整与线程的实现没有关系,主要是因为命令窗口的缓冲没有完整的原因):


3)真正实现生产者与消费者:生产者生产后消费者才可以消费,消费者消费之后生产者才可以生产如此往复:


Student.java

public class Student 

    private String name;
    private int age;

    //默认情况下不存在数据
    private boolean flag;

    public String getName() 
        return name;
    
    public void setName(String name) 
        this.name = name;
    
    public int getAge() 
        return age;
    
    public void setAge(int age) 
        this.age = age;
    

    public boolean isFlag() 
        return flag;
    
    public void setFlag(boolean flag) 
        this.flag = flag;
    
    @Override
    public String toString() 
        return "Student [name=" + name + ", age=" + age + "]";
    

SetThread.java

public class SetThread extends Thread

    private Student student;

    private int x = 0;

    public SetThread(Student student) 
        super();
        this.student = student;
    

    @Override
    public void run() 
        while (true) 
            // 必须是相同的同一把锁
            synchronized (student) 

                // 判断
                if (student.isFlag()) 
                    try 
                        student.wait();
                     catch (InterruptedException e) 
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    
                

                if (x%2 == 0) 
                    student.setName("admin");
                    student.setAge(24);
                 else 
                    student.setName("manager");
                    student.setAge(28);
                
                x++;

                // 修改标记
                student.setFlag(true);
                student.notify();
            

         
    

GetThread.java

public class GetThread extends Thread 

    private Student student;

    public GetThread(Student student) 
        super();
        this.student = student;
    

    @Override
    public void run() 
        while (true) 
            synchronized (student) 

                if (!student.isFlag()) 
                    try 
                        student.wait();
                     catch (InterruptedException e) 
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    
                

                System.out.println(student.getName() + "====" + student.getAge());

                student.setFlag(false);
                student.notify();
            
        
    

运行效果:
StudentDemo.java

/**
 * 等待唤醒:
 *      Object类中提供了三个方法:
 *      wait():等待;
 *      notify():唤醒单个线程;
 *      notifyAll():唤醒所有线程;
 * @author YQ
 *
 */
public class StudentDemo 

    public static void main(String[] args) 

        // 创建两个线程的共用资源
        Student student = new Student();
        // 创建生产者和消费者
        SetThread thread1 = new SetThread(student);
        GetThread thread2 = new GetThread(student);

        //开启生产者和消费者线程
        thread1.start();
        thread2.start();
    

运行效果:

以上是关于Java多线程-Lock锁的使用,以及生产者和消费者的实现的主要内容,如果未能解决你的问题,请参考以下文章

多线程--简单生产者与消费者(Lock锁)

多线程JUC并发篇常见面试详解

JUC - 多线程之Synchronized和Lock锁;生产者消费者模式

java多线程之生产者-消费者

通过Lock对象以及Condition对象实现多线程同步

爬虫:生产者消费者方法