Vetx.x : EventLoop线程不要锁(Synchronized/ReentrantLock)

Vetx.x : EventLoop线程不要锁(Synchronized/ReentrantLock)

vert.x 里面有eventloop线程,而且eventloop线程是绝对不能阻塞的,

但是实际使用中,有时候我们对阻塞理解不深刻,所以一些隐藏的阻塞

没有那么好发现。

eventloop 线程和work线程共用相同的锁

下面的代码是一个简单的例子,用户请求localhost:8133/sync的时候

会在work现场锁sb对象,然后在eventloop线程也要获取sb这个对象的锁。

那么,我们发现,这样eventloop就会超时,可以看下日志。


package concurrency.file.vertx;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler;

/**
 * work 线程中有锁
 *
 * @author kyle
 */
public class SynchronizedTest {

  public static class Inner extends AbstractVerticle {
    StringBuilder sb = new StringBuilder();

    public void write(String str) {

      synchronized (sb) {
        sb.delete(0, sb.length());
        sb.append(str);

      }
    }

    @Override
    public void start(Future<Void> future) throws Exception {
      Router route = Router.router(vertx);
      route.route().handler(BodyHandler.create());
      route.route("/sync").handler(context1 -> {
        vertx.executeBlocking(f -> {
          foo();
        }, f -> {

        });
        vertx.setTimer(1, f -> {
          write(context1.getBodyAsString());
          context1.response().end();
        });
      });
      vertx.createHttpServer()
          .requestHandler(route::accept)
          .listen(8133, ar -> {
            if (ar.failed()) future.fail(ar.cause());
            else future.complete();
          });


    }

    private void foo() {
      synchronized (sb) {
        try {
          Thread.sleep(5000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }

  }


  public static void main(String[] args) {
    VertxOptions vertxOptions = new VertxOptions();
    vertxOptions.setMaxEventLoopExecuteTime(20_000_000);
    Vertx vertx = Vertx.vertx(vertxOptions);
    vertx.deployVerticle(new Inner(), ar -> {
      for (int i = 0; i < 50; i++) {
        vertx.createHttpClient()
            .getNow(8133, "localhost", "/sync", result -> {
              System.out.println("finish");
            });
      }
    });
  }


}

日志如下
这里写图片描述

这是由于两个线程同时抢夺一个锁,如果eventloop线程拿不到锁,它就会阻塞。

仅eventloop中有锁

下面是上面的代码的修改,去掉了work线程对锁的把持。但是这样写代码依然有问题,如果多个eventloop线程

抢夺一个锁,必定会造成其他的eventloop线程的阻塞。所以也不要使用这种写法


package concurrency.file.vertx;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler;

/**
 * work 线程中有锁
 *
 * @author kyle
 */
public class SynchronizedTest {

  public static class Inner extends AbstractVerticle {
    StringBuilder sb = new StringBuilder();

    public void write(String str) {

      synchronized (sb) {
        sb.delete(0, sb.length());
        sb.append(str);

      }
    }

    @Override
    public void start(Future<Void> future) throws Exception {
      Router route = Router.router(vertx);
      route.route().handler(BodyHandler.create());
      route.route("/sync").handler(context1 -> {
          write(context1.getBodyAsString());
          context1.response().end();

      });
      vertx.createHttpServer()
          .requestHandler(route::accept)
          .listen(8133, ar -> {
            if (ar.failed()) future.fail(ar.cause());
            else future.complete();
          });


    }

  }


  public static void main(String[] args) {
    VertxOptions vertxOptions = new VertxOptions();
    vertxOptions.setMaxEventLoopExecuteTime(20_000_000);
    Vertx vertx = Vertx.vertx(vertxOptions);
    vertx.deployVerticle(new Inner(), ar -> {
      for (int i = 0; i < 50; i++) {
        vertx.createHttpClient()
            .getNow(8133, "localhost", "/sync", result -> {
              System.out.println("finish");
            });
      }
    });
  }


}

由于这种现象需要高并发的情况下,才容易观察出问题,所以这种写法,经常会被我们忽略。

看不到的synchronize

看下面的一段代码,是不是觉得没有用到锁,不会阻塞线 。其实不是的,由于Files.write底层仍然使用了synchronized 锁,

所以也会阻塞 eventloop线程,这种情况下,我们很难发现和察觉。


package concurrency.file.vertx;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.Future;
import io.vertx.core.Vertx;
import io.vertx.core.VertxOptions;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler;

/**
 * work 线程中有锁
 *
 * @author kyle
 */
public class SynchronizedTest {

  public static class Inner extends AbstractVerticle {


    public void write(String str) {
        Files.write(Path.get("temp"), str.getBytes());
    }

    @Override
    public void start(Future<Void> future) throws Exception {
      Router route = Router.router(vertx);
      route.route().handler(BodyHandler.create());
      route.route("/sync").handler(context1 -> {
          write(context1.getBodyAsString());
          context1.response().end();

      });
      vertx.createHttpServer()
          .requestHandler(route::accept)
          .listen(8133, ar -> {
            if (ar.failed()) future.fail(ar.cause());
            else future.complete();
          });


    }

  }


  public static void main(String[] args) {
    VertxOptions vertxOptions = new VertxOptions();
    vertxOptions.setMaxEventLoopExecuteTime(20_000_000);
    Vertx vertx = Vertx.vertx(vertxOptions);
    vertx.deployVerticle(new Inner(), ar -> {
      for (int i = 0; i < 50; i++) {
        vertx.createHttpClient()
            .getNow(8133, "localhost", "/sync", result -> {
              System.out.println("finish");
            });
      }
    });
  }


}

不用锁,我该怎么办

我个人觉得,在Eventloop线程中是绝对不能用锁的,各种锁都不行,比如synchronize ,reentrantLock, CountdownLatch, CycleBarrier等等。那我们如何处理同步问题呢。我的一个思路是用单线程池,不用加锁,天然保持线程的同步。参考列子,如下。

 package concurrency.file.vertx;

  import io.vertx.core.AbstractVerticle;
  import io.vertx.core.Future;
  import io.vertx.core.Vertx;
  import io.vertx.core.VertxOptions;
  import io.vertx.ext.web.Router;
  import io.vertx.ext.web.handler.BodyHandler;

  import java.util.concurrent.ExecutorService;
  import java.util.concurrent.Executors;

  /**
   * work 线程中有锁
   *
   * @author kyle
   */
  public class SynchronizedTest {

    public static class Inner extends AbstractVerticle {
      ExecutorService executorService = Executors.newSingleThreadExecutor();
      StringBuilder sb = new StringBuilder();

      public Future<Void> write(String str) {
        Future<Void> future = Future.future();
        executorService.submit(() -> {
          try {
            Thread.sleep(5000);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          sb.delete(0, sb.length());
          sb.append(str);

        });
        return future;
      }

      @Override
      public void start(Future<Void> future) throws Exception {
        Router route = Router.router(vertx);
        route.route().handler(BodyHandler.create());
        route.route("/sync").handler(context1 -> {
            write(context1.getBodyAsString());
            context1.response().end();

        });
        vertx.createHttpServer()
            .requestHandler(route::accept)
            .listen(8133, ar -> {
              if (ar.failed()) future.fail(ar.cause());
              else future.complete();
            });


      }


    }


    public static void main(String[] args) {
      VertxOptions vertxOptions = new VertxOptions();
      vertxOptions.setMaxEventLoopExecuteTime(20_000_000);
      Vertx vertx = Vertx.vertx(vertxOptions);
      vertx.deployVerticle(new Inner(), ar -> {
        for (int i = 0; i < 50; i++) {
          vertx.createHttpClient()
              .getNow(8133, "localhost", "/sync", result -> {
                System.out.println("finish");
              });
        }
      });
    }


  }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java8新特性及实战视频教程完整版Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。 Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用Lambda 表达式可以使代码变的更加简洁紧凑。Java8实战视频-01让方法参数具备行为能力Java8实战视频-02Lambda表达式初探Java8实战视频-03Lambda语法精讲Java8实战视频-04Lambda使用深入解析Java8实战视频-05Lambda方法推导详细解析-上.wmvJava8实战视频-06Lambda方法推导详细解析-下Java8实战视频-07Stream入门及Stream在JVM中的线程表现Java8实战视频-08Stream知识点总结Stream源码阅读Java8实战视频-09如何创建Stream上集Java8实战视频-10如何创建Stream下集.wmvJava8实战视频-11Stream之filter,distinct,skip,limit,map,flatmap详细介绍Java8实战视频-12Stream之Find,Match,Reduce详细介绍Java8实战视频-13NumericStream的详细介绍以及和Stream之间的相互转换Java8实战视频-14Stream综合练习,熟练掌握API的用法Java8实战视频-15在Optional出现之前经常遇到的空指针异常.wmvJava8实战视频-16Optional的介绍以及API的详解Java8实战视频-17Optional之flatMap,综合练习,Optional源码剖析Java8实战视频-18初识Collector体会Collector的强大Java8实战视频-19Collector使用方法深入详细介绍-01Java8实战视频-20Collector使用方法深入详细介绍-02Java8实战视频-21Collector使用方法深入详细介绍-03.wmvJava8实战视频-22Collector使用方法深入详细介绍-04Java8实战视频-23Collector原理讲解,JDK自带Collector源码深度剖析Java8实战视频-24自定义Collector,结合Stream的使用详细介绍Java8实战视频-25Parallel Stream编程体验,充分利用多核机器加快计算速度Java8实战视频-26Fork Join框架实例深入讲解Java8实战视频-27Spliterator接口源码剖析以及自定义Spliterator实现一个Stream.wmvJava8实战视频-28Default方法的介绍和简的例子Java8实战视频-29Default方法解决多重继承冲突的三大原则详细介绍Java8实战视频-30多线程Future设计模式原理详细介绍,并且实现一个Future程序Java8实战视频-31JDK自带Future,Callable,ExecutorService介绍Java8实战视频-32实现一个异步基于事件回调的Future程序.wmvJava8实战视频-33CompletableFuture用法入门介绍Java8实战视频-34CompletableFuture之supplyAsync详细介绍Java8实战视频-35CompletableFuture流水线工作,join多个异步任务详细讲解Java8实战视频-36CompletableFuture常用API的重点详解-上Java8实战视频-37CompletableFuture常用API的重点详解-下Java8实战视频-38JDK老DateAPI存在的问题,新的DateAPI之LocalDate用法及其介绍.wmvJava8实战视频-39New Date API之LocalTime,LocalDateTime,Instant,Duration,Period详细介绍Java8实战视频-40New Date API之format和parse介绍

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值