QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1612|回复: 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

    . p9 u/ i* d, u& y! G- d  J( gJava如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。  k, D: @. c( [* P' M

    " C9 h/ D/ B6 d% j0 T5 T一:时间控制的几种方案
    ) Z! H5 C0 q3 N2 b- q2 |; @
    ( ^& P* y) R/ @( k1.1: 从线程方面解决
    1 l& q2 R9 @4 z# d7 i; G2 g6 l* t- O) `" S2 Z# y# q6 i) @+ P& A
    最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。% y- Y. j, f0 ?0 a8 ?- Q+ q
    ' a4 I7 V/ X( |" @1 a
    1.2:使用Timer
    ' x3 g( C1 X" `; h7 H7 _/ I
    . R4 s% j% ?* c/ t" |查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:
    ( m2 g# h  P; U) a" O! u- P7 O" n5 ?. T' `9 }% N  y
    public class TimmerTest {9 p- J- O, B( c2 V. x* x, S/ F
       /**8 }" n+ V$ ?4 z' A; J
         * 测试方法2 l/ R. F4 z! P; g
         */6 D) `* H* q+ |0 b! ~# p( X  X2 `* f
        public void test() {% x) N+ S  Y- ]$ e3 Z% {' N# }
            Timer timer = new Timer();' t2 {4 v6 ~& v7 a; k) A7 b, F
            timer.schedule(new MyTask(), 800);9 n. q  S: D/ m6 ~# M+ a
        }
    ; z  Y+ e' d3 ~) H: G  x$ n3 S/ j) a; e1 E/ l
        public class MyTask extends TimerTask {
    ( d1 r! ?" O% q) s" Y- e+ j
    + U! m& {3 l* U+ U        /**$ G/ j6 I2 f: s& L$ W. J& u1 P/ C
             * 运行方法
    " j  z) g; c# K1 T& b- ?/ F( Z         */
    8 e# q" C1 Q2 q6 k( q$ o8 O        @Override
    + |/ e) O" p# @. X; z) _7 F        public void run() {- y  s( K! v% w* |3 J
                System.out.println("输出");! D3 N; }8 w/ Z' n" `/ }
            }
    ; k; \/ r& P% _5 a/ m: Q( S$ U    }
    . M3 z7 H9 X6 i& A1 z# }}
    5 x( _4 e# _4 q& J- \/ b6 o这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
    ' x- P8 {  K, X4 O& M, @
    5 y- n5 u7 j" n# D3 B1.3:redis延时
    $ L- n  X, `9 T2 A) d1 v5 f
    : S2 d7 ]8 e' G+ D在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
    7 z6 t! @1 Z0 Z, S' V- {5 o4 s. v8 ^7 j8 ~

    $ ?+ o: S9 G# D" m
    - N$ o$ l. Z" @7 K4 ]0 A通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:
    $ g0 A6 O( E/ N4 {+ G' W6 q
      M) [% J9 ^' E/ n1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
    ! a+ D7 o1 D9 O9 ~, z6 x) Y% j4 F) e
    2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现* S" p, V, I/ z0 G

    " a" S/ _$ P+ w3 `- S3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
    $ ?8 N+ \. t5 b% C8 f, `% t
    6 k5 ?( ?4 |7 z二:redis0 a! ~0 ~9 O, n3 O
    0 }) v( N& S4 D/ r
    2.1:maven中引入redis
    5 ~" B, U4 K# ?$ |; D( h# k) z
    3 s, L3 @' c, p3 r引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
    & Q1 i6 }2 ]  H4 x" E2 ?$ C+ }3 m2 F/ j& M3 j$ ~6 l
    <dependency>
    & M1 e$ K: K9 ~+ B  <groupId>org.springframework.boot</groupId>% L# }( {; I, z; }4 {- g! v
        <artifactId>spring-boot-starter-data-redis</artifactId>
    + G' P5 z  K2 y9 P      <exclusions>% N+ ~# c" D% k: P1 O
            <exclusion>6 `# ~4 _; C4 q' X6 n& B; @% h
              <groupId>io.lettuce</groupId>
    3 z6 G& i$ H* ]2 R+ I          <artifactId>lettuce-core</artifactId>
    $ p/ d0 L" T- N8 c* \9 x3 j8 Y: ^        </exclusion>
    * _9 [5 ]! k3 c1 E$ j5 B  s/ @      </exclusions>
    $ O  d3 \' m) E& ]4 P</dependency>2 M3 o, `8 \- k# j8 |/ f
    <dependency>
    * U* E/ l9 i  N3 u: m8 i  <groupId>redis.clients</groupId>
    7 Y7 j3 A: n; C. s/ U. H7 T  <artifactId>jedis</artifactId>
    # g& }- U- E) k) U6 g</dependency>
    ; r+ _) k/ O" b1 I, s) @& q0 B6 R2.2: 在springboot中配置redis9 v, S6 W6 W. s/ t

    # {- n& Q2 {/ K. \# Aimport org.springframework.beans.factory.annotation.Autowired;
    2 q1 y/ A# `/ h8 ximport org.springframework.context.annotation.Bean;
    + Q/ P# Y9 t' f  ximport org.springframework.context.annotation.Configuration;- S) P8 }( v9 O% M% k% ^# e. A! P
    import org.springframework.data.redis.core.RedisTemplate;9 z/ b* [5 x+ v6 Q- o' X
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    5 `  Z9 R9 I% x: k3 @import org.springframework.data.redis.serializer.StringRedisSerializer;
    , V, x# s# w% j3 c( A1 E  T4 `; F9 h8 M4 }* Z( V: ~+ g, c
    @Configuration
    ) _6 ?" f. q3 Z4 e  L, Lpublic class RedisConfig {5 a/ |0 G" j% |4 k" }9 L

    9 |! }4 \5 ~+ {: t4 C7 A    @Autowired
    1 z% S! l9 R+ _) {    private RedisTemplate redisTemplate;+ f7 R' Y6 V5 P* }

    0 S9 Y8 B3 @; o    /**# |- O& v5 U: b' {8 v, H
         * redisTemplate实例化' P, m- V# q! k. W0 I- V5 P; R
         *. {" J8 o4 r) g
         * @return
    6 C3 B9 j. W) W( l" p/ C     */
    $ _: }  F$ @! Y# m$ ]% L, w    @Bean/ j9 [  |0 R' R
        public RedisTemplate redisTemplateInit() {$ k7 R: H- {2 u% g# E5 z
            //设置序列化Key的实例化对象& }. R$ t4 E" K# t. y1 e
            redisTemplate.setKeySerializer(new StringRedisSerializer());
    ; ~) m- E! L0 t4 F) P1 ~8 R) m        //设置序列化Value的实例化对象
    5 p1 `0 Y+ Y* F4 Y" Q        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());6 ^. y8 Y  ?! I* R% P
            return redisTemplate;
    7 j' Q7 ]& N* q, d: u- {    }, Z9 z; G& _( m" ~7 o# j

    % i2 i6 g: \5 L7 b1 y}
    & s5 U# R6 v0 x8 B. {2 }) X2.2:redisTemplate模板工具类
    % B1 x! N5 ~. x/ m# h2 y9 ]! w- N/ h* I: G3 |7 m0 O9 }/ E4 u0 j
    @Component* f" _* \- y* G2 v
    public class RedisManager {+ E4 }/ {& t* P- K
    9 c# Q. w9 r) l3 D6 S/ V" `) G! X
        private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);# S8 ?8 ~) B6 _6 r+ Y9 U* g
    2 h/ O9 h7 r* s; J. {. B% w0 h3 }
        @Autowired# t1 C: W5 B9 c" I& ^" Q1 g! f
        private RedisTemplate redisTemplate;
    . [0 F! T9 P% Y7 ^- i0 A( x/ z+ t2 L7 G
        /**" ~% Z! j2 M6 K. n' o: ?9 X
         * 设置对象
    # H5 t$ e- E4 ]9 z     *) T: i* g- m/ J; S
         * @param key key
    ' |9 y+ I+ S; k: ~* ?* P4 P     * @param value value值8 S, p4 |5 k' X
         * @param <T> 返回值泛型
    . d" D2 n4 N/ [% e  g3 ~     * @return 正确的值:<T> 错误的值:null3 O2 l. j6 e8 `* D$ _
         */. R' [5 |% L7 F2 r1 E, v9 m
        @SuppressWarnings("unchecked")7 U0 j) K& {) h" \
        public <T> ValueOperations<String, T> setObject(final String key, final T value) {0 c8 O  _, Q- j1 j" J
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();3 O6 j! _7 @6 f3 L- E
            operation.set(key, value);$ h' l: a4 [4 P: O$ T- J' r
            return operation;
    $ g2 B: d; Z9 x    }, p6 f% P8 X0 Q
    0 Z. _) y3 z. V* }( ~
        /**
    " r3 m( @1 L" t5 i6 z     * 设置对象及失效时间 (单位:秒)
    ( e3 M; r& S# n/ a( F) A     *+ D5 C/ L. o9 K5 o" B
         * @param key key  K' K, O1 J+ z' d6 X* ~* Z
         * @param value value值
    9 q; `# }# a, J     * @param <T> 返回值泛型
      G6 \3 `% ]+ S! c( S/ g" Z     * @param time 秒值
      x! q) n# ]" R6 ?) [     * @return 正确的值:<T> 错误的值:null
    1 O7 S% U7 M" p3 `; {, S# z     */
    8 ^9 ^6 e6 Q6 m4 g2 R- g) z7 b, l$ G    @SuppressWarnings("unchecked")
    % Z% M8 H5 u% F' t    public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {6 O+ N. J9 v* ]$ m3 G: F# ?) G
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();  e! A8 c$ b! E6 A* ~+ s
            operation.set(key, value, time, TimeUnit.SECONDS);" T) {4 v* g9 v! A+ {
            return operation;  ]2 ^* b8 |' Z+ v- k
        }
    . ~1 G. Q; r5 w3 s8 q6 s  d# ?1 J& ?& V9 }
    " G* a9 H+ o7 e, z1 n+ U
        /**
    . s! O, m- `: |' W# X1 F7 A     * 设置对象及失效时间(单位:毫秒)
    4 l# e: B7 t8 g. Y4 i     *
    $ m- u6 `8 a) a" p6 P     * @param key key
    " K2 C" g. `: K, x* i1 n     * @param value value值! L7 O4 M6 s/ Z. H
         * @param <T> 返回值泛型8 G+ `+ m) P, m1 K% i
         * @param time 秒值
    1 w% b& g% x8 C/ o' @9 }6 W     * @return 正确的值:<T> 错误的值:null
    & F: v# \3 w1 B: C7 u     */* C- `$ U4 G! X5 A0 {* [! j; s7 h
        @SuppressWarnings("unchecked")
    & [( C- P9 G0 K+ I/ {    public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
    4 X: c5 b. N. V# R) P% Z        final ValueOperations<String, T> operation = redisTemplate.opsForValue();) T: W% M3 g4 U% c0 _2 `
            operation.set(key, value, time, TimeUnit.MILLISECONDS);
    ! T, q$ X! z' V  x1 e        return operation;
    . V% F% V6 x. \; x    }, d! t7 Y4 ^" \
    ' w0 a+ x# i0 @# t0 G
        /**
    * ^9 P9 M8 E" Q6 z6 J     * 获取对象
    2 ~" K. `  @/ E- e     *
    % l6 c" _! a" }1 v     * @param key 键
    6 z# j+ S- I1 s7 w3 \0 d5 `' G     * @return 正确的值:Object值对象<br>9 a. |0 ^) t' v7 X- p. X, S( K, z' @
         * 错误的值:null  \+ ]( p' s9 T! R
         */
    5 Z/ K% m5 S' h0 f: p: X" }    @SuppressWarnings("unchecked")
    6 h4 N: m1 t' ?- U9 w  p; d6 q5 i    public Object getObject(final String key) {
    $ L: d$ u' O3 w' F* A0 N* V3 L        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    + j, L7 d2 p4 k$ D0 Y5 U, J1 s        if (valueOperations == null || !redisTemplate.hasKey(key)) {' H/ n2 ]  i- E1 P, `" r, ~
                return null;
    8 N9 C* F5 v- c& K1 m        }
    / T4 G( Y" Y3 @3 \* D$ Q        final Object object = valueOperations.get(key);4 _7 ^4 o& c6 y
            return object;' G3 J7 y6 B2 T7 Z/ X1 H
        }" [8 \8 O: D! R2 u1 c* f# m1 X3 a

      ^" M% |, u0 d5 A+ Z+ ?) }; Z, n    /**
    7 G" N8 x! ?4 L& r: n     * 从缓存中获取string值, d& K7 ]& R7 Q* l( G9 \+ Q
         *
    1 z& \5 h7 h! p+ `3 p     * @param key- o2 r0 V7 m, R: O
         * @return*/
      k- M+ U# R) Q. m6 `    @SuppressWarnings("unchecked")
    2 r$ X1 F, Q$ G9 R+ M    public String getString(final String key) {+ |% S% P! n/ w- j( x4 |% r
            String value = "";; P: r6 [8 B% Q5 e4 q
            final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();" n  }$ w$ B: h* f
            if (valueOperations != null && redisTemplate.hasKey(key)) {
    ( c- a2 @0 c! h0 K            final Object object = valueOperations.get(key);
    3 M, N/ \& g" Z! k) R/ S            if (null != object) {
    1 l8 k5 ]. k0 k: M8 h1 C& S                LOGGER.info("--getString--object not empty");
    / B" K3 u" z# t% O* U. @' b                value = object.toString();. n; H* Y- {: r: w) g. ^* A8 s
                } else {" a) ^- `1 w# l% s
                    LOGGER.info("--getString--object empty");
    % f% q7 W3 s4 B8 V4 v5 w            }0 [' J( c1 P+ b, \; D
            }
    ; y( i) a1 h4 z        return value;& U3 q" B5 Z+ s7 J! Q8 M5 \
        }6 T" c+ [, H$ X7 u' [2 P* `% w5 j
    2.2:在redis中实现时间控制7 V( c% z, @4 c6 k' R

    : S" h) K( d0 A' ~8 K2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。, h/ [& j; {! A8 D

    : d, x' T! c9 a- z; A0 wimport com.youjia.orders.redis.RedisManager;9 g4 I# o- ]; a. r0 f3 m
    import org.junit.Test;4 ?; Y& W1 {3 ?% l+ ?3 @: C
    import org.springframework.beans.factory.annotation.Autowired;
    5 @/ N$ U  g: ^0 r; Q( u, M
    0 r: d& [' \. ]2 L- ?9 K- q3 Z' wimport java.util.Objects;- s) `$ L* l5 P+ l' ^9 t

    * J0 R* o  M: i! [7 A* [/**
    0 O& Q( X0 s  @# d' r * @Auther: Yrion
    & h5 Q/ ?; ~9 v, h) V1 N * @Date: 2019-01-11 23:36
    $ J) a" B2 _. S! \) {# r */' {" T4 f; u% [) {0 o5 ]3 n: j6 W

    8 F6 b: q. j7 E5 h7 Cpublic class RedisTest extends OrderProviderApplicationTests {
    5 T# X& @, s4 ]" r$ a, Y; B
    - h% k! c. M' d; F7 r    @Autowired3 k3 q9 }; C$ V7 {5 t+ O0 @! Z
        private RedisManager redisManager;
    ; C' F  a# N1 k6 P3 }% h; c
    " y6 a9 s" u9 L- E% V" J' u* g    @Test
    3 }) w# i5 y: S2 \( ]    public void test() {
    ; Z! W6 ?4 X: x$ j" M! O  z        controlTime("10000001", 10L);
    . b0 H4 w" M' o: ~. ?* r    }8 m- _7 `( K+ }7 a- x+ t
    ; S' W5 c; N0 C3 Z9 H# e
        public void controlTime(String requestId, Long timeOut) {' a6 p0 }  b# y4 H$ Z. n
    ( e  l& ^3 p2 |+ @+ r
            if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
    6 q* D# G" o0 r% z9 K4 [7 z            return;. l" j3 v  R5 |/ K
            }( e4 E% N' v! t( L; p" l" m& C
            //something code
    . h1 K& Q+ ?. K4 S* i7 O        final String value = "value";
      z( W% A7 P6 q- }        redisManager.setObject(requestId, value, timeOut);2 i! S, P6 \7 L. C; g4 S0 u, O
            final long startTime = System.currentTimeMillis();
    + y  a+ R; w6 q5 C4 }        System.out.println("开始控制时间");) [; _3 e2 u3 m& L
            //start' ?+ ?8 v+ F1 n! C+ j
            for (; ; ) {& c: w/ F# `( r' T
                if (Objects.isNull(redisManager.getObject(requestId))) {: {% K2 {8 b, p* ?/ }
                    break;
    ( Z8 J: t4 Q4 ]- T, X% j$ K            }
    3 _1 J% v3 T4 M& {  O3 ?        }
    7 G1 t, W0 k. Q        final long endTime = System.currentTimeMillis();. g& q) l9 ^8 D7 A' I, Z  A
    5 I! ^) [7 }5 Z4 X8 e! N; N
            final long useTime = endTime - startTime;
    ! Y3 _2 p1 l7 [/ ?. F, s: r+ G6 ?! O0 p
            System.out.println("一共耗费时间:" + useTime);
    % U, H$ E* J9 n5 F    }5 B" X# O; j6 [+ o+ U, R7 I
    }
    2 a( I7 s6 k. F. N( }outPut:
    ' u- R1 h, g- H5 b
    7 m6 Y0 m& j/ t! j# S开始控制时间
    $ y7 \" T4 I: O  N* n1 U( S一共耗费时间:10042
    : k: l/ T% z! b% H9 Z三:总结
    & J$ ]/ C- U5 n: H1 I% P# h" A4 x& a8 F+ p  E3 s
    本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!" z4 g1 d- w3 a
    ————————————————
    - l% L1 S) T& a3 \3 s版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    1 R- K3 n$ [4 o+ X, |+ E* `; ~) x原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325
      H' H$ S, `0 x  t! z8 H( e/ ~5 e/ b- i* O7 P6 I8 V" t
    ' U/ o- {6 C7 O8 n- \4 \8 Q# k; P
    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-6-9 20:08 , Processed in 0.374214 second(s), 57 queries .

    回顶部