Java多线程模式(三)

4 篇文章 0 订阅
4 篇文章 0 订阅

Read-Wirte Lock Pattern

        Read-Write Lock Pattern 将读取和写入分开来处理。在读取数据之前,必须获取用来读取的锁定。而要写入的时候,则必须获取用来写入的锁定。因为进行读取时,实例的状态不会改变,所以,就算有多个线程在同时读取也没有关系。但当有线程在进行写入的时候,不可以再进行写入的操作。写入的时候,实例的状态会改变。于是,当有一个线程在写入的时候,其他线程就不可以进行读取或写入。一般来说,进行共享互斥会使程序性能变差,但将写入的共享互斥与读取的共享互斥拆分开来,就可以提高程序的性能

public class ReadWriteLock {

    /**
     * 正在读取的线程数
     */
    private int     readingThreadsNumber;

    /**
     * 正在写入的线程数(最多为1)
     */
    private int     writingThreadsNumber;

    /**
     * 等待写入的线程数
     */
    private int     waitingWriteThreadsNumber;

    /**
     * 是否优先写入,true:优先写入;false:优先读取
     */
    private boolean preferWriter = true;

    public synchronized void readLock() throws InterruptedException {
        // 如果有线程正在写入或者优先写入时,有线程正在等待写入,读取线程则等待
        while (this.writingThreadsNumber > 0
                || (this.preferWriter && this.waitingWriteThreadsNumber > 0)) {
            wait();
        }

        this.readingThreadsNumber++;
    }

    public synchronized void readUnlock() throws InterruptedException {
        this.readingThreadsNumber--;
        this.preferWriter = true;
        notifyAll();
    }

    public synchronized void writeLock() throws InterruptedException {
        this.waitingWriteThreadsNumber++;
        // 如果有线程正在写入或者正在读取,当前写入线程等待
        try {
            while (this.writingThreadsNumber > 0 || this.readingThreadsNumber > 0) {
                wait();
            }
        } finally {
            this.waitingWriteThreadsNumber--;
        }

        this.writingThreadsNumber++;
    }

    public synchronized void writeUnlock() throws InterruptedException {
        this.writingThreadsNumber--;
        this.preferWriter = false;
        notifyAll();
    }

}

public class Data {

    private char[]        buffer;

    private ReadWriteLock readWriteLock = new ReadWriteLock();

    public Data(int size) {
        this.buffer = new char[size];
        for (int i = 0; i < size; i++) {
            this.buffer[i] = '*';
        }
    }

    public char[] read() throws InterruptedException {
        try {
            readWriteLock.readLock();
            return doRead();
        } finally {
            readWriteLock.readUnlock();
        }
    }

    public void write(char c) throws InterruptedException {
        try {
            readWriteLock.writeLock();
            doWrite(c);
        } finally {
            readWriteLock.writeUnlock();
        }
    }

    private char[] doRead() {
        char[] newChars = new char[buffer.length];
        System.arraycopy(this.buffer, 0, newChars, 0, this.buffer.length);
        slowly();
        return newChars;
    }

    private void doWrite(char c) {
        for (int i = 0; i < this.buffer.length; i++) {
            this.buffer[i] = c;
            slowly();
        }
    }

    private void slowly() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
    }
}


import java.util.Random;

public class ReaderThread extends Thread {

    private static final Random random = new Random();

    private final Data          data;

    public ReaderThread(Data data) {
        this.data = data;
    }

    @Override
    public void run() {
        while (true) {
            try {
                char[] c = data.read();
                System.out.println(Thread.currentThread().getName() + "reads " + String.valueOf(c));
                Thread.sleep(random.nextInt(1000));
            } catch (InterruptedException e) {
            }
        }
    }

}


import java.util.Random;

public class WriterThread extends Thread {

    private static final Random random = new Random();

    private final Data          data;

    private final String        filler;

    private int                 index  = 0;

    public WriterThread(Data data, String filler) {
        this.data = data;
        this.filler = filler;
    }

    @Override
    public void run() {
        while (true) {
            char c = nextChar();
            try {
                data.write(c);
                Thread.sleep(random.nextInt(1000));
            } catch (InterruptedException e) {
            }
        }
    }

    private char nextChar() {
        char c = filler.charAt(index);
        index++;
        if (index > filler.length()) {
            index = 0;
        }

        return c;
    }
}


public class MainThread {

    public static void main(String[] args) {
        int bufferSize = 10;
        Data data = new Data(bufferSize);

        new ReaderThread(data).start();
        new ReaderThread(data).start();
        new ReaderThread(data).start();
        new ReaderThread(data).start();
        new ReaderThread(data).start();
        new ReaderThread(data).start();
        new ReaderThread(data).start();

        String filler1 = "abcdefghjklmnopqrstuvwxyz";
        String filler2 = "ABCDEFGHJKLMNOPQRSTUVWXYZ";
        new WriterThread(data, filler1).start();
        new WriterThread(data, filler2).start();

    }

}
        单纯使用Single Thread Execution Pattern时,就连read的操作一次也只有一条线程可以执行。如果read操作比较频繁或比较耗时,那使用Read-Write Lock Pattern会比Single Thread Execution Pattern好很多。但因为Read-Write Lock Pattern的程序比Single Thread Execution Pattern实现复杂,如果read操作很简单,那么使用Single Thread Execution Pattern可能性能反而较高。 Read-Write Lock Pattern的优点在于Reader参与者之间不会起冲突。不过,当wirte操作比较频繁时,Writer参与者会经常阻挡Reader参与者的进行,这样就无法展现Read-Write Lock Pattern的优点。

        在ReadWriteLock类中,提供了“读访问锁定”和“写访问锁定”两种逻辑上的锁定,但在“物理”上只用到了一个锁定,即ReadWriteLock实例的锁定。




Thread-Per-Message Pattern

thread per message 就是每一个消息一个线程。对每一个命令或请求,分配一个线程,由这个线程执行工作,这就是Thread-Per-Message Pattern。

public class Helper {

    public void handle(int count, char c) {
        System.out.println("handle(" + count + ", " + c + ") BEGIN");
        for (int i = 0; i < count; i++) {
            System.out.print(c);
            slowly();
        }
        System.out.println("");
        System.out.println("handle( " + count + ", " + c + ") END");
    }

    private void slowly() {
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
        }
    }
}



public class Host {

    private final Helper helper = new Helper();

    public void request(final int count, final char c) {
        System.out.println("reqeust (" + count + ", " + c + ") BEGIN");
        new Thread() {
            @Override
            public void run() {
                helper.handle(count, c);
            }
        }.start();
        System.out.println("reqeust (" + count + ", " + c + ") END");
    }

    public static void main(String[] args) {
        System.out.println("main Begin");

        Host host = new Host();
        host.request(10, 'a');
        host.request(20, 'b');
        host.request(30, 'c');

        System.out.println("main End");
    }

}


该模式适合在操作顺序无所谓的请求时。如果操作顺序有意义时,不适合适用Thread-Per-Message Pattern。另外不需要返回值的时候也是适合使用该模式的。


在该模式里,由于每个请求都需要启动一个线程,那么启动线程以及线程上下文的切换就成为了系统的瓶颈点,为了降低线程的启动所需的时间,可以使用Worker Thread Pattern



Worker Thread Pattern

     Woker Thread Pattern可以看作是Thread-Per-Message Pattern的改进,该模式定义了一个线程池,线程池里面的线程被称作Worker Thread。由于在系统启动时,这些Worker Thread已经准备好了,当请求来时,不需要在进行重现启动,并且系统中也维持了一定数量的Worker Thread,而不是不断的启动新线程,在性能上要优于Thread-Per-Message Pattern。
import java.util.Random;

public class Request {

    private final String        name;

    private final int           number;

    private final static Random random = new Random();

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

    public void request() {
        System.out.println(Thread.currentThread().getName() + " " + toString());
        try {
            Thread.sleep(random.nextInt(1000));
        } catch (InterruptedException e) {
        }
    }

    @Override
    public String toString() {
        return "[ Reqeust name = " + name + ", number = " + number + " ]";
    }
}
import java.util.LinkedList;

public class Channel {

    private final LinkedList<Request> buffers    = new LinkedList<Request>();

    private static final int          bufferSize = 100;

    private WorkerThread[]            threadPool;

    public Channel(int threads) {
        this.threadPool = new WorkerThread[threads];

        for (int i = 0; i < threads; i++) {
            threadPool[i] = new WorkerThread("WorkerThread-" + (i + 1), this);
        }
    }

    public void startWorkers() {
        for (int i = 0; i < this.threadPool.length; i++) {
            threadPool[i].start();
        }
    }

    public synchronized void put(Request request) throws InterruptedException {
        while (this.buffers.size() >= bufferSize) {
            wait();
        }
        this.buffers.addLast(request);
        notifyAll();
    }

    public synchronized Request take() throws InterruptedException {
        while (this.buffers.size() == 0) {
            wait();
        }
        Request request = this.buffers.removeFirst();
        notifyAll();
        return request;
    }
}
public class WorkerThread extends Thread {

    private Channel channel;

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

    @Override
    public void run() {
        while (true) {
            try {
                Request request = this.channel.take();
                request.request();
            } catch (InterruptedException e) {
            }
        }
    }
}
package workerthread;

import java.util.Random;

public class ClientThread extends Thread {

    private final Channel       channel;

    private final static Random random = new Random();

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

    @Override
    public void run() {
        int i = 0;
        while (true) {
            Request request = new Request(getName(), ++i);
            try {
                this.channel.put(request);
                Thread.sleep(random.nextInt(1000));
            } catch (InterruptedException e) {
            }
        }
    }
}
public class Main {

    public static void main(String[] args) {

        int threads = 5;
        Channel channel = new Channel(threads);

        new ClientThread("Alice", channel).start();
        new ClientThread("Bobby", channel).start();
        new ClientThread("Chris", channel).start();

        channel.startWorkers();
    }
}

       Worker Thread Pattern 还将方法调用和方法执行进行了分离,所以我们在该模式里面看到了设计模式里面的Command Pattern的影子,因为它们的主题都是将方法调用和方法执行进行分离。方法调用和方法执行的分离可以提高响应性,能够控制实行的顺序,我们可以对Reqeust设立优先性,控制Channel传递Request给Worker的顺序。同时我们可以取消方法的执行,或者重复方法的执行。
      Request对象可以进行多态。由于Worker Thread并不知道Request类的具体内容,只是知道执行Request类的execute方法而已,所以我们可以建立Request类的子类,并将其实例传给Channel,Worker Thread也能正确调用这个实例的execute方法。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值