线程安全
- 线程安全概念:当多个线程访问某个类(对象或方法)时,这个类始终都能表现出正确的行为,那么这个类(对象或方法)就是线程安全的。
- synchronized:可以在任意对象及方法上加锁,而加锁的这段代码称为“互斥区”或“临界区”。
- 多个线程多个锁:多个线程,每个线程都可以拿到自己指定的锁,分别获得锁之后,执行synchronized方法体的内容。
/**
* synchronized加锁
* 当多个线程访问run方法时,以排队的方式进行处理(按cpu分配的先后顺序而定),一个线程想要执行synchronized修饰
* 的方法的代码,首先尝试获得锁,如果拿到锁,执行synchronized代码内容,拿不到锁,这个线程就会不断的尝试获得这把锁,
* 直到拿到为止,多个线程同时去竞争这把锁。
*/
public synchronized void run(){
count--;
System.out.println(this.currentThread().getName() + " count= " + count);
}
/**
* 关键字synchronized取得的锁都是对象锁,而不是把一段代码(方法)当做锁,
* 所以线程先执行synchronized关键字的方法,那个线程就持有该方法所属对象的锁(Lock),
* 两个对象,线程获得就是两个不同的锁,它们互不影响。
* 在静态方法上加synchronized关键字,表示锁定.class类,类级别对的锁。
*/
public static synchronized void printNum(String tag){
try {
if (tag.equals("a")) {
num = 100;
System.out.println("tag a,set num over ");
Thread.sleep(1000);
}else {
num = 200;
System.out.println("tag b,set num over ");
}
System.out.println("tag " + tag + ",num = " + num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
对象锁的同步和异步
- 同步:synchronized,同步的概念就是共享。
- 异步:asynchronized,异步的概念就是独立,互相之间不受任何制约。
public class MyObject {
public synchronized void method1() {
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public synchronized void method2() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
final MyObject mo = new MyObject();
/**
* t1线程先持有MyObject对象的锁,t2可以以异步的方式调用对象中的非synchronized修饰的方法
* t1线程先持有MyObject对象的锁,t2线程如果在这个调用对象中的synchronize方法则需等待,也就是同步
*/
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
mo.method1();
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
mo.method2();
}
}, "t2");
t1.start();
t2.start();
}
}
同步的目的是为了线程安全,线程安全需要满足两个特性:
- 原子性(同步)
- 可见性
在设计程序是要注意整体性,比如要注意脏读。
public class DirtyRead {
private String username = "shuai";
private String password = "123";
public synchronized void setValue(String username,String password){
this.username = username;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.password = password;
System.out.println("setValue最终结果:username = " + username + ",password = " + password);
}
/**
* 对一个对象的方法加锁的时候,需要考虑业务的整体性,即为setValue/getValue方法同时加锁,
* 保证业务的原子性,从而保证业务的一致性
*/
public synchronized void getValue(){
System.out.println("getValue方法得到:username = " + this.username + ",password = " + this.password);
}
public static void main(String[] args) {
final DirtyRead dr = new DirtyRead();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
dr.setValue("airc", "1234");
}
});
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
dr.getValue();
}
}
synchronized锁重入:
关键字synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到了一个对象的锁后,再次请求此对象时是可以再次得到该对象的锁。
/**
* 方法之间的锁可重入
*/
public class ReentLock {
public synchronized void method1(){
System.out.println("method1");
method2();
}
public synchronized void method2(){
System.out.println("method2");
method3();
}
public synchronized void method3(){
System.out.println("method3");
}
public static void main(String[] args) {
final ReentLock sd = new ReentLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
sd.method1();
}
});
t1.start();
}
}
/**
* 类继承,锁重入
*/
public class ReentLock2 {
static class Main {
public int i = 10;
public synchronized void operationSup(){
i--;
System.out.println("Main print i = " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class Sub extends Main {
public synchronized void operationSub(){
try {
while(i>0){
i--;
System.out.println("Sub print i = " + i);
Thread.sleep(100);
super.operationSup();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
Sub sub = new Sub();
sub.operationSub();
}
});
t1.start();
}
}
对于web应用程序,异常释放锁的情况,如果不及时处理,很可能对应用程序业务逻辑产生严重的错误,比如执行一个队列认为,很多对象都去在等待第一个对象正确执行完毕再去释放说,但是第一个对象由于异常的出现,导致业务逻辑没有正常执行完毕,就释放了锁,后续对象执行的都是错误的逻辑。
synchronized代码块
使用synchronized声明的方法在某些情况下是有弊端的,比如A线程调用同步的方法执行一个很长时间的任务,那么B线程就必须等待比较长的时间才能执行,这样的情况下可以使用synchronized代码块去优化代码执行时间,也就是通常所说的减小锁的粒度。
注意点:不要使用String的常量加锁,会出现死循环问题。
锁对象的改变问题,当使用一个对象进行加锁的时候,要注意对象本身发生改变的时候,那么持有的锁就不同,如果对象本身不发生改变,那么依然是同步的,即使是对象的属性发生了改变。
public class ObjectLock {
public void method1(){
synchronized (this) {//对象锁
System.out.println("do method1..");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void method2(){
synchronized (ObjectLock.class) {//类锁
System.out.println("do method2..");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private Object lock = new Object();
public void method3(){
synchronized (lock) {//任何对象锁
System.out.println("do method3");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
volatile
volatile概念:volatile关键字的主要作用是使变量在多个线程间可见。
在java中,每个线程都会有一块工作内存区,其中存放着所有线程共享的主内存中的变量值的拷贝。当线程执行时,他在自己的工作内存区中操作这些变量。为了存取一个共享的变量,一个线程通常先获取锁定并去清除它的内存工作区,把这些共享变量从线程的共享内存中正确的装入到他自己所在的工作内存区中,当线程解锁时保证该工作内存区中变量的值写回到共享内存中。
一个线程可以执行的操作有使用(use)、复制(assign)、装载(load)、存储(store)、锁定(lock)、解锁(unlock)。
而主内存可以执行的操作有读(read)、写(write)、锁定(lock)、解锁(unlock),每个操作都是原子的。
volatile的作用就是强制线程到主内存(共享内存)里去读取变量,而不去线程工作内存区里去读取,从而实现了多个线程间的变量可见,也就是满足线程安全的可见性。
public class RunThread extends Thread{
/**
* 被volatile修饰,当变量改变时强制线程执行引擎去内存里读取
*/
private volatile boolean isRunning = true;
private void setRuning(boolean isRunning){
this.isRunning = isRunning;
}
public void run(){
System.out.println("进入run方法..");
while(isRunning == true){
//
}
System.out.println("线程停止");
}
public static void main(String[] args) throws InterruptedException {
RunThread rt = new RunThread();
rt.start();
Thread.sleep(3000);
rt.setRuning(false);
System.out.println("isRunning的值已经被设置了false");
Thread.sleep(1000);
System.out.println(rt.isRunning);
}
}
volatile关键字的非原子性:
volatile关键字虽然拥有多个线程之间的可见性,但是却不具备同步性(原子性),可以算上是一个轻量级的sunchronized,性能要比synchronized强很多,不会造成阻塞,一般volatile用于只针对于多个线程可见的变量操作,并不能代替synchronized的同步功能。
volatile关键字只具有可见性,没有原子性。要实现原子性建议使用atomic类的系列对象,支持原子性操作(automic类只保证本身方法原子性,并不保证多次操作的原子性)。
public class VolativeNoAutomic extends Thread{
// private static volatile int count;
/**
* AtomicInteger具有原子性
*/
private static AtomicInteger count = new AtomicInteger(0);
private static void addCount(){
for (int i = 0; i < 1000; i++) {
// count++;
count.incrementAndGet();
}
System.out.println(count);
}
public void run(){
addCount();
}
public static void main(String[] args) {
VolativeNoAutomic[] arr = new VolativeNoAutomic[10];
for (int i = 0; i < 10; i++) {
arr[i] = new VolativeNoAutomic();
}
for (int i = 0; i < 10; i++) {
arr[i].start();
}
}
}
线程之间通信
线程通信概念:线程是操作系统中独立的个体,单这些个体如果不经过特殊的处理就不能成为一个整体,线程间的通信就成为整体的必用方式之一。当线程存在通信指挥,系统间的交互性会更强大,在提高CPU利用率的同时还会使开发人员对线程认为在处理的过程中进行有效的把控与监督。
使用wait/notify方法实现线程间的通信。
1. wait和notify必须配合synchronized关键字使用
2. wait方法释放锁,notify方法不释放锁
public class ListAdd1 {
private static volatile List list = new ArrayList();
public void add(){
list.add("shuai");
}
public int size(){
return list.size();
}
public static void main(String[] args) {
final ListAdd1 list1 = new ListAdd1();
final Object lock = new Object();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
synchronized(lock){
for (int i = 0; i < 10; i++) {
list1.add();
System.out.println("当前线程:" + Thread.currentThread().getName() + "添加一个数据");
Thread.sleep(500);
if(list1.size() == 5){
System.out.println("已经发出通知。。。");
lock.notify();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
if (list1.size() != 5) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程收到通知:" + Thread.currentThread().getName() + " 线程停止");
throw new RuntimeException();
}
}
}
},"t2");
t2.start();
t1.start();
}
}
使用countDownLatch达到wait/notify效果,实时通知。
public class ListAdd2 {
private static volatile List list = new ArrayList();
public void add(){
list.add("shuai");
}
public int size(){
return list.size();
}
public static void main(String[] args) {
final ListAdd2 list1 = new ListAdd2();
final CountDownLatch countDownLatch = new CountDownLatch(1);
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
// synchronized(lock){
for (int i = 0; i < 10; i++) {
list1.add();
System.out.println("当前线程:" + Thread.currentThread().getName() + "添加一个数据");
Thread.sleep(500);
if(list1.size() == 5){
System.out.println("已经发出通知。。。");
// lock.notify();
countDownLatch.countDown();
}
}
// }
} catch (Exception e) {
e.printStackTrace();
}
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
// synchronized (lock) {
if (list1.size() != 5) {
try {
// lock.wait();
countDownLatch.await();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("当前线程收到通知:" + Thread.currentThread().getName() + " 线程停止");
throw new RuntimeException();
}
}
// }
},"t2");
t2.start();
t1.start();
}
}
BlockingQueue是一个队列,并且支持阻塞的机制,阻塞的放入和得到数据。要实现put和take方法。
- put,把anObject加到BlockingQueue里,如果BlockQueue没有空间,则调用此方法的线程阻断,直到BlockingQueue里面有空间在继续。
- take,取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的数据被加入。
public class MyQueue {
private LinkedList<Object> list = new LinkedList<Object>();
private AtomicInteger count = new AtomicInteger(0);
private final int minSize = 0;
private final int maxSize ;
public MyQueue(int size) {
this.maxSize = size;
}
private final Object lock = new Object();
/**
* put,把Object加到BlockQueue里,如果BlockQueue没有空间,则调用此方法的线程阻断,直到BlockQueue里有空间再继续
*/
public void put(Object obj){
synchronized (lock) {
while (count.get() == this.maxSize) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//加入元素
list.add(obj);
//计数器累加
count.incrementAndGet();
System.out.println("queue加入一个元素:" + obj);
//唤醒另外一个线程
lock.notify();
}
}
/**
* take,取走BlockQueue里排在首位的对象,若BlockQueue为空,阻断进入等待状态直到BlockQueue有新的数据被加入
*/
public Object take(){
Object result = null;
synchronized (lock) {
while (count.get() == this.minSize) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//移除元素
result = list.removeFirst();
//计数器递减
count.decrementAndGet();
//唤醒另外一个线程
lock.notify();
}
return result;
}
public int getSize(){
return this.count.get();
}
public static void main(String[] args) {
final MyQueue myQueue = new MyQueue(5);
myQueue.put("a");
myQueue.put("b");
myQueue.put("c");
myQueue.put("d");
myQueue.put("e");
System.out.println("当前线程长度:" + myQueue.getSize());
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
myQueue.put("f");
myQueue.put("g");
}
},"t1");
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
Object obj1 = myQueue.take();
System.out.println("取出数据:"+ obj1);
Object obj2 = myQueue.take();
System.out.println("取出数据:" + obj2);
}
},"t2");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}
ThreadLocal
ThreadLocal:线程局部变量,是一种多线程间并发访问变量的解决方案。与其synchronized等加锁的方式不同,ThreadLocal完全不提供锁,而使用以空间换时间的手段,为每个线程提供变量的独立副本,以保障线程安全。
从性能上说,ThreadLocal不具有绝对的优势,在并发不是很高的时候,加锁的性能会跟好,但作为一套与锁完全无关的线程安全解决方案,在高并发量或者竞争激烈的场景,使用ThreadLocal可以在一定程度上减少锁竞争。
public class ConnThreadLocal {
private ThreadLocal<String> tl = new ThreadLocal<String>();
public void set(String value) {
tl.set(value);
}
public void get(){
System.out.println(Thread.currentThread().getName() +"取出的值是: "+ tl.get());
}
public static void main(String[] args) {
final ConnThreadLocal ctl = new ConnThreadLocal();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
ctl.set("james");
ctl.get();
}
},"t1");
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
ctl.get();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"t2");
t2.start();
}
}
单例模式
public class InnerSingleton {
private static class Singleton{
private static Singleton singleton = new Singleton();
}
public static Singleton getSingleton(){
return Singleton.singleton;
}
}
并发类容器
- ConcurrentMap接口下有两个重要的实现:
- ConcurrentHashMap
- ConcurrentSkipListMap(支持并发排序)
ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的HashTable,它们都有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。把一个整体分成16个段(Segment)。也就是最高支持16个线程的并发修改操作。这是一种在多线程场景时减小锁的粒度从而降低锁竞争的一种方案。并且代码中大多共享变量使用volatile关键字声明,目的是第一时间获取修改的内容,性能优越。
- Copy-On-Write是一种用于程序设计中的优化策略(COW)。JDK里的COW容器有两种:
- CopyOnWriteArrayList
- CopyOnWriteArraySet
CopyOnWrite容器即写时复制的容器。当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素,所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
- Queue,在并发队列上JDK提供了两套实现,一个是以ConcurrentLinkedQueue为代表的高性能队列,一个是以BlockingQueue接口为代表的阻塞队列。
ConcurrentLinkedQueue:是一个适用于高并发场景的队列,通过无锁的方式,实现了高并发状态下的高性能,通常ConcurrentLinkedQueue性能好于BlockingQueue。它是一个基于链接节点的无界线程安全队列。该队列元素遵循先进先出的原则。该队列不允许null元素。
- add()和offer()都是加入元素的方法(在concurrentLinkedQueue中,这两个方法没有任何区别)
- poll()和pell()都是取头元素节点,区别在于前者删除元素,后者不会删除
ArrayBolckingQueue,基于数组的阻塞队列实现,在ArrayBlockingQueue内部,维护了一个定长数组,以便缓存队列中的数据对象,其内部没实现读写分离,意味着生产和消费不能完全并行,长度是需要定义的,可以指定先进先出或者先进后出,也叫有界队列,在很多场合非常适合使用。
LinkedBlockingQueue:基于链表的阻塞队列,同ArrayBlockingQueue类似,其内部也维持着一个数据缓冲队列,能高效的处理并发数据,是因为其内部实现采用分离锁(读写分离两个锁),从而实现生产者和消费者操作的完全并行运行。是一个无界队列。
SynchronousQueue:一种没有缓冲的队列,生产者产生的数据直接会被消费者获取并消费。
PriorityBlockingQueue:基于优先级的阻塞队列(优先级的判断通过构造函数传入的Compator对象来决定,也就是说传入队列的对象必须实现comparable接口),在实现PriorityBlockingQueue时,内部控制线程同步的锁采用的是公平锁,它是一个无界的队列。
DelayQueue:带有延迟时间的Queue,其中的元素只有当其指定的延迟时间到了,才能从队列中获取到该元素。DelayQueue中的元素必须实现Delayed接口,DelayQueue是一个没有大小限制的队列,应用场景比如对缓存超时的数据进行移除、任务超时处理、空闲连接的关闭等。
public class Netizen implements Delayed{
private String id;
private String name;
private Long endTime;
private TimeUnit timeUnit = TimeUnit.SECONDS;
public Netizen(String id, String name, Long endTime) {
this.id = id;
this.name = name;
this.endTime = endTime;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
@Override
public int compareTo(Delayed delayed) {
Netizen netizen = (Netizen) delayed;
return this.getDelay(this.timeUnit) - netizen.getDelay(this.timeUnit) > 0 ? 1 : 0;
}
@Override
public long getDelay(TimeUnit unit) {
return endTime-System.currentTimeMillis();
}
}
/**
* 使用DelayQueue,到时间取出
*/
public class NetBar implements Runnable{
private DelayQueue<Netizen> queue = new DelayQueue<Netizen>();
private boolean doBusiness = true;
public void play(String name,String id,int money){
Netizen netizen = new Netizen(id,name,1000*money + System.currentTimeMillis());
System.out.println("网民" + netizen.getName() + "编号" + netizen.getId() + "交钱" + money + "元,开始玩。。。");
this.queue.add(netizen);
}
public void shutDown(Netizen netizen){
System.out.println("网民" + netizen.getName() + "编号" + netizen.getId() + "下机。。。");
}
@Override
public void run() {
while (doBusiness) {
try {
Netizen netizen = queue.take();
shutDown(netizen);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
System.out.println("网吧开始营业。。。");
NetBar netBar = new NetBar();
Thread playGame = new Thread(netBar);
playGame.start();
netBar.play("甲", "1", 1);
netBar.play("乙", "234", 10);
netBar.play("丙", "456", 5);
}
}
多线程的设计模式
Future模式类似于发送Ajax请求的时候,页面是异步的进行后台处理,用户无须一直等待请求的结果,可以继续操作其他内容。
public interface Data { String getRequest(); } public class RealData implements Data{ private String result; public RealData(String queryStr) { System.out.println("根据" + queryStr + "进行查询"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("查询结束,获取结果"); result = "真实结果"; } @Override public String getRequest() { return result; } } public class FutureData implements Data{ private RealData realData; private boolean isReady = false; public synchronized void setRequest(RealData realData){ //如果加载完毕,直接返回 if (isReady) { return; } //没有加载完,加载真实数据 this.realData = realData; isReady = true; notify(); } @Override public synchronized String getRequest() { //没有加载好,一直阻塞 while (!isReady) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //加载完,获取数据 return this.realData.getRequest(); } } public class FutureClient { public Data request(final String queryStr){ //1.代理对象(Data接口实现者)先返回给发送请求的客户端,请求已经接收 final FutureData futureData = new FutureData(); //2.启动一个新的线程,去加载真实的数据,传递给这个代理对象 new Thread(new Runnable() { @Override public void run() { //3.这个新的线程可以去加载真实对象,然后传递给代理对象 RealData realData = new RealData(queryStr); futureData.setRequest(realData); } }).start();; // return futureData; } } public class Main { public static void main(String[] args) { FutureClient futureClient = new FutureClient(); Data data = futureClient.request("请求参数"); System.out.println("请求发送成功!"); System.out.println("进行其他操作"); String result = data.getRequest(); System.out.println(result); } }
Master-Worker模式四常用的并行计算模式。核心思想是系统由两类进程协作工作:Master进程和Worker进程。Master负责接收和分配任务,Worker负责处理子任务。当各个Worker子进程处理完成后,会将结果返回给Master,由Master做归纳和总结。其好处是能将一个大任务分解成若干个小任务,并行执行,从而提高系统的吞吐量。
/** * 任务 */ public class Task { private int id; private String name; private int price; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } } /** * 子任务worker */ public class Worker implements Runnable{ private ConcurrentLinkedQueue<Task> workQueue; private ConcurrentHashMap<String, Object> resultMap; public void setWorkQueue(ConcurrentLinkedQueue<Task> workQueue) { this.workQueue = workQueue; } public void setResultMap(ConcurrentHashMap<String, Object> resultMap) { this.resultMap = resultMap; } @Override public void run() { while (true) { Task input = this.workQueue.poll(); if (input == null) break; //做业务处理 Object output = MyWorker.handle(input); this.resultMap.put(Integer.toString(input.getId()), output); } } public static Object handle(Task input) { return null; } } /** * 自定义Worker,具体业务处理 */ public class MyWorker extends Worker{ /** * 业务处理 * @param input * @return */ public static Object handle(Task input) { Object output = null; try { //模拟处理task任务的耗时 Thread.sleep(500); output = input.getPrice(); } catch (InterruptedException e) { e.printStackTrace(); } return output; } } /** * 接收、分配任务 */ public class Master { //1.承装任务的集合 private ConcurrentLinkedQueue<Task> workQueue = new ConcurrentLinkedQueue<Task>(); //2.使用HashMap承装所有work对象 private HashMap<String,Thread> workers = new HashMap<String,Thread>(); //3.承装每个worker执行的结果集 private ConcurrentHashMap<String, Object> resultMap = new ConcurrentHashMap<String,Object>(); //4.构造方法 public Master(Worker worker,int workerCount){ // 每一个worker对象都需要有Master的引用 workQueue用于任务的领取,resultMap用于任务的提交 worker.setWorkQueue(this.workQueue); worker.setResultMap(this.resultMap); for (int i = 0; i < workerCount; i++) { //key表示每一个worker的名字, value表示线程执行对象 workers.put("子节点" + i , new Thread(worker)); } } //5.提交方法 public void submit(Task task){ workQueue.add(task); } //6.执行方法,启动所有worker public void execute(){ for(Entry<String, Thread> entry : workers.entrySet()){ entry.getValue().start(); } } //7.判断线程是否执行完毕 public boolean isComplete(){ for(Entry<String,Thread> entry : workers.entrySet()){ if (entry.getValue().getState() != Thread.State.TERMINATED) { return false; } } return true; } //8.返回结果集数据 public int getResult(){ Integer result = 0; for(Entry<String, Object> entry : resultMap.entrySet()){ //汇总结果 result += (Integer) entry.getValue(); } return result; } } /** * 测试 */ public class Main { public static void main(String[] args) { Master master = new Master(new MyWorker(), Runtime.getRuntime().availableProcessors()); Random r = new Random(); for (int i = 0; i < 100; i++) { Task task = new Task(); task.setId(i); task.setName("任务" + i); task.setPrice(r.nextInt(1000)); master.submit(task); } //执行 master.execute(); long start = System.currentTimeMillis(); while (true) { if (master.isComplete()) { long exec = System.currentTimeMillis() - start; //获取结果 int result = master.getResult(); System.out.println("最终结果:" + result + ", 执行耗时:" + exec); break; } } } }
生产者-消费者,通常由两类线程,即若干个生产者的线程和若干个消费者的线程。生产者线程负责提交用户请求,消费者线程则负责处理生产者提交的任务,在生产者和消费者之间通过共享内存缓存区进行通信。
public class Data { private int id; private String name; public Data(){ } public Data(int id,String name){ this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } /** * 生产者 */ public class Producer implements Runnable{ //共享缓冲区 private BlockingQueue<Data> queue; //是否启动线程 private volatile boolean isOpen = true; //id生成器 private AtomicInteger count = new AtomicInteger(0); private Random r = new Random(); public Producer(BlockingQueue<Data> queue){ this.queue = queue; } @Override public void run() { while (isOpen) { try { Thread.sleep(r.nextInt(1000)); int id = count.incrementAndGet(); //通过getData方法获取 Data data = new Data(id,"数据" + id); System.out.println("当前线程" + Thread.currentThread().getName() + ",获取了数据,id为" + id + ",装载到缓冲区"); if(!queue.offer(data, 2, TimeUnit.SECONDS)){ System.out.println("提交缓冲区数据失败"); } } catch (InterruptedException e) { e.printStackTrace(); } } } public void stop(){ this.isOpen = false; } } /** * 消费者 */ public class Consumer implements Runnable{ private BlockingQueue<Data> queue ; private Random r = new Random(); public Consumer(BlockingQueue<Data> queue) { this.queue = queue; } @Override public void run() { while(true){ try { //获取数据 Data data = queue.take(); //模拟数据处理 Thread.sleep(r.nextInt(1000)); System.out.println("当前线程:" + Thread.currentThread().getName() + "消费成功,获取数据id" + data.getId()); } catch (InterruptedException e) { e.printStackTrace(); } } } } /** * 测试 */ public class Main { public static void main(String[] args) { //内存缓冲区 BlockingQueue<Data> queue = new LinkedBlockingQueue<Data>(10); //生产者 Producer p1 = new Producer(queue); Producer p2 = new Producer(queue); Producer p3 = new Producer(queue); //消费者 Consumer c1 = new Consumer(queue); Consumer c2 = new Consumer(queue); Consumer c3 = new Consumer(queue); //创建线程池 ExecutorService cachedPool = Executors.newCachedThreadPool(); cachedPool.execute(p1); cachedPool.execute(p2); cachedPool.execute(p3); cachedPool.execute(c1); cachedPool.execute(c2); cachedPool.execute(c3); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } p1.stop(); p2.stop(); p3.stop(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } // cachedPool.shutdown(); // cachedPool.shutdownNow(); } }