Java多线程之Worker Thread模式

目录
  • 一.Worker Thread模式
  • 二   .Worker Thread模式中的角色
    • 1.Client(委托者)
    • 2.Channel(通信线路)
    • 3.Worker(工人)
    • 4.Request(请求)
  • 三.Worker Thread使用场景
  • 四.Worker Thread模式程序示例

一.Worker Thread模式

Worker的意思是工作的人,在Worker Thread模式中,工人线程Worker thread会逐个取回工作并进行处理,当所有工作全部完成后,工人线程会等待新的工作到来。

Worker Thread模式也被成为Background Thread(背景线程)模式,另外,如果从保存多个工人线程的场所这一点看,我们也可以称这种模式为Thread Pool模式。

二   .Worker Thread模式中的角色

1.Client(委托者)

创建表示工作请求的Request并将其传递给Channel。在示例程序中,ClientThread相当于该角色。

2.Channel(通信线路)

Channel角色接受来自于ClientRequest,并将其传递给Worker。在示例程序中,Channel相当于该角色。

3.Worker(工人)

Worker角色从Channel中获取Request,并进行工作,当一项工作完成后,它会继续去获取另外的Request,在示例程序中,WorkerThread相当于该角色。

4.Request(请求)

Request角色是表示工作的角色,Request角色中保存了进行工作所必须的信息,在示例程序中,Request相当于该角色。

三.Worker Thread使用场景

想象一个场景,一个工厂在生产玩具,在一个车间里,有几个工人,每次生产部件准备好车间外的人就将部件放到车间的一个桌子上,工人每次做完一个玩具就从桌子上取部件。在这里,注意到,部件并不是直接交给工人的,另外一点,工人并不是做完一个部件就回家换个新人,后者在现实有点滑稽,但是在程序中却对应一个典型的线程使用方法:线程池。

所谓线程池,就是对线程的复用,当线程执行完任务之后就继续取其他任务执行,而不是销毁启动新线程执行其他任务。因为线程的启动对于系统性能开销比较大,所以这样对于系统性能的提高很有好处。

四.Worker Thread模式程序示例

首先是请求,即玩具的部件

public class Request {

    private final String name;
    private final int number;

    public Request(String name, int number) {
        this.name = name;
        this.number = number;
    }

    public void execute(){
        System.out.println(Thread.currentThread().getName()+" executed "+this);
    }
    @Override
    public String toString() {
        return "Request=> " + "No." + number + "  Name." + name;
    }
}

也就是拥有namenumber并且execute的时候打印出字段的一个简单类。

ClientThread,负责将请求放入RequestQueue中,即将部件放到桌子上。

public class ClientThread extends Thread {

    private static final Random random = new Random(System.currentTimeMillis());

    private final Channel channel;

    public ClientThread(String name, Channel channel) {
        super(name);
        this.channel = channel;
    }

    @Override
    public void run() {
        try {

            for (int i = 0; true; i++) {
                Request request = new Request(getName(),i);
                this.channel.put(request);
                Thread.sleep(random.nextInt(1_000));
            }
        } catch (Exception e) {

        }
    }
}

Channel类,可以当做车间

public class Channel {

    private final static int MAX_REQUEST = 100;

    private final Request[] requestQueue;
    private final WorkerThread[] workerPool;
    private int head;
    private int tail;
    private int count;

    public Channel(int workers) {

        this.requestQueue = new Request[MAX_REQUEST];
        this.head = 0;
        this.tail = 0;
        this.count = 0;
        this.workerPool = new WorkerThread[workers];
        this.init();
    }

    private void init() {
        for (int i = 0; i < workerPool.length; i++) {
            workerPool[i] = new WorkerThread("Worker-" + i, this);
        }
    }

    /**
     * push switch to start all of worker to work
     */
    public void startWorker() {
        Arrays.asList(workerPool).forEach(WorkerThread::start);

//        List<WorkerThread> workerThreads = Arrays.asList(workerPool);
//
//        workerThreads.stream().forEach(WorkerThread::start);
    }

    public synchronized void put(Request request) {
        while (count >= requestQueue.length) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.requestQueue[tail] = request;
        this.tail = (tail + 1) % requestQueue.length;
        this.count++;
        this.notifyAll();
    }

    public synchronized Request take() {
        while (count <= 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        Request request = this.requestQueue[head];
        this.head = (this.head + 1) % this.requestQueue.length;
        this.count--;
        this.notifyAll();
        return request;
    }
}

Requestqueue可以当做桌子,是一个数量有限的请求队列。threadPool是一个工人线程的数组,这就是一个线程池。在这里提供了putRequesttakeRequest方法,分别是往请求队列放入请求和取出请,这里使用了上一篇博文讲到的生产者消费者模式 java多线程设计模式之消费者生产者模式。确保了WorkerThreadClientThread之间可以友好合作。

工人线程:

public class WorkerThread extends Thread {

    private static final Random random = new Random(System.currentTimeMillis());
    private final Channel channel;

    public WorkerThread(String name, Channel channel) {
        super(name);
        this.channel = channel;
    }

    @Override
    public void run() {
        while (true) {
            channel.take().execute();

            try {
                Thread.sleep(random.nextInt(1_000));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

这里就是一个不断从请求队列中取出请求然后执行请求的过程,保证了工人线程的复用,并不会执行完一个请求任务就销毁。

最后是Main:

public class WorkerClient {

    public static void main(String[] args) {
        final Channel channel = new Channel(5);
        channel.startWorker();

        new ClientThread("Alex", channel).start();
        new ClientThread("Jack", channel).start();
        new ClientThread("William", channel).start();
    }
}

结果:

Worker-4 executed Request=> No.0  Name.Alex
Worker-2 executed Request=> No.0  Name.Jack
Worker-3 executed Request=> No.0  Name.William
Worker-4 executed Request=> No.1  Name.Jack
Worker-0 executed Request=> No.1  Name.William
Worker-3 executed Request=> No.2  Name.Jack
Worker-2 executed Request=> No.1  Name.Alex
Worker-4 executed Request=> No.2  Name.William
Worker-1 executed Request=> No.3  Name.Jack
Worker-3 executed Request=> No.2  Name.Alex
Worker-4 executed Request=> No.3  Name.William
Worker-0 executed Request=> No.4  Name.Jack
Worker-0 executed Request=> No.3  Name.Alex
Worker-1 executed Request=> No.5  Name.Jack
Worker-3 executed Request=> No.4  Name.William
Worker-1 executed Request=> No.6  Name.Jack
Worker-2 executed Request=> No.4  Name.Alex
Worker-3 executed Request=> No.7  Name.Jack
Worker-0 executed Request=> No.5  Name.William
Worker-1 executed Request=> No.5  Name.Alex
Worker-4 executed Request=> No.8  Name.Jack
Worker-2 executed Request=> No.6  Name.Alex
Worker-0 executed Request=> No.7  Name.Alex
Worker-4 executed Request=> No.8  Name.Alex
Worker-2 executed Request=> No.6  Name.William
省略...

可以看出线程执行任务的线程就是WorkerThread1,2,3,4,5五个,它们不断执行来自ClientThread Alex,Jack,William的请求任务。

到此这篇关于Java多线程之Worker Thread模式的文章就介绍到这了,更多相关Java多线程 Worker Thread内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java多线程CountDownLatch与线程池ThreadPoolExecutor/ExecutorService案例

    1.CountDownLatch: 一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行. 2.ThreadPoolExecutor/ExecutorService: 线程池,使用线程池可以复用线程,降低频繁创建线程造成的性能消耗,同时对线程的创建.启动.停止.销毁等操作更简便. 3.使用场景举例: 年末公司组织团建,要求每一位员工周六上午8点到公司门口集合,统一乘坐公司所租大巴前往目的地. 在这个案例中,公司作为主线程,员工作为子线程. 4.代码示例: package

  • Java多线程 Guarded Suspension设计模式

    目录 1.Guarded Suspension模式的结构 2. Guarded Suspension模式的简单实现 前言: Guarded Suspension意为保护暂停,其核心思想是仅当服务进程准备好时,才提供服务.设想一种场景,服务器可能会在很短时间内承受大量的客户端请求,客户端请求的数量可能超过服务器本身的即时处理能力,而服务端程序又不能丢弃任何一个客户请求.此时,最佳的处理方案莫过于让客户端要求进行排队,由服务端程序一个接一个处理.这样,既保证了所有的客户端请求均不丢失,同时也避免了服

  • Java多线程 ThreadLocal原理解析

    目录 1.什么是ThreadLocal变量 2.ThreadLocal实现原理 3.内存泄漏问题 4.使用场景 1)存储用户Session 2)解决线程安全的问题 3)使用ThreadLocal重新设计一个上下文设计模式 4)ThreadLocal注意事项 脏数据 内存泄漏 父子线程共享线程变量 1.什么是ThreadLocal变量 ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本. 这里有几点需要注意: 因为每个 T

  • Java多线程ThreadAPI详细介绍

    1.Thread的构造方法 package threadAPI; public class CreateThread { public static void main(String[] args) { Thread t1 = new Thread(); Thread t2 = new Thread(); t1.start(); t2.start(); System.out.println(t1.getName()); System.out.println(t2.getName()); } }

  • Java多线程 Producer and Consumer设计模式

    目录 producer是生产者的意思:指生产数据的线程, consumer是消费者的意思:指的是使用数据的线程 public class ProducerThread extends Thread { private final static Random random = new Random(System.currentTimeMillis()); private final static AtomicInteger counter = new AtomicInteger(0); priva

  • Java 多线程的同步代码块详解

    目录 synchronized 同步代码块 同步方法(this锁) 静态同步方法 死锁问题 lock 总结 火车站抢票问题 由于现实中买票也不会是零延迟的,为了真实性加入了延迟机制,也就是线程休眠语句 package test.MyThread.ticketDemo; public class RunnableThread implements Runnable{ private int ticket = 100; @Override public void run(){ while(true)

  • java Thread 多线程

    Thread 创建线程的两种方法: 1.定义类继承Thread类,覆写类中的run方法,调用类对象的start方法,start方法启动线程,调用run方法.Thread类用于描述线程:该类定义一个功能run,用于存储线程要运行的代码. 2.定义类实现Runnable接口,覆盖Runnable接口中的方法,通过Thread类建立线程对象,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数,调用Thread类的start方法开启线程,线程会调用Runnable接口子类中的ru

  • JAVA多线程Thread和Runnable的实现

    java中只允许单一继承,但允许实现多个接口,因此第二种方法更灵活. 复制代码 代码如下: /**     * 运行继承java.lang.Thread类定义的线程     */    public void startOne() {        // 创建实例        OneThread oneThread = new OneThread();        // 启动线程ThreadA        oneThread.startThreadA();        try {    

  • Java多线程 两阶段终止模式Two-Phase Termination Patter

    目录 1.两阶段终止模式介绍 2.Terminator代码演示 3.TerminationRequester 4.模拟客户端或者服务端都可能终止服务的例子 5.mac telnet模拟客户端输入 1.两阶段终止模式介绍 有时候,我们希望提前结束线程,但安全可靠地停止线程,并不是一件容易的事情,如果立即停止线程,会使共享的数据结构处于不一致的状态,如目前已经废弃使用的Thread类的stop方法(它会使线程在抛出java.lang.ThreadDeath之后终止线程,即使是在执行synchroni

  • Java多线程之Worker Thread模式

    目录 一.Worker Thread模式 二   .Worker Thread模式中的角色 1.Client(委托者) 2.Channel(通信线路) 3.Worker(工人) 4.Request(请求) 三.Worker Thread使用场景 四.Worker Thread模式程序示例 一.Worker Thread模式 Worker的意思是工作的人,在Worker Thread模式中,工人线程Worker thread会逐个取回工作并进行处理,当所有工作全部完成后,工人线程会等待新的工作到来

  • java多线程之Balking模式介绍

    Balk有拒绝,阻碍的意思.如果现在不适合执行这个操作,或者没必要执行这个操作,就停止处理,直接返回.这就是Balking模式. Balking 模式可以和Guarded Suspension 模式对比,都存在守护条件.而在Balking模式中,如果守护条件不成立就立即中断处理,而Guarded Suspension 模式则是一直等待至可以运行. 创建4个类 名字 说明 Data 表示可以修改并保存的数据的类 SaverThread 定期保存数据内容的类 ChangerThread 修改并保存数

  • java多线程之CyclicBarrier的使用方法

    java多线程之CyclicBarrier的使用方法 public class CyclicBarrierTest { public static void main(String[] args) { ExecutorService service = Executors.newCachedThreadPool(); final CyclicBarrier cb = new CyclicBarrier(3); for(int i=0;i<3;i++){ Runnable runnable = n

  • Java多线程之readwritelock读写分离的实现代码

    在多线程开发中,经常会出现一种情况,我们希望读写分离.就是对于读取这个动作来说,可以同时有多个线程同时去读取这个资源,但是对于写这个动作来说,只能同时有一个线程来操作,而且同时,当有一个写线程在操作这个资源的时候,其他的读线程是不能来操作这个资源的,这样就极大的发挥了多线程的特点,能很好的将多线程的能力发挥出来. 在Java中,ReadWriteLock这个接口就为我们实现了这个需求,通过他的实现类ReentrantReadWriteLock我们可以很简单的来实现刚才的效果,下面我们使用一个例子

  • Java多线程之volatile关键字及内存屏障实例解析

    前面一篇文章在介绍Java内存模型的三大特性(原子性.可见性.有序性)时,在可见性和有序性中都提到了volatile关键字,那这篇文章就来介绍volatile关键字的内存语义以及实现其特性的内存屏障. volatile是JVM提供的一种最轻量级的同步机制,因为Java内存模型为volatile定义特殊的访问规则,使其可以实现Java内存模型中的两大特性:可见性和有序性.正因为volatile关键字具有这两大特性,所以我们可以使用volatile关键字解决多线程中的某些同步问题. volatile

  • java多线程之Phaser的使用详解

    前面的文章中我们讲到了CyclicBarrier.CountDownLatch的使用,这里再回顾一下CountDownLatch主要用在一个线程等待多个线程执行完毕的情况,而CyclicBarrier用在多个线程互相等待执行完毕的情况. Phaser是java 7 引入的新的并发API.他引入了新的Phaser的概念,我们可以将其看成一个一个的阶段,每个阶段都有需要执行的线程任务,任务执行完毕就进入下一个阶段.所以Phaser特别适合使用在重复执行或者重用的情况. 基本使用 在CyclicBar

  • java多线程之Future和FutureTask使用实例

    Executor框架使用Runnable 作为其基本的任务表示形式.Runnable是一种有局限性的抽象,然后可以写入日志,或者共享的数据结构,但是他不能返回一个值. 许多任务实际上都是存在延迟计算的:执行数据库查询,从网络上获取资源,或者某个复杂耗时的计算.对于这种任务,Callable是一个更好的抽象,他能返回一个值,并可能抛出一个异常.Future表示一个任务的周期,并提供了相应的方法来判断是否已经完成或者取消,以及获取任务的结果和取消任务. public interface Callab

  • Java多线程之Park和Unpark原理

    一.基本使用 它们是 LockSupport 类中的方法 // 暂停当前线程 LockSupport.park(); // 恢复某个线程的运行 LockSupport.unpark(暂停线程对象) 应用:先 park 再 unpark Thread t1 = new Thread(() -> { log.debug("start..."); sleep(1); log.debug("park..."); LockSupport.park(); log.debu

  • Java多线程之Disruptor入门

    一.Disruptor简介 Disruptor目前是世界上最快的单机消息队列,由英国外汇交易公司LMAX开发,研发的初衷是解决内存队列的延迟问题(在性能测试中发现竟然与I/O操作处于同样的数量级).基于Disruptor开发的系统单线程能支撑每秒600万订单,2010年在QCon演讲后,获得了业界关注.2011年,企业应用软件专家Martin Fowler专门撰写长文介绍.同年它还获得了Oracle官方的Duke大奖.目前,包括Apache Storm.Camel.Log4j 2在内的很多知名项

  • Java多线程之synchronized关键字的使用

    一.使用在非静态方法上 public synchronized void syzDemo(){ System.out.println(System.currentTimeMillis()); System.out.println("进入synchronized锁:syzDemo"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } 二.使用在静态方法上 publi

随机推荐