单线程执行设计模式


前言

这节我们一起探讨下多线程最简单的设计模式,单线程执行设计设计模式(Single Thread Execution Design Pattern)

一、什么是单线程执行设计模式

在某一时刻只有一个线程在执行,其它线程会进入block状态,这就是单线程执行设计模式。

二、简单例子

这里我们列举一个简单的例子:我们访问一个学校或者一个名人故居的时候都需要登记自己的个人信息:名字、地址、电话等。通常情况下大门又只有一个门卫,一张信息登记本,所以当我们通过大门的时候就只有一个一个排队登记,然后进门。这里就有两个重要的角色,一、大门,二、进入大门的人。站在线程的层次来讲,大门相当于共享资源,同一时刻只能有一个人访问,而进入大门的人就相当于一个个的线程,都要访问大门这个共享资源。

1.大门

大门有登记本,能够在人们通过的时候记录人的名字、地址等,还能记录今天进入大门的人数。因此这里我们设置大门有三个属性:counter\name\address

代码如下(示例):

package singlethreadexecutiondesignpattern;

public class Gate {
    /**
     * SharedResource
     */
    private Integer counter = 0;
    private String name = "Nobody";
    private String address = "NoWhere";

    public void pass(String name, String address) {
        counter++;
        this.name = name;
        this.address = address;
        verify();
    }

    private void verify() {
        if(this.name.charAt(0) != this.address.charAt(0)) {
            System.out.println("========broken==========" + toString());
        }
    }
    public String toString(){
        return "NO." + counter + " " + this.name + " " + this.address;
    }
}

2.人

人,要在想进入大门,那么就得调用通过大门的方法,要调用大门的方法,那就得现有大门这个属性。然后人在通过大门的时候要记录自己的身份信息,所以人要有身份信息。同时站在线程的角度来讲,人就是充当线程的角色,在启动的时候要通过大门登记信息

代码如下(示例):

package singlethreadexecutiondesignpattern;

public class User extends Thread {
    private final Gate gate;
    private String name;
    private String address;
    public User(Gate gate, String name, String address) {
        this.gate = gate;
        this.name = name;
        this.address = address;
    }

    @Override
    public void run() {
        System.out.println("begin");
        while (true) {
            this.gate.pass(this.name, this.address);
        }
    }
}

3.客户端

生成不同的人(线程),线程调用start()方法进行启动。
代码如下(示例):

package singlethreadexecutiondesignpattern;

public class client {
    public static void main(String[] args) {
        Gate gate = new Gate();
        User bj = new User(gate, "Bob", "Beijing");
        User sh = new User(gate, "ShangLao", "Shanghai");
        User nn = new User(gate, "Nannan", "Nanning");

        bj.start();
        sh.start();
        nn.start();
    }
}

4.执行结果

========broken==========NO.82491 Bob Beijing
========broken==========NO.83127 ShangLao Shanghai
========broken==========NO.83494 ShangLao Beijing
========broken==========NO.83794 ShangLao Shanghai

5.结果分析

上面是我截取的一部分代码,我们可以看到两个问题:
1、名字和地址首字母相等的时候进入了broken
2、名字和地址首字母不相等的时候进入了broken
为什么会出现这两种情况呢?第二种好理解,我们先讲,首先当一个线程执行Gate的pass方法的时候记录了自己的name和adrress于是它执行到了verify方法,同时另外一个线程也执行了gate的pass方法登记了name和名字,于是第一个线程执行到verify的时候name和address的首字母不相等了,于是进入了broken。
第一种又是什么情况呢?当第一个线程执行到verify方法进入broken之后,另一个线程又登记了name和address于是第一个线程执行toString方法的时候查到的name和address的首字母是相等的。

6.问题解决

我们要实现的功能是任何时候name和address的首字母都是相等的,并且永远不会进入broken中。根据之前分析的两个问题的原因,我们就可以很轻松的解决问题了。那就是使name与address的写和读的时候,只有一个线程在执行。也就是同一时间只有一个人能够登记进入大门。使用synchronized关键字给pass方法和toString方法加上this锁

7.解决问题后的执行结果

begin
begin
begin

可以看到,这里永远不会执行到broken里,也就是name和address的首字母永远是相等的

问题延伸

我们看到我们在name\address写的时候加上了锁,读的时候也加上了锁。写的时候加上锁是可以理解的,但是读的时候并不会改变name\address的值,这时候如果有多个线程去读这两个属性的值,也同样需要排队,一个线程读完,另外一个线程才能读。这样就是使得串行化,大大地降低了代码的执行效率。所以接下来一讲我们要解决这个问题。于是我们引入了读写锁分离的设计模式。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值