JDK1.5开始引入并发包,由此,并发编程开始了性能提升的旅程
第一部分:生产者和消费者
描述:在多线程中,存在多线程同时操作一个或者是多个资源的情况,当生产线程生产出来产品,消费线程才可以进行消费,如果当前产品已经不剩余,则消费线程会进入阻塞状态,如果当前产品已经生产完成或者是产品数量达到某一个条件,则生产线程进入阻塞状态,线程之间通过这种协作,能很好的提升资源的利用率
package com.milla.study.netbase.expert.concurrent.lock;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Package: com.milla.study.netbase.expert.concurrent.lock
* @Description: <生产者消费者>
* @Author: MILLA
* @CreateDate: 2020/6/2 13:22
* @UpdateUser: MILLA
* @UpdateDate: 2020/6/2 13:22
* @UpdateRemark: <>
* @Version: 1.0
*/
public class ProducerAndConsumerTest {
public static void main(String[] args) {
ProducerAndConsumerTest test = new ProducerAndConsumerTest();
Thread t1 = new Thread(() -> {
while (true) {
try {
Thread.sleep(800L);
test.producer();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "producer");
Thread t2 = new Thread(() -> {
while (true) {
try {
Thread.sleep(500L);
System.out.println(Thread.currentThread().getName() + ",消费数据:" + test.consumer());
System.out.println();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "consumer");
t1.start();
t2.start();
}
//产品code
private String productCode;
//锁
private ReentrantLock lock = new ReentrantLock();
//消费者信号量
private Condition consumer = lock.newCondition();
//生产者信号量
private Condition producer = lock.newCondition();
/**
* 生产者方法
*
* @throws InterruptedException
*/
public void producer() throws InterruptedException {
//加锁
lock.lock();
try {
//如果当前产品code为空,需要阻塞
while (!Objects.isNull(productCode)) {
producer.await();//阻塞
}
//TODO 真正的生产数据(模拟)
productCode = UUID.randomUUID().toString();
System.out.println(Thread.currentThread().getName() + ",生产数据:" + productCode);
//需要唤醒消费者信号量
consumer.signal();
} finally {
//最终需要释放锁
lock.unlock();
}
}
/**
* 消费者方法
*
* @return
* @throws InterruptedException
*/
public String consumer() throws InterruptedException {
//加锁
lock.lock();
try {
// 如果当前不存在可用的产品code需要阻塞
while (Objects.isNull(productCode)) {
consumer.await();//阻塞
}
//产品被消费掉后,需要唤醒生产者
producer.signal();
return productCode;
} finally {
//TODO 消费掉数据(模拟)
productCode = null;
//最终释放锁
lock.unlock();
}
}
}
第二部分:JDK中阻塞队列的实现方式.采用可重入锁和信号量进行控制
package com.milla.study.netbase.expert.concurrent.lock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Package: com.milla.study.netbase.expert.concurrent.lock
* @Description: <阻塞队列>
* @Author: MILLA
* @CreateDate: 2020/6/1 18:14
* @UpdateUser: MILLA
* @UpdateDate: 2020/6/1 18:14
* @UpdateRemark: <>
* @Version: 1.0
*/
public class ListBlockLockQueue {
/**
* 1.定义个队列
* 2.采用可重入锁及信号量进行控制
* 3.数据存储时,如果数据已经满了,则存储线程进行阻塞,唤醒获取数据线程
* 4.数据获取时,如果当前数据已经消费完,则获取线程进行阻塞,获取存储数据线程
* 5.数据的存储按照从0到队列的长度存入,如果存满则存储索引置0
* 6.数据的获取按照从0到队列的长度获取,如果存满则存储索引置0
*/
//定义队列容量
private Integer size = 5;
//采用集合定义一个队列
private final Object[] data = new Object[size];
//定义可重复锁
ReentrantLock lock = new ReentrantLock();
//获取为空信号量
Condition take = lock.newCondition();
//获取为满信号量
Condition put = lock.newCondition();
//获取数据索引值
int takeIndex;
//加入数据索引值
int putIndex;
//当前有多少个数据可取
int count;
/**
* 拉取一个数据
*
* @return
*/
public Object take() throws InterruptedException {
lock.lock();
try {
//没有数据可取-取数据线程阻塞
while (count == 0) {
take.await();//阻塞
}
//获取先存如数据
Object o = data[takeIndex];
//数据获取索引达到队列长度(说明当前数据取完了)-将获取索引置为0
if (++takeIndex == data.length) {
takeIndex = 0;
}
--count;
System.out.println(Thread.currentThread().getName() + ",takeIndex: " + takeIndex + " count: " + count);
put.signal();
return o;
} finally {
lock.unlock();
}
}
/**
* 存入数据
*
* @param obj
*/
public void put(Object obj) throws InterruptedException {
//先上锁
lock.lock();
try {
//如果已经满了-阻塞
while (count == data.length) {
//就不能继续添加
put.await();
}
//添加元素
data[putIndex] = obj;
//添加数据索引达到队列长度-将索引值重置为0
if (++putIndex == data.length) {
putIndex = 0;
}
++count;
System.out.println(Thread.currentThread().getName() + ",takeIndex: " + takeIndex + " count: " + count);
take.signal();
} finally {
//最后释放锁
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ListBlockLockQueue queue = new ListBlockLockQueue();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 3; i++) {
try {
queue.put("" + i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "一");
Thread t2 = new Thread(() -> {
while (true) {
try {
System.out.println(Thread.currentThread().getName() + ",获取数据: " + queue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "二");
t2.start();
t1.start();
Thread.sleep(1000);
queue.put("123456");
Thread.sleep(1000);
queue.put("00000");
Thread.sleep(1000);
queue.put("555555");
}
}
以上代码,均借鉴JDK 并发编程大神Doug Lea的示例代码,摘录如下:
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
PS:最好的代码果然是底层的代码中,经过这么多年的千锤百炼自然是精益求精;路漫漫其修远兮,吾将上下而求索,革命尚未成功,同志尚需努力!!!