实现目标
我们设定一定空间的队列,由生产者进行生产(填充队列)和消费者进行消费(取队列数据)。生产者在队列满的时候需要等待,消费者在队列空的时候需要等待。同时保证多个生产者、消费者的线程安全和数据协同。
队列的设计
public class Buffer<T> {
private T[] data;
private int size;
private boolean empty;
private int reader;
private int writer;
public boolean isEmpty() {
return reader == writer;
}
public boolean isFull(){
return (writer + 1) % size == reader;
}
public int size(){
return (writer + size - reader) % size;
}
public Buffer(Class clazz, int size) {
this.data = (T[]) Array.newInstance(clazz, size + 1);
this.size = size + 1;
this.reader = 0;
this.writer = 0;
this.empty = true;
}
public boolean push(T ele){
if(isFull()){
return false;
}
data[writer] = ele;
writer = (++writer) % size;
return true;
}
public T pop(){
if(isEmpty()){
return null;
}
T val = data[reader];
reader = (++reader) % size;
return val;
}
}
这里使用循环数组实现了一个队列。这里设置了两个指针:读指针和写指针。读指针用于取出数据,写指针用于压入数据。取出数据之前先做队列非空判断,压入数据之前先做队列非满判断。
生产者设计
public class Producer implements Runnable{
private final Buffer<Integer> buffer;
public Producer(Buffer<Integer> buffer) {
this.buffer = buffer;
}
@Override
public void run() {
try {
while (true){
synchronized (buffer){
while (buffer.isFull()){
buffer.wait();
}
int random = new Random().nextInt(100);
buffer.push(random);
System.out.println(Thread.currentThread().getName() + ":" + buffer.size());
buffer.notify();
}
}
}catch (Exception e) {
System.err.println("hit Exception" + e);
}
}
}
生产者在一个while循环中反复执行以下操作:
1. 获取Buffer对象的对象锁。
2. 判断队列非满,如果满了调用对象锁的wait方法,进入等待池。
3. 队列非满的情况下,将数据添加到队列。
4. 通知其他线程当前可用。
消费者设计
public class Consumer implements Runnable{
private final Buffer<Integer> buffer;
public Consumer(Buffer<Integer> buffer) {
this.buffer = buffer;
}
@Override
public void run() {
try {
while (true){
Thread.sleep(1000);
synchronized (buffer){
while (buffer.isEmpty()){
buffer.wait();
}
int val = buffer.pop();
System.out.println(Thread.currentThread().getName() + ":" + buffer.size());
buffer.notify();
}
}
}catch (Exception e) {
System.err.println("hit Exception" + e);
}
}
}
消费者在一个while循环中反复执行以下操作:
1. 获取Buffer对象的对象锁。
2. 判断队列非空,如果空了调用对象锁的wait方法,进入等待池。
3. 队列非空的情况下,将数据从队列中取出。
4. 通知其他线程当前可用。
测试
public class Test {
public static void main(String[] args) {
Buffer<Integer> buffer = new Buffer<Integer>(Integer.class, 5);
for (int i = 0; i < 5; i++) {
new Thread(new Producer(buffer), "producer" + i).start();
}
for (int i = 0; i < 10; i++) {
new Thread(new Consumer(buffer), "consumer" + i).start();
}
}
}
补充:查看当前执行线程
Thread.currentThread()方法可以获取当前执行的线程。
package com.jdojo.threads;
public class CurrentThread extends Thread {
public CurrentThread(String name) {
super(name);
}
@Override
public void run() {
Thread t = Thread.currentThread();
String threadName = t.getName();
System.out.println("Inside run() method: " + threadName);
}
public static void main(String[] args) {
CurrentThread ct1 = new CurrentThread("First Thread");
CurrentThread ct2 = new CurrentThread("Second Thread");
ct1.start();
ct2.start();
// Let’s see which thread is executing the following statement
Thread t = Thread.currentThread();
String threadName = t.getName();
System.out.println("Inside main() method: " + threadName);
}
}
线程睡眠
睡眠线程是不会被分配CPU的时间片的,如果线程在睡眠之前已经占用了锁,睡眠期间他将继续占用锁。
package com.jdojo.threads;
public class LetMeSleep {
public static void main(String[] args) {
try {
System.out.println("I am going to sleep for 5 seconds.");
Thread.sleep(5000);
// The "main" thread will sleep
System.out.println("I woke up.");
}
catch(InterruptedException e) {
System.out.println("Someone interrupted me in my sleep.");
System.out.println("I am done.");
}
}
}
等你等到死
执行某线程的join方法,执行的线程将停下等待,直到被调用join的线程执行完毕。
package com.jdojo.threads;
public class JoinRight {
public static void main(String[] args) {
Thread t1 = new Thread(JoinRight::print);
t1.start();
try {
t1.join(); // "main" thread waits until t1 is terminated
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("We are done.");
}
public static void print() {
for (int i = 1; i <= 5; i++) {
try {
System.out.println("Counter: " + i);
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}