QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1649|回复: 1
打印 上一主题 下一主题

Java如何优雅的实现时间控制

[复制链接]
字体大小: 正常 放大
杨利霞        

5273

主题

82

听众

17万

积分

  • TA的每日心情
    开心
    2021-8-11 17:59
  • 签到天数: 17 天

    [LV.4]偶尔看看III

    网络挑战赛参赛者

    网络挑战赛参赛者

    自我介绍
    本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。

    群组2018美赛大象算法课程

    群组2018美赛护航培训课程

    群组2019年 数学中国站长建

    群组2019年数据分析师课程

    群组2018年大象老师国赛优

    跳转到指定楼层
    1#
    发表于 2020-5-3 16:03 |只看该作者 |倒序浏览
    |招呼Ta 关注Ta
    / c! U5 ~  m; J) a; n
    Java如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
    5 G$ C. }: V4 Q! V6 e  n$ Z* l& u( q( w: f3 x
    一:时间控制的几种方案. c8 o1 Z% c6 b" {- `, L
    . e* {* C' m; Q/ P3 {/ x. e1 S. y
    1.1: 从线程方面解决. [  l) B, V" y

    6 r# k7 ~" W9 g( d最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。! [. ^$ h0 T5 G: X
    4 A4 s8 }. y' Z& _# L
    1.2:使用Timer
    4 M! n9 {! l' ~" f; u; P3 j
      x/ K# y8 h  C7 ]6 `/ |查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:
    % i, h6 `7 y4 T* s9 ?; v4 v
    * G, Q8 G8 _5 J; Q) Ypublic class TimmerTest {
    9 ?6 \+ M6 Z- k* Q   /**. n  `5 i* T5 g6 h$ H
         * 测试方法
    6 [6 F% w3 ?: e  K* ^. g# I     */3 A% i$ p: X' c0 c7 j8 \* c& n
        public void test() {0 \$ {$ w/ q$ w2 q9 T3 Z
            Timer timer = new Timer();. D2 h& p0 r0 y  A
            timer.schedule(new MyTask(), 800);
    9 h, |: c: ?* D" n3 j    }4 N7 ?5 ^( i; L- W

    . s* p' h, n9 F3 H" u8 B    public class MyTask extends TimerTask {% L7 R# T; {0 c5 p
    # x* e/ |$ M* Z" e4 d$ H/ S
            /**; x/ j3 X" S1 }" w: I
             * 运行方法( R- e6 c. b1 P. g' ^3 z- a7 \& P
             */5 r& n6 d+ H5 L
            @Override$ R! Y6 H2 e$ I4 V5 U# k
            public void run() {
    $ b) g# ?# h. X4 }* C& Q/ M, Z/ ~* F            System.out.println("输出");/ C6 m- v6 Q) @  W. ]+ a
            }& i: y) v; e' Y) L+ L* \* X2 l$ i: a
        }/ }1 b1 R' k& g) e
    }- ?2 k" G" H( _; a( N: X$ B9 I# v
    这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
    ! k  c; k# a2 l- g, X: c% h
    % v. r" f( a' {# F1.3:redis延时
    7 d+ P# G) M/ Z. e' l2 K  M/ I$ S, X+ j6 b! L% H; @, Q2 U
    在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
    & m( T) A+ u( S1 Z! Z" H' o1 p; g2 q5 z$ `3 `

    # l# i" W+ A, M) [( j8 V; |
    ( e& x5 o. y/ M0 t通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:: t5 \, H  V9 J7 F

    2 i( N- b4 B- n0 Q* z) j  v1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制- v! N/ @; y, d- G5 g

    + G, l% _* J7 W6 c: s4 v* u# k2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
    ! K. t- M+ _+ u9 C3 c
    7 h) k# H4 p" j! i7 ~! B3:简单,真正的代码实现起来只有很少,下面会给出代码示范。' p3 E, J$ y7 K$ K1 B6 v% K

    6 _! X6 [( N: X, }* G二:redis% A' E% l. s1 c$ [4 D9 _% i6 R5 Z
    + }$ K: A- k( e0 S; V( Z9 s5 x
    2.1:maven中引入redis4 F4 i# v& K$ `7 a0 G2 y5 m
    4 Q8 c2 z( n" `' d- M
    引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
    - `/ E, A' K- x- c3 \
    % @; B3 R4 m4 m6 G3 I<dependency>
    ( R6 Y2 @% `% D" f9 i9 @  <groupId>org.springframework.boot</groupId>
    # x8 w  p# l- E% `- c" B- u; w    <artifactId>spring-boot-starter-data-redis</artifactId>( j! @3 _" G4 W+ ~
          <exclusions>4 m4 M- K- M7 X4 W7 \
            <exclusion>5 d' j+ ~3 A1 z7 ?/ _
              <groupId>io.lettuce</groupId>
    ! n5 X6 C2 O+ |: K  x4 S          <artifactId>lettuce-core</artifactId>
    $ v1 b) ]- ]' F        </exclusion>
    ; w: g+ c% L5 Q0 u      </exclusions>
    6 ?8 Y. z/ b( `5 m7 Y% @# |' E</dependency>
    3 A1 S, Q% {9 ]" t1 }<dependency>
    ) m* V% [! P. z3 u  <groupId>redis.clients</groupId>' P& T  C; Z+ c8 U6 f
      <artifactId>jedis</artifactId>
    7 Y  u' l) I7 p( {</dependency>
    5 V' _2 o9 {3 f' m1 z- B2.2: 在springboot中配置redis. F8 r0 t/ V# ~0 l! i2 C  o7 `

    , ~4 M" J% o' q2 {  bimport org.springframework.beans.factory.annotation.Autowired;) l+ {9 M0 c6 h" a- U
    import org.springframework.context.annotation.Bean;0 [3 ]! o- O( k
    import org.springframework.context.annotation.Configuration;
    7 b% `7 D# t' ?- N- [import org.springframework.data.redis.core.RedisTemplate;
    $ n: z. q& u+ D1 h1 P9 K  Vimport org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;2 ?$ \/ M8 I+ Y8 F( [4 `9 Y
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    - n( a3 |2 E3 }/ m* g% l% r. n5 k' g) u( G, I0 s- @; H
    @Configuration
    6 z, _* n0 }5 h' C  W  Opublic class RedisConfig {  @8 c3 F# N2 I0 o( r+ X3 ^& |
    5 O9 K; D$ [" e& I: M8 X
        @Autowired+ t0 Y1 r2 d- w0 a( b. X
        private RedisTemplate redisTemplate;
    9 {* C8 U% K( [/ H6 }
    4 {/ w1 R. g2 r$ d5 A    /**4 \; x  E2 C: [* ]# o7 m* e7 \, u
         * redisTemplate实例化
    . e  @  Q8 M3 U- B     *
    , H! G! x: z4 I* {# }$ z6 G     * @return
      D: K& Z' s! g, _7 i7 J     */- ^8 f! S+ N# f$ w
        @Bean( L' \/ T+ M: H( p% e+ p8 F
        public RedisTemplate redisTemplateInit() {7 S1 d2 i+ x1 R$ A
            //设置序列化Key的实例化对象7 h; X2 G+ I$ u8 }
            redisTemplate.setKeySerializer(new StringRedisSerializer());1 M" b( Z6 W' q& W" l, I+ W9 C1 [+ p
            //设置序列化Value的实例化对象" N0 n# x# h6 B6 u
            redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());0 {1 k2 h' ?9 F0 c4 r
            return redisTemplate;
    : X; U( o+ Y2 P/ F  x# d7 x3 {$ `    }
    , W8 {9 D8 l( `& T; n
    # T! o: w  L$ |4 d5 f$ e4 q" ]( h}# e+ w! i# z; a9 v
    2.2:redisTemplate模板工具类
    ( u4 x$ A2 A# G! C7 }
    ! {" H" L9 `( t* D@Component
    " K, Z( S- e& i4 j- Wpublic class RedisManager {
    6 S3 w! G% v% p8 H: Z) b) _/ {; r% g
    7 ]1 T7 K7 [6 w6 b$ N    private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);1 q- J1 R/ c* N7 R# A

    # [2 }7 V8 Z. i* o    @Autowired
    7 T0 q- Q& i3 A! `4 |# W, l. e    private RedisTemplate redisTemplate;
    ! p2 @" J$ @5 i# d) R7 X, B, Z0 [3 w* B  A2 E
        /**
    9 r& y( @" C8 q: `, s/ W     * 设置对象) J+ P! Y( F8 `2 i" f# S3 t. N9 X* v
         *
    ! ]0 U9 [$ d! N: _3 n# ]     * @param key key
    - Q+ k4 R/ g9 `% k* O5 B- ^( ^     * @param value value值, o' v  U& @$ N( P# C
         * @param <T> 返回值泛型' L1 U" T7 c! l( m, T8 e
         * @return 正确的值:<T> 错误的值:null
    / b, u# \+ Z, D9 S1 `     */5 ~2 o5 F; q/ }+ [
        @SuppressWarnings("unchecked"), n# T4 A& }3 J/ L* p. Y
        public <T> ValueOperations<String, T> setObject(final String key, final T value) {
    2 e' b; w- ^5 O; ]$ N* Z3 O% ^        final ValueOperations<String, T> operation = redisTemplate.opsForValue();+ k# ], b* G& ?8 |3 S* R0 w
            operation.set(key, value);# s8 x: ~( e6 x- B& B
            return operation;
    # F7 |1 q* l% b6 `5 j- c    }# z3 s$ c, v, i0 |! E  W8 a
    # z5 g8 N0 h+ B/ W* t1 S  C% L+ Y& D
        /**
    5 F, `$ u6 v0 T& w" n     * 设置对象及失效时间 (单位:秒)
    1 z3 U& p9 D& \9 k9 |     *
    " ^& G4 S: h" U* u/ e# ~     * @param key key
    * u2 k" j6 v* [" {     * @param value value值/ B: N( `4 u$ d* U
         * @param <T> 返回值泛型
    ! g& h; }9 T" W0 v1 N7 X     * @param time 秒值
    5 Q, e5 {  l5 F6 [$ D1 c     * @return 正确的值:<T> 错误的值:null
    0 P: P6 z5 |; f: M  A     */
    8 h/ h, M' Z, a    @SuppressWarnings("unchecked")
    , F( ^, N2 X, m( O# b. q, E    public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {3 s: ?9 W, m6 M; x- s! c
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();1 s- [. Y2 A( e0 t, B# J9 _* @% l& ^
            operation.set(key, value, time, TimeUnit.SECONDS);2 w0 P9 _  q/ y8 K7 N8 @% q, K
            return operation;* s* l$ j* l4 R
        }6 F( m( w' I  t- p1 k- C; b2 ^8 T

    $ |5 b9 {* w$ |" n/ W! _- D+ k% s
    # V& Y: O) Y7 a. Z. X. K$ p3 K    /**
    0 V/ l" }7 \: Z. L& t* b- d     * 设置对象及失效时间(单位:毫秒)
    / X" F" o) S- l! Y4 I9 v% D     ** g) [. n- f. R& R# V% l
         * @param key key
      q7 r% L% q: I# d4 ~# w     * @param value value值
    % @: e6 w% G  N- Y# ]+ ~     * @param <T> 返回值泛型
    " o2 o7 l" s+ b2 t) T" T2 ^     * @param time 秒值
    % X0 c" ], E+ R$ c8 W" H     * @return 正确的值:<T> 错误的值:null
    4 b0 y, \4 J3 l1 O6 N     */* `! e5 l) Y4 B/ v9 W
        @SuppressWarnings("unchecked")0 C  e. ?: M3 D+ j
        public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
    " r5 Y. O. b* `5 t# V        final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    + H/ p& U; w( c" u4 r4 o/ G        operation.set(key, value, time, TimeUnit.MILLISECONDS);5 B! l2 h( n% R0 j8 |: |6 P
            return operation;
    : o( W/ a; @; T5 M    }
    ; o- r  c& x, `6 S5 w4 `
    . g* t/ |: g, K9 z9 {    /*** v, s* \2 j; e8 \; M" ]" X
         * 获取对象) g2 `& f- t% |. m) }5 ~
         *& n  ?- T! \1 S' m9 W! c" I
         * @param key 键; W! p* r  r. B% I  L6 D
         * @return 正确的值:Object值对象<br>
      ~, n% o/ S; O2 C     * 错误的值:null
      ]3 d0 C8 |5 H1 _* d9 `     */
    5 @( E7 U8 G( J" @7 l5 w4 Z+ H    @SuppressWarnings("unchecked")
    - b7 h- ]5 L3 l" b3 \7 e    public Object getObject(final String key) {
    5 E8 O0 m: s" F( S# a8 e/ d        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    5 m7 Z7 i" Q1 z8 x. f. n+ b( Z1 m        if (valueOperations == null || !redisTemplate.hasKey(key)) {
    + B4 ^0 M' q6 r5 b" g& R6 G- B            return null;
    / I" N* d' z5 K! I1 \        }; R' E# T2 f( c7 t# Z6 ?, N
            final Object object = valueOperations.get(key);; o1 e/ r6 g& ~  V7 m/ n  ~
            return object;
    0 U' S5 u0 {% x. B# A8 Z    }
    0 _1 x, L! d( w2 O, U
    . E' r4 K9 h8 q3 T) X: j    /**/ E, x6 x1 `4 ?; r" }1 M$ a5 `
         * 从缓存中获取string值4 [3 |( U* O8 D9 a3 Y
         */ p. [( r: t& A6 F4 {! b
         * @param key
    ' T+ y) q, `/ ~     * @return*/" x% w2 N4 ~7 Y( Z9 v, d1 E' @
        @SuppressWarnings("unchecked")
    & q4 H* o  S9 G& ~    public String getString(final String key) {% ~0 B$ b3 }; v6 P2 o9 G
            String value = "";! D) ]8 `6 ~. I  U& g2 b. D7 `* Z
            final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    8 J: e8 m  }# H% D2 d  E8 p        if (valueOperations != null && redisTemplate.hasKey(key)) {* C' Z* R7 ^; X1 ~0 C) n& o5 B8 v: S1 u
                final Object object = valueOperations.get(key);
    / l, x1 L" |- P. `, _            if (null != object) {( A3 R$ b- v2 i
                    LOGGER.info("--getString--object not empty");( q+ `2 m( _$ T; A, F+ K& g
                    value = object.toString();
    ) b! @$ C1 p& x, i: |/ @, }            } else {
    ! g/ I& P0 T  D! D                LOGGER.info("--getString--object empty");; J5 `, r/ T; L
                }
    # E) ^" r0 {8 j        }: a# p7 v. L- |: H6 w5 q- `" ?7 f+ T
            return value;! S/ x/ v* X! I8 a' d) {; d/ |
        }
    6 r/ T4 ^/ ]$ l7 m$ N) o1 U& j% d2.2:在redis中实现时间控制% D/ h' U" U; H& f, h2 \
    . p  W5 }$ n% q7 r
    2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。
      `, }  _2 [" _- z) q9 R7 j6 v( X2 f/ {6 k! ?
    import com.youjia.orders.redis.RedisManager;
    ' R4 E1 |+ Q& f/ Y% M0 S- X% Uimport org.junit.Test;
    1 }  v8 q5 J5 N& Y% ?import org.springframework.beans.factory.annotation.Autowired;
      K2 t8 U( ?9 J/ O2 y
    6 _: S  D4 D, F, ?import java.util.Objects;! n4 ~) Q# u) ^* [# s* E! h/ R  ~: A
    2 ?4 @) J8 A/ s+ V
    /**
    1 K, ~3 G- W$ g  [ * @Auther: Yrion  [  ?0 C/ c! _
    * @Date: 2019-01-11 23:36
    4 F0 _4 {0 [5 p- `) m */- e  `+ }5 m, o0 q, k6 ?' c4 i/ U
    4 I* {& o$ q6 ^6 f; y
    public class RedisTest extends OrderProviderApplicationTests {
    * G* B- ^9 i6 \/ y2 S$ `
    ' _+ w3 D+ w, V; M7 `# Y    @Autowired- W5 L" a5 P" O. ~# x2 C
        private RedisManager redisManager;
    $ I, Q. j, t. G) f
    3 u1 e. d  c3 q. K: v    @Test/ t" l& l: @2 [8 g0 j; n" t
        public void test() {$ C$ B. }0 @" a  @# F
            controlTime("10000001", 10L);
    0 K' ~+ f# V% r' |* Y. \2 Q    }
    0 M  K% i' ]: W7 ^  o2 ^6 V8 [& x6 W5 G( o- A  {
        public void controlTime(String requestId, Long timeOut) {, ]+ K6 o% ?0 ?: c( p1 [. Z
    2 z& a6 T, n: ^# \
            if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
    2 Z+ [% I3 e/ b* e& }            return;3 p+ J( O  u+ C# C( p7 ~  d+ X
            }7 Z# b0 g- ?& T
            //something code) F* x0 X( _8 I8 ]( Z; V$ `# @
            final String value = "value";- P$ F' G: z  H0 v' s/ H' t
            redisManager.setObject(requestId, value, timeOut);6 l9 A8 B4 G0 b
            final long startTime = System.currentTimeMillis();
    8 ^- y. k! h% L  R        System.out.println("开始控制时间");
    8 Y6 H7 _( p5 T3 h8 E  \        //start
    : m) q4 f6 k1 e+ r) u3 U        for (; ; ) {
    , a7 e0 b+ j5 g, Y            if (Objects.isNull(redisManager.getObject(requestId))) {
    $ b9 a) w8 l' E; K4 Y4 q% w; f                break;
    ! I" b- _7 `( q" X            }
    7 e# ]6 ^2 n0 ~) e" I        }& ?9 J& H3 X7 T6 V3 ^- C$ I$ I
            final long endTime = System.currentTimeMillis();& j1 n( k  h7 l3 Z  R* p/ Z

    ' G6 C* B9 b) i* }$ f' k        final long useTime = endTime - startTime;
    : ?* N( t3 ]8 c: \4 o/ B. ~7 I9 t( _) z: ]! i7 i  B
            System.out.println("一共耗费时间:" + useTime);
    + r8 Y; [# {6 c2 @; G- d- ]    }
    % @5 O0 F& {* _6 w6 U}7 R+ T2 y. ^% @
    outPut:
    ( k' }4 v  g' S7 B
    ; l, n7 Y( |0 g/ w开始控制时间
      x7 |5 Q( H6 d& H3 P, h& F一共耗费时间:10042
    5 g; X1 Z% W% `& ]三:总结; C# u, M2 t) K) p( V3 O) |

    / \2 _+ q# ]2 s- m; s本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!2 B( a" E; m( ]: V
    ————————————————3 I. L0 |7 m, K  X4 d
    版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。3 ^7 k6 \$ M& q) ^
    原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325$ f+ B3 j6 x0 n, w

    , m) [2 n  Z. @' A- Z5 P
    % Z0 [- n) _& `# O$ K5 d- t$ R
    zan
    转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信

    0

    主题

    0

    听众

    27

    积分

    升级  23.16%

  • TA的每日心情
    慵懒
    2020-5-6 10:54
  • 签到天数: 1 天

    [LV.1]初来乍到

    自我介绍
    马上比赛了

    邮箱绑定达人

    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 注册地址

    qq
    收缩
    • 电话咨询

    • 04714969085
    fastpost

    关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

    手机版|Archiver| |繁體中文 手机客户端  

    蒙公网安备 15010502000194号

    Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

    GMT+8, 2026-8-5 18:52 , Processed in 0.545016 second(s), 57 queries .

    回顶部