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();
    }   
}

運行效果:
Lock鎖運行效果
可以看見線程在運行的時候,基本上都是成片運行的,並沒有比較好的交叉運行


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();
    }

}

運行效果:
生產者消費者例子1運行效果


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();
    }
}

運行效果:
StudentDemo的運行效果

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章