QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1640|回复: 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
    ' D3 U7 G. i# W+ X1 v* Q8 b
    Java如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。( Z. L+ e. Q/ e/ d8 w4 K( ?/ K+ u

    8 z5 Z$ b+ w1 a4 J8 n一:时间控制的几种方案! R5 S8 V. l5 R& ?5 s, Y' r
    4 @- D! @7 k# n
    1.1: 从线程方面解决
    : T$ Q5 V2 y! P; E
    . v% ?% z8 p# h2 G  w最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
    9 N7 M9 h4 Z) o! I% e, j4 J0 m1 S% e/ Y1 R) p
    1.2:使用Timer0 f( ]8 x  |7 c0 F( o

    4 [* o* |$ ^" D# H7 o查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:4 t. k1 L$ y2 x. D7 t

    3 J, d2 ~6 T3 ?0 K$ upublic class TimmerTest {6 k8 ]/ x$ [7 B" h
       /**
    ! d6 T; S4 m# I% ^1 f$ [# Z2 i     * 测试方法
    + V% I: A. K+ h9 G/ B     */
    ) p5 Q6 {& I1 r# R. ~) @1 Q    public void test() {
    ) \- q8 w# j* D8 R        Timer timer = new Timer();% X$ d& R3 b! N1 s' Z. |
            timer.schedule(new MyTask(), 800);
    2 [- G. J/ L6 C; `+ Z0 u' H1 }+ G    }
    ' W& ~! R: Z* q$ t1 G. |4 \6 n/ `+ C
        public class MyTask extends TimerTask {
      z" R4 N9 J3 @5 a* n& b$ X( }5 b- r3 S4 ]6 P
            /**0 h! B) J; f  A
             * 运行方法$ n/ C3 ?9 Z; d% D6 w. @
             */7 n8 e, m: M$ [
            @Override* y. w1 |- y2 s- v$ A  u# G
            public void run() {
    / M0 Y" r" `4 i6 B$ e            System.out.println("输出");
    * M% B" _! a3 F% r        }
    0 [- m6 A/ S) @3 ?) M' \    }$ Q& X4 g) s0 v( B
    }
    0 N& h8 ~" I) p  t( m- B! Q$ u这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。1 }) t8 d; @, }
    + @4 r% o- Q( i$ I# X' c* _
    1.3:redis延时
    7 G5 J( Z/ h5 I6 F0 {5 _) U( d5 v% c( N3 O2 d5 [8 m
    在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
    8 R' }* M% m/ ^- H% K8 N0 I1 P$ {0 Z0 s" @" H

    " \2 {7 n8 {6 m- p# W
    . L9 G  T/ Z% y" |# m通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:
    & C3 E) s0 Q, h' c& v8 e* B2 S! t" z/ g1 L6 r
    1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制( Z9 J8 R  e2 J+ }( N6 d  e) O1 k) }

    0 A3 S, |; i) @4 s2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现3 K( K' j& h. c: n! D

    ( x4 p) u! d- O8 n3:简单,真正的代码实现起来只有很少,下面会给出代码示范。' g. y3 w1 }6 q# a5 x4 \
    $ a& X5 N# f1 k: Q
    二:redis
    , q9 j( L! l& M3 ^! b) l# y& |- b" v6 f8 U3 r% S& g$ O$ W
    2.1:maven中引入redis: f7 L% G- d( d  T" q
    ' B, h9 E7 Z! c& |' d& w" s6 a( P
    引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。( ?2 p" S0 Q, _/ W1 k7 m

    6 i: u! c6 i: O2 y% H<dependency>! e1 l( T. M. y2 Y7 D
      <groupId>org.springframework.boot</groupId>
    + y3 r- J7 y' r3 m$ Q8 |    <artifactId>spring-boot-starter-data-redis</artifactId>
    & f% L& Z7 w7 t9 |, V      <exclusions>6 l: \! p, R+ ^
            <exclusion>% X# ^- V) d& S- H! W, V( R2 u
              <groupId>io.lettuce</groupId>
    / Y; C1 C/ v9 s( s          <artifactId>lettuce-core</artifactId>
    , J/ G9 W) h- e; z: s6 J        </exclusion>
    9 k" D6 m  x# E4 F9 T: W7 n' S0 T      </exclusions>% B5 w, l  ~) H# b7 o6 ~6 O: M! g
    </dependency>3 A* R6 g) m( F& z
    <dependency>
    , ^/ D4 z+ O4 X! H1 L2 |  <groupId>redis.clients</groupId>
    6 V; t, m( m( C# ~0 [  <artifactId>jedis</artifactId>
    3 z) ?8 A5 e* R% U</dependency>, N$ {. ]3 x6 C
    2.2: 在springboot中配置redis% X4 Z) B' M( [$ I8 D1 P
    . x, n0 H9 r5 T  C
    import org.springframework.beans.factory.annotation.Autowired;
    4 P6 T7 v9 \# X/ bimport org.springframework.context.annotation.Bean;" P$ n5 k1 C  O
    import org.springframework.context.annotation.Configuration;5 t$ o& U* X% g4 N" b* n* ?' c
    import org.springframework.data.redis.core.RedisTemplate;5 G6 ^5 q) u; B' [( U- Y
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;) q* b& M, k5 F
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    " J& O: |# A9 |* `
    . ~1 [. {# b: l4 C/ s@Configuration# F0 t; {8 w  R# f6 G
    public class RedisConfig {6 J, g% O5 y) N1 K

    2 P; t5 j2 C0 q5 {% f: t$ ^8 ~    @Autowired+ A- [0 L1 i/ d( X0 q4 c3 C
        private RedisTemplate redisTemplate;0 d6 e! W+ ^* u6 U; h8 v6 V

    / J" }' f+ h! r$ A- J6 s    /**
    * y. p; w: ^' a# H5 O     * redisTemplate实例化7 `& U' i" X3 ?% S
         *
    # v! _. N5 ^; y0 h' |6 @     * @return4 {" S4 Q7 H; E+ |9 N, X4 D
         */" u3 q; w' J' c: }
        @Bean7 O2 D+ E3 h/ T( y$ b5 x
        public RedisTemplate redisTemplateInit() {
    0 M" V) L% B7 J) m7 H% a% m& P        //设置序列化Key的实例化对象
    4 y  Y9 a( C% ^* j7 x8 D& o& w        redisTemplate.setKeySerializer(new StringRedisSerializer());2 I9 z9 g' \; x& x
            //设置序列化Value的实例化对象
    0 x5 }) E$ a- r& C2 y0 n/ R% x        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    " ?% m1 N$ F; L, }9 ]" J9 A        return redisTemplate;
    4 b7 ?& M# a0 E5 A6 N% [8 |    }/ B, _( M' n3 l, _5 N

    . I7 g8 B" L% h2 N# q( J}
    ( J* I9 b) \! n: h* F7 r6 A2.2:redisTemplate模板工具类9 j& t) h, l" S2 u
    2 t5 C" p. t* h) Z
    @Component4 P4 N( a9 L2 o' D" ~
    public class RedisManager {
    , G- j* U0 a+ b3 i- Z" Y7 Y0 o3 j- ]. G' P
        private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);
    ; D! J7 q0 ~2 g0 V" i* C3 M
    . c8 J, h7 S7 A8 |: z, U4 Q4 d    @Autowired5 e' P9 _7 i9 Q8 J( E
        private RedisTemplate redisTemplate;+ q* b% Y" K5 {* N! T; }
    2 y, f+ ^9 c' C+ D+ B0 m' L
        /**
      J9 @' r5 i* y, p. u! z     * 设置对象
    4 ^+ M( W$ F* b) P     *- z2 u+ }$ u' M# P4 v; P  v
         * @param key key/ J/ U' Q7 x1 Z% z/ G* ]; D/ J
         * @param value value值4 l/ B+ H: A* x- F6 n& q- P  n
         * @param <T> 返回值泛型# H7 q+ `( n: l  |$ n
         * @return 正确的值:<T> 错误的值:null
    1 n, a- d/ x7 \5 ^; P3 V5 I     */9 W1 \# F9 L/ d! a
        @SuppressWarnings("unchecked"). s( u/ Y' d- D' f! z# h: n8 |0 i
        public <T> ValueOperations<String, T> setObject(final String key, final T value) {  z# d& N$ n1 w( K' H- F
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    + w1 f2 E, _& g0 Q        operation.set(key, value);; r8 y9 O! R) g# F" F* t# {
            return operation;
    + P: I6 T2 q# w! b    }
    ' U( z& `7 y# h5 B7 S0 U' A" [; m, n; V( G' w; o/ _( T) e  H9 S2 x
        /*** Z9 J, g# X7 @8 Q8 @* K
         * 设置对象及失效时间 (单位:秒)3 V" N- t- V/ W- o
         *
      @  c' [9 Z4 ]: X8 y  J     * @param key key. ^  j. j. ^- k2 S
         * @param value value值
    ' r  n1 s% ?1 |! k$ O' y* Y% [     * @param <T> 返回值泛型
    ( |& f/ h( j0 I. f     * @param time 秒值
    " e* P* B* u* t7 Q4 q8 |* C9 p     * @return 正确的值:<T> 错误的值:null
    # R$ N) ~7 x2 S7 s  e     */6 b; T4 v, Q4 P! O
        @SuppressWarnings("unchecked")
    6 A8 o+ z5 K! j8 I    public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
    : s2 G% b; y/ q! f4 e        final ValueOperations<String, T> operation = redisTemplate.opsForValue();
      O9 D( r8 X2 c" X1 I0 g% d  ^, |# ~0 l        operation.set(key, value, time, TimeUnit.SECONDS);
    5 C% @! K/ E+ e- R' a* I; q        return operation;3 `0 |6 l- @; q/ E
        }
    % L3 J2 T, x- y- }3 f2 I" i% E" o4 \9 A4 X* F8 t  u

    # r9 Y. k  `, e3 Z9 [- _    /**; L' a6 ~+ N( j4 O0 w' j
         * 设置对象及失效时间(单位:毫秒). a9 _% n6 ~; t) z
         *
    * e" l1 Q" m, p9 I+ z8 m4 ^     * @param key key
      o* r9 N% @8 M2 n. I4 _     * @param value value值
    % d& f8 Q  J) N     * @param <T> 返回值泛型: n' f6 I  v1 B0 k& A, N% @0 G
         * @param time 秒值$ A  c; q" W; g. ^5 F
         * @return 正确的值:<T> 错误的值:null
    8 H: @# D' X- }0 p  T7 M4 d     */
    ) m* R0 O- W1 N    @SuppressWarnings("unchecked")
    % Z4 j8 v# m+ ]$ Q1 Z    public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
    * e  z; T3 K6 v, x$ W8 U        final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    / d; z" c( `2 H9 D) m5 v0 W        operation.set(key, value, time, TimeUnit.MILLISECONDS);$ R1 _3 Q! j2 d( n* p* b
            return operation;
    , [# n- }# G9 E8 S  K* I    }
    ) X5 ]* u; Q- V2 i  y8 ?; d$ h* _% A3 _
        /**
    2 h5 V% {; k4 J! `) m! M) P     * 获取对象7 L7 r0 D( k- y0 V# `6 A% j- |5 F+ }" N
         *0 [! t, P0 e  K. f
         * @param key 键
    - o; R# x' K' J2 x     * @return 正确的值:Object值对象<br>7 n) c6 |- ]" y0 I5 O
         * 错误的值:null: N; j& u( T3 L/ Y
         */% ?! A% W5 I- F/ W5 A) q& S
        @SuppressWarnings("unchecked")4 M( d4 {8 ?+ o8 n8 z" d7 H
        public Object getObject(final String key) {* A- i9 u' y5 n2 d5 X( ?
            final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    1 c  q/ L/ N! J. |) f        if (valueOperations == null || !redisTemplate.hasKey(key)) {8 P7 I8 H: G' y; F0 q
                return null;7 }; V2 V, V2 `" T
            }
    2 E; G; r: K* Y9 l        final Object object = valueOperations.get(key);$ S- o" f1 Z/ e! {# F! K8 m  ^* @
            return object;
    8 V, E& o0 ], t    }$ H4 j: Y. u) F4 w: p/ [$ S# E" X" M- Y
    4 y. x& y/ a+ @# N) ^' n+ c3 \
        /**9 a' n( F6 n+ P; ^' d
         * 从缓存中获取string值0 {) t) D5 ?2 \
         *
    0 L- {8 D! F( G1 {  Y     * @param key
      ]+ l# }$ |9 P& E' S: L6 x     * @return*/3 k2 s; ]" ~9 b' Y. X& ^( W
        @SuppressWarnings("unchecked")2 q& L3 B  C( T+ i6 Y9 t
        public String getString(final String key) {- b4 o* y# q& d# p, ^6 @
            String value = "";7 Z$ K& D- q0 Y+ D% z$ i1 D
            final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();+ [+ n% t* ~$ b$ R7 O$ D% U
            if (valueOperations != null && redisTemplate.hasKey(key)) {
    3 U, q# i0 d4 x7 F            final Object object = valueOperations.get(key);& N$ ~, S: ]9 d( F; v/ `
                if (null != object) {- F+ n8 f% H3 w' u$ A
                    LOGGER.info("--getString--object not empty");5 u: I. K/ X0 s1 O2 N5 _
                    value = object.toString();
    ' z, S5 v1 @3 \9 h" @3 N7 l5 t* n            } else {; `2 a( T! m- Q4 d: l% k* M4 F6 }  `
                    LOGGER.info("--getString--object empty");
    ! j( D$ M: e2 J2 C- J            }
    * P% \# G7 I5 j" r        }
    ; Y  s( W/ v* b6 v! r% T, P        return value;4 d/ w& N( I) M* f. Z. I# \. X
        }: M' q  b1 @5 g$ H9 d" Y8 R7 h5 z8 n
    2.2:在redis中实现时间控制8 ^$ B# b/ B) M: j7 u( j) W
    ) m7 M/ ^' l" c9 p
    2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。5 Y% ~% y* F# y) q- Y/ S0 ?: X
    & d" W5 _+ N9 Y5 y" {) B
    import com.youjia.orders.redis.RedisManager;; j& C; P: n7 \/ A$ p
    import org.junit.Test;- f& [6 \0 v9 J9 B
    import org.springframework.beans.factory.annotation.Autowired;
    " d+ R5 G& b) d7 ]7 k* N& l8 F3 L4 O' Q: q0 I9 h. Y; I* G
    import java.util.Objects;5 h. @8 V) H# X: c

    4 u+ E& H  K  r* G0 c/**& g+ T2 _; }* r
    * @Auther: Yrion
    & R1 c) k, F" E; W/ p, h& Z5 p * @Date: 2019-01-11 23:360 s8 _$ g4 B" {
    */1 d& A0 v: a1 n* S4 G7 U1 {/ I
    : N2 D1 B/ `! P0 ~( U1 m7 i
    public class RedisTest extends OrderProviderApplicationTests {6 g7 x& {$ y5 m- _
    ( @5 m: Q, ^" C" P5 o5 L; _& c
        @Autowired- r$ U7 F( v, M+ S, _, U6 W# P0 F% f
        private RedisManager redisManager;
    * p9 B( X) N; G& y8 v/ B  e$ Y( ?# Y' _4 l. u% C4 \( E  D6 ]) _
        @Test
    0 Y- P) V; V+ k2 {! Q: t" ?    public void test() {
    - T9 N4 U0 A! k/ y( q        controlTime("10000001", 10L);
    , ^& E+ ~7 [! L4 G9 [5 a4 m    }
    1 U" H. F) q6 Y3 N1 g# r* ]1 x2 k3 P% h6 C/ U
        public void controlTime(String requestId, Long timeOut) {# \, O# X7 a. h6 ^8 T2 t
    3 a2 {. P2 K% t, d, U& d+ w
            if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {3 n* {  G% n1 z+ U
                return;
    2 T) Q1 R) D$ N+ F: E, v) b  Y        }
    3 h/ |6 l5 U  k( e4 p        //something code' F/ r) U2 B3 m- y
            final String value = "value";
    ( w1 G0 J  Z2 u7 ?1 @: Q        redisManager.setObject(requestId, value, timeOut);3 Z7 u4 [/ M' C6 J
            final long startTime = System.currentTimeMillis();. u" q3 U/ Y4 }9 S9 P
            System.out.println("开始控制时间");# D; u. u4 S) z
            //start: k. v* G% U( A
            for (; ; ) {2 A" C$ z: x- {' Z; A+ Z
                if (Objects.isNull(redisManager.getObject(requestId))) {8 t* D' Z" q2 I
                    break;1 x% \- F0 m" {4 R+ `* b
                }
    9 B) b' J5 A! G) N8 ]        }
    7 S( @7 V8 l9 ~        final long endTime = System.currentTimeMillis();/ A# x1 G  Z$ w1 h% C
    5 B$ \. V! D; g! n; ]
            final long useTime = endTime - startTime;7 e  C: J) p; V  ~. {7 w- x
    9 j: g  n; k) N
            System.out.println("一共耗费时间:" + useTime);
    2 {; K( ]# x. f3 E# ?+ Y5 m    }
    & J" ]$ M" S8 T$ g/ e% n% h}
    ) C( V/ N, ~* S& ioutPut:  ]% B# g: V# S- a2 f
    2 L9 o6 {, t' q( Z7 u* M- w
    开始控制时间& X- j% @" T. s
    一共耗费时间:10042
    & W; p; C$ p$ d" Y# c8 t7 I三:总结! A! P& t  G! {# a7 o5 n) s6 T  l
    5 i; I" b+ i/ j
    本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!
    ! b! h) d5 l9 s( z0 Z& r# f$ U————————————————: U0 ?/ N& p0 V  ?& ^' K" M7 T$ ~
    版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    # F( ^# B( c9 |原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325% n& q- ]8 B0 W0 R, y

    ) v$ s$ V# `4 U( r- {& v: u
    , p$ [1 e- t7 R' Y
    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-7-25 06:21 , Processed in 0.424854 second(s), 57 queries .

    回顶部