本文中將主要介紹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();
}
}
運行效果: