QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1500|回复: 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
    1 |) k$ W4 o: Q- R# v: C$ x
    Java如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。4 t  c% K3 n$ L. Y$ ?
    : ?5 m- `+ M6 \* P
    一:时间控制的几种方案# J2 J2 N: W# i( I. W% G
    % Q" Q" ~$ k0 R7 U$ O3 z
    1.1: 从线程方面解决
    6 ~7 E1 E) o& A- M4 l0 d" |* b2 {2 k: c9 X9 E1 h
    最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
    8 s2 w! O6 J1 }# `" {
    ; V: J# E1 d' I1.2:使用Timer
    1 \" D- C7 n, o- r# a3 u' B- _  ]" K/ ]
    查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:
    ( m* g! e' a- H7 d. H- z
    " I0 g7 V- }, \& A+ n$ Lpublic class TimmerTest {; [3 ?, ?/ l7 ]2 L9 B0 W3 g2 p
       /**
    4 f2 W3 o: t  p6 K- D; }     * 测试方法
    - [9 @* e* \2 T2 o3 [     */8 A2 D4 D2 c2 [! I
        public void test() {
    , W: c1 ?5 C# n! r" W        Timer timer = new Timer();  C  A" I0 ]. \3 E
            timer.schedule(new MyTask(), 800);
      g- i* U. g8 a9 {' j, f% z    }; P* U7 x- O. X0 p; P& i# b
    ( j  C' i1 g2 X' d( w% g
        public class MyTask extends TimerTask {
    5 v( O* B2 s  w8 Y- l+ N7 w+ P7 i) ?7 T5 z
            /**$ Z% D6 w; _* ~
             * 运行方法
    ( c8 ?7 |+ }2 p8 c0 H         */
    + o4 _/ X  A# \        @Override9 n. z% ~/ V, K( T7 y, [
            public void run() {
    ) Y. G* Y' {4 M) T; n( A5 M2 H            System.out.println("输出");% I* O+ j. B, O* U9 B7 K5 i) P
            }& i) c* |5 }* b
        }
    2 M2 J" o) }6 v& B6 u. u0 P}" L# F3 m6 ~2 W/ Y$ |8 j
    这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。0 \  ^: J0 `0 G% P% T
    / w2 }; g" B3 K+ M) j! X- x
    1.3:redis延时" {1 M8 `8 g% `' l+ I* ^) g" q

    $ r/ d, N5 ?+ P5 k6 n在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
    9 K) W$ B" x# k! K% n) w8 I" F3 J: `, u+ r& X7 A8 S! o! U  B% G
    7 W4 s! D1 w" |7 F: y

    ; c$ J5 [7 j' O! t4 i5 H+ q' g( X6 u通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:0 n2 d1 \/ x: p3 g2 J

    / |3 Z( s0 I6 P$ ?1 ^' ?. @1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
    9 ]5 E' c1 g0 v" r. w# B' T8 Z, X9 m$ r( F5 G; c
    2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
    ( K, l  W; C9 O! R& w  I3 d+ j: o* w5 c
    . N  L: V1 B& @2 I7 c4 M1 M3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
    9 V! m3 s( |3 W  s( p6 X6 l+ ]0 H& z* ~$ @' }
    二:redis) h$ |+ w( u& |/ b3 X4 }+ H2 D

    + C9 z6 A/ G( d- x2.1:maven中引入redis
    9 l; l, Y! q3 m) c6 x
    , f+ F+ ~) m; F) t0 }引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。# \$ r: r$ E! K/ \+ M

    6 `  }+ I4 W, n; N/ Y+ C4 I* |0 _<dependency>1 l9 D: C$ ~: y0 M8 B' Y
      <groupId>org.springframework.boot</groupId>2 l* Y" A; @& k: h: n
        <artifactId>spring-boot-starter-data-redis</artifactId>5 K1 U7 c, J. z: t4 r" V! k" a% [' G
          <exclusions>
    % k3 v' k/ a( Z3 a2 `        <exclusion>2 M- r3 ]2 V  Z& f
              <groupId>io.lettuce</groupId>' }: N0 i9 ]+ Q: }
              <artifactId>lettuce-core</artifactId>$ W, r8 a' n5 k, D) K) Z
            </exclusion>2 q0 w2 U7 I6 T( h5 N& \
          </exclusions>- w# ^! V- N) [# ^* O; o, I* v3 R- o
    </dependency>9 w2 C) I& H: g7 Q/ E
    <dependency>
    8 n9 ?6 g2 b  }  <groupId>redis.clients</groupId>
    ) J8 Q% @. v5 s* j& m  <artifactId>jedis</artifactId>7 u$ P# G; U0 S; l
    </dependency>
    - M; K& e/ N% e$ B2.2: 在springboot中配置redis3 G& c! ]8 M7 y- g& a6 v0 s5 M$ r
    # b9 \) ?- X) a# t: s
    import org.springframework.beans.factory.annotation.Autowired;
    ) ^% X: d9 h& [( Bimport org.springframework.context.annotation.Bean;
    4 }  p  {$ w( a7 fimport org.springframework.context.annotation.Configuration;
      J5 }3 r! v2 _  nimport org.springframework.data.redis.core.RedisTemplate;, D( _# K7 k, }% J7 t) [9 t+ I
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;& S' A/ r1 A2 D  a2 p5 u
    import org.springframework.data.redis.serializer.StringRedisSerializer;0 f% C* E" m6 }) p4 ]3 M: a

    . v% a$ [7 F5 w% L. y: Y3 H@Configuration
    ( \1 W: }. g- J5 H% p5 U# \: M8 spublic class RedisConfig {
    5 V: P: g2 v. j; A% }' A. x
    % P- r9 W/ W- b4 W9 }, C: f    @Autowired
    : i6 G) i" `8 W0 l( E/ V( h    private RedisTemplate redisTemplate;
    8 j% r* }0 H# J  G. o& b0 s0 X$ D- v/ j
        /**
      X- z; Y' O1 X, ]     * redisTemplate实例化6 f- @+ s, n2 D! L/ P
         *" r& r# j+ p6 b- e- ?
         * @return4 w% U6 j1 t0 e4 Y1 D
         */
      B2 s# E( Z4 k) |: ^/ Z    @Bean
    2 W3 J9 B3 D- }" }7 G7 `    public RedisTemplate redisTemplateInit() {
    : N  ]4 g* U  ~% z        //设置序列化Key的实例化对象
    0 i5 K' a3 e  U5 G0 A% `5 E        redisTemplate.setKeySerializer(new StringRedisSerializer());1 W( A) i! W+ [% ?7 p
            //设置序列化Value的实例化对象
    1 A, y2 L  Q" A0 b" K6 R4 r5 t2 f        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());3 D3 |% g1 t3 `7 j, h. j, W
            return redisTemplate;
    $ ~/ y- J, o9 _" k    }  z! K7 @# z! H& C5 c

    4 K, d' O8 C2 ?$ D+ [3 T}, w6 z) V1 F& e
    2.2:redisTemplate模板工具类1 N1 i- p7 J6 w: n, d# m

      i# c6 y# k: A3 p" x* f' u@Component
    2 L5 q/ |+ i% P& e5 E+ lpublic class RedisManager {+ U; v( G8 Q# I5 ~8 x8 i/ g

    & d) q3 S  l; _5 |/ }7 y    private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);! Q& R, A0 ~" W3 p

    # n7 w0 j+ Q" O9 P5 ^: Z    @Autowired. X# F$ Z5 }& v+ P5 H
        private RedisTemplate redisTemplate;
    - ~- y& Z5 g  d  b! E& K  P6 u( |4 f6 t2 L# ]1 z/ U" Y
        /**
    . t2 c; W& `, w     * 设置对象
    ; v1 w8 b- ]* l" ?! s  @8 P3 h% v: |     *
    ' t" l5 l' ?; Y  L# m$ c8 H     * @param key key
      p, b  l% I$ U6 L$ B% j& N- K7 ?     * @param value value值
    # J3 D# L3 g/ }6 T4 u& j2 k! {$ V: U     * @param <T> 返回值泛型
    4 b* g9 n% r' G9 ~  D     * @return 正确的值:<T> 错误的值:null
    $ q3 e1 O6 @2 V5 R% \' r' G& [     */: V9 b. C, |- V, X
        @SuppressWarnings("unchecked")& W, O/ n% p- d9 d
        public <T> ValueOperations<String, T> setObject(final String key, final T value) {
    ( L% E* r+ G4 Y4 p        final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    2 C- J6 q. e" |- }: }! g) L        operation.set(key, value);/ O% _: X0 g9 s! \% F3 H
            return operation;" o8 ?* m+ k& U
        }
    / F! v- G4 l5 S+ d. ]4 }  u9 @+ D( J* M* Z
        /**! i/ S' E" a! N+ _/ k0 x
         * 设置对象及失效时间 (单位:秒)
    1 K. C/ j# g2 J" |     *; I" p  n, L* M1 T5 S! D" v8 s
         * @param key key' A5 s( |# I+ s# k
         * @param value value值
    " S0 ?4 E: M$ B1 `% d     * @param <T> 返回值泛型
    4 G6 a' ]* K! ?3 v. I     * @param time 秒值7 {' Q  A! O2 ~# x9 |0 d
         * @return 正确的值:<T> 错误的值:null3 \6 R" Z, H" Y5 q# W5 @
         */3 @2 u+ G: _/ I: s! T4 [/ k8 W0 I8 [
        @SuppressWarnings("unchecked")
    ) g5 p- W) e! f    public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {" J5 Y1 w) F) ?2 N$ n
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    % @1 v( e7 b  u3 U- p- \8 Q8 k4 _        operation.set(key, value, time, TimeUnit.SECONDS);0 w; `5 I$ h3 }1 _& s+ {& W
            return operation;5 A7 I: J' ~( ?( d
        }
    7 i0 Z4 P& h0 c" T  E4 F0 `
    2 C' m1 d& m6 o- T
    & t6 b" ~9 @2 j; }; o& }5 V4 |    /**8 l( h) x  \9 L) w6 h: {
         * 设置对象及失效时间(单位:毫秒)# k* a4 m+ z' p& t, b% }, d
         *, X; ?. C/ K7 g1 O. b
         * @param key key$ j; ?3 S, {4 ~
         * @param value value值
    ( e) @1 z5 A( g     * @param <T> 返回值泛型6 m+ E5 i+ [& s# D! J; |: c
         * @param time 秒值& c  O9 g" }6 [; A8 w4 L% ?+ _1 t# W
         * @return 正确的值:<T> 错误的值:null
    / i6 @5 F8 u& l8 q" p, K7 P     */
    8 Y1 Z# N; Z  k; q6 U# E    @SuppressWarnings("unchecked")
    ' w* r3 [9 \6 u& H    public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {9 E$ m+ \1 M0 O
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    2 [8 M, U5 C- T$ Y" e        operation.set(key, value, time, TimeUnit.MILLISECONDS);
    6 g0 G5 P. M1 e7 r9 d        return operation;
    ' o" p( @  l' K; ^    }% l) c+ {* \- F2 d& L
    0 w! N7 O9 x# j4 P$ |
        /**
    ) E( o4 Y# H- U4 S     * 获取对象
    8 g; {0 k# h. }/ M     *- `  N4 e( q% i1 H1 I
         * @param key 键
      Z" v; i  F( w- Y* O, L& F, O( _     * @return 正确的值:Object值对象<br>: j4 H0 K: q& y7 G- b1 N  l
         * 错误的值:null; q( y" ~. H8 x: L8 W4 ?' d( o$ k/ B
         */4 P8 g8 e  K4 }2 g6 m
        @SuppressWarnings("unchecked")
    * O2 Q% z$ W/ j# ]1 p    public Object getObject(final String key) {( W0 @) ~( s( C4 i! g  ^( H: z
            final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    ! R3 |5 O2 C$ N, {- q        if (valueOperations == null || !redisTemplate.hasKey(key)) {
    : Q: d& }/ p: Q6 |3 k            return null;" s# Z( z& N1 F3 \& V6 u5 ^
            }
    % k+ S' g6 M9 J% x+ R' P* I" w1 [        final Object object = valueOperations.get(key);
    3 v1 M' K# \- h5 B3 I7 ~5 X) B' v        return object;
    $ U0 N7 U, j+ y' B    }) }# _0 J0 i+ z$ ]

    + a: n- k3 i/ ]" {7 C6 Z& ~: [    /**+ T8 X/ I4 r% y% N5 Y- Z9 _) Y* Q2 O
         * 从缓存中获取string值
      e8 M  z" W( l" c     *6 V# g2 Z9 O4 U3 D  P4 n! ^
         * @param key) o$ H: K- B+ U% G6 P2 F
         * @return*/4 _; O8 Q5 Q4 O2 f! F
        @SuppressWarnings("unchecked")/ G, a: n  K% |9 D* c. W) v
        public String getString(final String key) {) H5 M" m' |$ d' K
            String value = "";
    ( }& m+ P' y$ |/ |; }3 G( k2 \        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    ' Y# {! q; G% @3 K; A' ~5 x        if (valueOperations != null && redisTemplate.hasKey(key)) {
    2 @- R& p# U/ r* i- p8 n  R            final Object object = valueOperations.get(key);( P- [) L& U& Z  w6 c
                if (null != object) {
    ' F# P& p- P3 M% ^2 h, V- R                LOGGER.info("--getString--object not empty");4 R8 B' R2 _: J4 n# J. u5 b& U: E
                    value = object.toString();
    2 O8 E& ?4 [$ ?6 [            } else {3 p8 g6 p' M" C- x5 @7 m+ d
                    LOGGER.info("--getString--object empty");$ ~$ o6 ~/ O! ~# B
                }! n& n# @% \! D- s
            }
    ' k# B! I( P% Y7 G' p        return value;
    2 z( O/ E0 W7 Q' [5 O( s. ~4 X    }
    + ]7 I8 Y( ^# Q2.2:在redis中实现时间控制* x% n3 x/ g9 X, Y6 {
    5 F( E. i, [' W
    2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。; @$ [% q) E; N: o" S" b+ n, {

    1 V+ u1 ]- a* kimport com.youjia.orders.redis.RedisManager;" A. l6 B9 I. ^+ l* S! ~" s
    import org.junit.Test;# {# u( @* I$ n+ E) D% f7 \+ F, C
    import org.springframework.beans.factory.annotation.Autowired;& ^# z, a3 F- f5 z4 _" [# _# e& [

    ) k- e; S2 e8 r& w) w4 R; z4 F7 uimport java.util.Objects;0 L6 `8 K, C1 g. I% \! j+ B5 @* _

    3 ]; Q) l7 E) b; z/ q9 I/**+ E8 p" o: R, n3 \0 f
    * @Auther: Yrion& j- W, z2 H: s2 k/ s  a
    * @Date: 2019-01-11 23:36
    & @8 r" z, R4 C9 @ */$ b' l- k5 \9 I5 d% A$ W0 k

    6 j' `* t* m7 o, qpublic class RedisTest extends OrderProviderApplicationTests {7 v9 e/ ^3 s/ P

    , _4 J2 E9 Y4 x9 E7 i    @Autowired6 ?$ d0 O  p! J2 V
        private RedisManager redisManager;# X  j. y" o# F: N) H) h' m8 t

    " X1 G/ E7 Q! b. O" h; [2 K    @Test" g" [% o. Z+ g; o$ f* f
        public void test() {4 E7 r, {$ X( V! P: h
            controlTime("10000001", 10L);0 P2 C2 B+ h, b2 s
        }
    . |$ c! \1 f0 `3 v
    + q% o2 J! x/ }8 P" ^% G    public void controlTime(String requestId, Long timeOut) {- g* q7 V8 N# j* a

    2 g3 v: f" ~/ h, ?/ v        if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {! Q; Y% M  C5 `! q$ v9 v5 r
                return;- K& f% k+ L  |- v9 a6 R( t3 r
            }
    % P0 I' g; V( Y) \# U% I2 h        //something code8 Q0 @0 h1 b1 u
            final String value = "value";* m* }' j4 c+ G( Q
            redisManager.setObject(requestId, value, timeOut);, s5 W5 G( G) g! ~
            final long startTime = System.currentTimeMillis();( c% h6 g  o. o- Y
            System.out.println("开始控制时间");* d% Z  u9 v, a% y+ L
            //start6 y; K: Z, M" q$ i" n) O
            for (; ; ) {
    ' K$ V8 s$ _  F6 g& ?! k            if (Objects.isNull(redisManager.getObject(requestId))) {
    - m$ l7 M9 ~% [9 K, N                break;
    & N( K& ?) _$ \            }* g- a- A% k( r* R: O  ?. _
            }
    # P; Q: S* \1 G' w" m$ l1 K% @        final long endTime = System.currentTimeMillis();
    ; Z8 i+ H; ~! k% Y; N$ q- m7 h3 B( m% X( b3 v7 d1 I  E0 W7 I
            final long useTime = endTime - startTime;; Z+ X' L8 y1 i7 i1 l( T& S( v

    ( ]( v/ L* N- o* ^6 H" }        System.out.println("一共耗费时间:" + useTime);5 o% T0 F2 u  ^7 ]
        }3 x% `$ f4 d4 }0 u8 W& O( n
    }; f& Z: L! x3 ]9 U
    outPut:
    8 Y7 V9 j7 z) V
      v/ E6 O% L% b" a' p% Z: s开始控制时间$ o$ [* P9 Y/ ^/ R6 _
    一共耗费时间:10042
      m- u6 A  C+ o5 D' I) S& ~三:总结! W+ y* g- O4 o" b: G' W7 K4 q
    - S4 U4 k! n$ n; x2 U/ n/ P" _& ~
    本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!/ X- S" R- o+ w- l8 t
    ————————————————
    " v( P! g5 {- n+ r( A* n( C( l' H版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    4 ^) |7 l& I1 V7 d原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/1058933251 N5 o, B0 o- }% d5 m0 i

    $ W; R/ y+ Q8 o7 |/ k0 I$ u( ^
    : B$ m) D* a. N* m3 h+ n
    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, 2025-8-22 01:14 , Processed in 0.359683 second(s), 56 queries .

    回顶部