QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1660|回复: 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 m/ Z( ~  E5 }0 j% c: lJava如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。5 m2 C0 S9 ]# }

    ; ?) l) P+ d5 I! n* S$ m* }一:时间控制的几种方案
    * w% e1 e8 X. c& [% W, J
    3 A6 f5 m  q! W' @% d8 t- ~1.1: 从线程方面解决* L9 Y: H. \, \: Z9 v4 k- K
    0 w2 ~# B5 v  k0 G4 l+ _
    最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。/ v: c7 v, F2 o* I, @

    + L1 s: r+ k. m3 N8 ?8 ]1.2:使用Timer8 g$ I& b( D# n% I+ J: g
    7 f5 T$ R- K4 n
    查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:9 O5 }) {7 I6 w2 k, Z0 E: q/ H7 h
    ! ~$ x8 l0 d- s5 y* ]# e
    public class TimmerTest {* F, s6 n- c( |: O0 q: v
       /**& v7 J1 }- L, {- s
         * 测试方法
    # W4 i& N: _9 B     */
    + Y+ ]. [* {) u9 a1 x# P    public void test() {1 a  X4 p3 |3 K# r# K& [
            Timer timer = new Timer();9 I! u4 Q4 V2 j+ c2 @4 [
            timer.schedule(new MyTask(), 800);# v. |$ r! V, i2 e. J4 t* q
        }
    5 d6 l( W/ o  R4 T0 R7 q* {8 f- e; P5 U3 Z2 g7 M
        public class MyTask extends TimerTask {/ L0 U  S/ n3 u

    4 w( |# y3 x+ t9 ^        /**8 ^' a# k9 n/ @
             * 运行方法2 R/ G0 Y+ U# Y1 e+ F: K
             */
    7 i' {( H# T$ n5 u' `2 y        @Override* k/ f* Z# A' @4 i
            public void run() {: A# m! u' g4 d
                System.out.println("输出");! _5 ^5 e5 T: E
            }0 ?2 V7 v  r! j' e, F- `9 A5 K
        }
    ' B& G1 j) a7 F9 A' ^" ~}. i% d! J* p( e  R
    这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
    1 Z- I) L" }+ s
    7 K4 Q8 v( p9 m' c9 z$ l, Z" e5 \1.3:redis延时
    - G% r  e& p: ^* r7 D! H  }# W% d8 M" `. U, F; {
    在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
    * T9 H. E& e& u5 y6 m& q# X- B) b$ [, U' b4 \- _
    ! J: r/ _; j! e3 D4 z% ]7 A7 C8 c

    6 r( R) M* X9 t' x$ R通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:+ F( A0 Z6 H+ _% A  T- h
    4 {# l7 K* }4 C4 r# p
    1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制% I' G/ S) m0 N- n

    3 g" ^4 N/ d% z: E6 N2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现( q% X, W9 X% e$ a" j
    ) Q+ g3 }" M" W: a& L. h
    3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
    $ x! w6 w- H8 G/ S" ^; k
    + u7 b. [9 }5 G二:redis" \/ {# V" n4 \  k& f1 _
    " s$ a1 a$ ?) Y/ K! T2 N$ G
    2.1:maven中引入redis
    5 F) l. z1 e8 R3 R$ c7 h! c' b3 g, u1 |
    引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
    ; r3 |8 L! e, \
    % w/ R! t/ J2 {& m<dependency>9 }: y) ?( u( q8 K1 L. ~/ D% C
      <groupId>org.springframework.boot</groupId>0 x/ F, ?7 ?6 K/ m9 P" F* e
        <artifactId>spring-boot-starter-data-redis</artifactId>8 W5 o1 X* l( D8 v5 {9 K
          <exclusions>1 R) y4 H! S1 @- }7 `; `
            <exclusion>* ?: o+ o; U# t& L' k5 x  F$ S
              <groupId>io.lettuce</groupId>9 G# c3 {6 m8 o( U
              <artifactId>lettuce-core</artifactId>+ j# l8 L3 F9 z3 Z
            </exclusion>- B/ O9 \1 u/ ]( g2 x
          </exclusions>* G3 G  K4 J  c* S" J' u
    </dependency>
    ! A2 k6 T, O) l# [0 P<dependency>0 y. f7 j0 M) `0 R5 j- M8 [# Z
      <groupId>redis.clients</groupId>
    / y& m9 ]' i" z1 z  <artifactId>jedis</artifactId>! [# ^' l: g) X5 y. f' _) N7 l
    </dependency>
    ! a3 A1 l8 [/ }6 {+ v2.2: 在springboot中配置redis
    1 Q$ p! V; B6 A' q% |/ |! q' B: x+ {
    import org.springframework.beans.factory.annotation.Autowired;1 D8 Y/ e  G9 D) s8 Y
    import org.springframework.context.annotation.Bean;
    9 q% t7 l, d0 H4 b: I8 eimport org.springframework.context.annotation.Configuration;
    * z* w6 I3 }9 X9 Z- m: Mimport org.springframework.data.redis.core.RedisTemplate;
    # _+ W+ Y0 g+ m3 @, kimport org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;: X% W" v1 i1 ^( J/ r- }! D3 Y( U  B
    import org.springframework.data.redis.serializer.StringRedisSerializer;. L+ S2 T" M8 H

    % l9 f/ J. c1 {7 g) B( N! A; a6 e8 n@Configuration
    0 p) E! b& }/ A. Mpublic class RedisConfig {
    7 Y) T0 S- m# ]* F% ^' l0 j. M* S0 m, v. ]0 f# Y$ f
        @Autowired1 q) D9 L9 k+ s! h: y
        private RedisTemplate redisTemplate;
    / ], Q8 x5 O; x6 W
    5 r  x* p$ y% ?% J( H    /**4 u3 e8 z7 s/ ~9 D* ]
         * redisTemplate实例化8 x0 v- P6 J* @: C# J/ k6 w
         *6 n" F8 ]& {* h7 _
         * @return% q% g) o) W8 X" j" H
         */
    ; ^1 O6 ~9 p: C    @Bean: e( t  s8 o. k: T3 t
        public RedisTemplate redisTemplateInit() {
    & Q# |/ N- S8 |        //设置序列化Key的实例化对象/ X. i) C: _: d6 c  a. Q& K4 |. U
            redisTemplate.setKeySerializer(new StringRedisSerializer());
    & J! x. ]1 a, J2 L3 T4 \        //设置序列化Value的实例化对象
    ! F+ p: v- [9 ?! W5 Q! I        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());* C# Q( a: V1 o9 s" {- Y6 @
            return redisTemplate;2 B0 b3 l" v( t  ^
        }
    1 P0 t2 i6 i0 V% n2 R0 c
    4 r# C7 ~* J. C1 w}  U! s" Y3 f' O0 Y* ~
    2.2:redisTemplate模板工具类7 [8 R- x% N0 n& I1 @

    - r# G0 ~8 @; q4 }! N" q@Component
    : D% V5 L+ t+ D8 a  S4 M+ v9 Rpublic class RedisManager {
    $ G- l( Z: v. S3 F" W9 {
    5 q/ S7 E( O1 W. p. w' f) q    private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);) X2 U3 U) [+ V- O$ t4 J
    # N- V4 h7 j% S0 E* n
        @Autowired
    5 s- s8 P" b. {8 I    private RedisTemplate redisTemplate;7 E; s5 a! e, p' h1 U) }. b
    ' l0 n" w7 S* t# `1 b
        /**% l: g/ j) Y1 e3 F' L! }3 t" W3 Y
         * 设置对象' S; G: Q+ L" K3 ~, Q
         *
    4 D( ^! ~8 A$ _( q     * @param key key) Y3 W4 J- C4 t( T0 w2 `- L1 o" B
         * @param value value值# k0 C; U" Z: Y
         * @param <T> 返回值泛型
    * k7 Y# B; D: X+ L" K7 `     * @return 正确的值:<T> 错误的值:null
    + h9 r$ Y0 A  o( R     */
    9 ~' @$ ^/ D& M    @SuppressWarnings("unchecked")1 w0 J6 c* g4 T* r, Q4 f
        public <T> ValueOperations<String, T> setObject(final String key, final T value) {+ S- A5 d0 [! z! m/ F
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();, T1 ?* M  H3 u6 \: f9 Q# `
            operation.set(key, value);
    : ?. d. W7 }9 K6 C3 ^        return operation;
    8 N8 @6 Q, b( e* Y" q* ~    }
    % Y% h, a* A9 @9 s9 O5 C  P* Q2 q/ X. D- t2 X( S+ Y- G5 u# B
        /**7 E1 H* u+ o7 L# H! s
         * 设置对象及失效时间 (单位:秒); p$ A# ?# U$ H' x+ F" l
         *
    # V! r& Y4 u+ y0 r. J& a& w; g     * @param key key; {5 h; |! J$ ?- e* Q4 o% `( w9 h
         * @param value value值
    ' s. w: ]0 o6 ~- _& p     * @param <T> 返回值泛型
    ' Y) @, C. F5 ~! }     * @param time 秒值
    & A& A, y+ A% J/ Q     * @return 正确的值:<T> 错误的值:null
      ~+ x& w4 g+ ]7 R     */
    3 T8 z) {4 e' W* O9 b) f- H& {+ c3 A    @SuppressWarnings("unchecked")
    3 ?. u& r) B9 ]# ]    public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
    # f6 g& z: O7 b7 W0 {6 ~- z4 m        final ValueOperations<String, T> operation = redisTemplate.opsForValue();7 K" Q  f# o$ n$ g* m+ w( y7 M
            operation.set(key, value, time, TimeUnit.SECONDS);% F+ S2 \$ ]  i
            return operation;8 i; `! H5 H& ]' g+ Y1 y$ u2 u
        }
    ( A( U3 r7 ]" m: L% k
    6 W6 m0 G! q" e8 w0 f" O& ^3 b; M" V7 I( y  ?6 [, Y
        /**
    7 |( e- t( W' A2 [( @" c/ W     * 设置对象及失效时间(单位:毫秒)- ^4 d3 Y7 w5 Y6 D) M; M
         *
    5 A. t' t' j/ ~1 q, J/ w# z# n4 v% J     * @param key key$ y  e9 m, x* p: ^% }  Y; w. J  c
         * @param value value值
    3 E2 T- x% i% q9 k2 K     * @param <T> 返回值泛型" }7 \+ |& j9 @. B. E& H
         * @param time 秒值
    6 U8 c4 v3 i9 `5 U     * @return 正确的值:<T> 错误的值:null  W6 |0 `3 E! l/ m: n( Q
         */, _: O% p7 ^" x% k- o  g, n) o' N
        @SuppressWarnings("unchecked")- [; u0 Z- A( Y( Z( L9 z! b' s- K
        public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {* Q" a' |1 U8 y2 \7 P9 a
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    + \, a4 q/ S, ?4 u& Q! \9 y4 }        operation.set(key, value, time, TimeUnit.MILLISECONDS);
    4 S, e0 U/ m& d        return operation;
    : \- m1 E( v/ T! k6 D; w3 j( J    }( l  b8 M+ K; ^( N6 T' O# W. ]

    3 p/ X% k, m4 O    /**9 y# t; }. I0 S
         * 获取对象
    2 X0 Z  X# F4 Y3 ]8 q5 u5 T     *+ M0 g, E3 Z) T+ ~: R
         * @param key 键6 e4 E* Q' n( m0 H8 z# l$ |: y9 _
         * @return 正确的值:Object值对象<br>
    & Z' ]. _' d: |     * 错误的值:null" x" s' `3 Y4 o! B$ j
         */  w  G; @& D: b  y9 q' P
        @SuppressWarnings("unchecked")" B7 k9 f2 Q6 G, i
        public Object getObject(final String key) {8 o( W: Y2 R( g3 N
            final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();3 q  t/ I8 E; R$ }
            if (valueOperations == null || !redisTemplate.hasKey(key)) {1 z6 u# U: c4 e. h/ J# ?: a
                return null;6 \  P' X9 P' t4 i4 T2 ]6 |/ M
            }
    / z7 O' d! ]5 L6 i8 j        final Object object = valueOperations.get(key);; L  [0 A( r1 h- C1 u$ J; n
            return object;
      D2 U5 F5 ]  q! U% h' A: H' Z( R    }7 D8 E- G5 [; L* `9 E/ A: @0 m/ u' c
    / q2 Q) V. p/ j' j+ f, R9 J
        /**
    ) N# ~9 g2 D  y' A. u6 `$ I     * 从缓存中获取string值- k5 b8 s6 X/ m
         *, d  {, r- F2 B
         * @param key$ N, L( M; C+ o* A
         * @return*/
    % G9 v' k; ^- a! O    @SuppressWarnings("unchecked")
    * G# c7 A$ r/ v5 W& ]    public String getString(final String key) {# g( O2 `) _+ R; V+ E
            String value = "";
    2 C- C+ f3 Y! \4 D7 m        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    7 W; E) c  i! ]( P$ M" k5 S        if (valueOperations != null && redisTemplate.hasKey(key)) {
    ) }- [2 ^# U. t+ J# e9 f" X            final Object object = valueOperations.get(key);9 Y- }+ W' V" R7 e
                if (null != object) {, `2 K8 E  {; C! M
                    LOGGER.info("--getString--object not empty");
    2 T' U; z' x. ?! n2 L. t& l                value = object.toString();
    & h. |# d; A) f' e+ w- ]            } else {3 V( s6 C* H' J8 R
                    LOGGER.info("--getString--object empty");
    " X% k6 i& @8 l, N1 {8 M            }
    ! R, i! C3 R) B% [% q; T9 F$ X        }0 A$ h1 i3 y% b7 z2 W+ D: t
            return value;7 j4 }/ l. S! Q, E9 k$ v! I/ [4 P/ y
        }. S" F! [! t# A4 y$ ]  C
    2.2:在redis中实现时间控制
    0 u# H- k5 Q1 M, l' O$ J( _9 T& b% }8 ?6 X# A. W
    2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。! K" o' s+ B; d2 e' C

    ) N2 ~) z& u4 t9 D/ N9 }4 d( v! dimport com.youjia.orders.redis.RedisManager;
    2 S6 C- V$ J- Q% Y6 ?4 himport org.junit.Test;* N  x, C: g7 S1 d2 x: Z+ p" ^
    import org.springframework.beans.factory.annotation.Autowired;9 C. |! Y$ W% I+ R: m8 g* N) C

    9 u) E* E* }5 \: simport java.util.Objects;& O; O3 b0 S  ~' b# X

    ; b" L" q+ e1 j1 h* o( Y/**
    $ [7 V7 e) q% V$ l8 j. b8 g1 e * @Auther: Yrion0 M% x6 f7 m7 f2 |; J7 K# \, }( i
    * @Date: 2019-01-11 23:36
    ' L$ I4 {0 ~+ E: h) P */
    - V! ^; f4 r% x6 r; Z4 e" {/ o. r* f+ p) P' h
    public class RedisTest extends OrderProviderApplicationTests {
    " C9 L; n  U) j: ^- o( G: [& P* \& ~/ N. @- K0 S
        @Autowired
    / [/ B1 ^5 |. H1 V  @: c    private RedisManager redisManager;
    5 g" _! Q& @% h6 D( d( C
    * ~; U: U- i. j" r/ `2 v& L    @Test
    5 i) K" S* l* e/ f5 A$ x: f  J/ k    public void test() {  X; ~- ?' H3 B/ g' w: z4 q/ ~5 m
            controlTime("10000001", 10L);
    8 [: r( X5 ]4 i( {! s6 O2 k    }" P, e% q1 i# V/ x
    7 Y+ E) B5 L0 q
        public void controlTime(String requestId, Long timeOut) {
    " F8 F! f- ]" F: s+ ^2 J: X) B5 Q; V5 T& Q( o/ g
            if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
    6 s) w+ w' a" C" r1 X2 M, l/ N* v, [            return;
    0 u/ k6 \6 f8 b! h! k$ h% h: _- u        }8 `* K  G2 ?9 F8 V) A
            //something code$ i3 B) K9 v9 Y2 ~1 r: y
            final String value = "value";
    : O% {( ]% J; o% z) o! F        redisManager.setObject(requestId, value, timeOut);( L# T2 Q% v- h& e) s
            final long startTime = System.currentTimeMillis();- e) k1 D' {7 Z; M) h& @5 s8 q
            System.out.println("开始控制时间");
    5 {) f1 F9 \) h9 M  y# x        //start) l0 a/ f  @( O- U. x
            for (; ; ) {2 Q4 g5 B$ B! N, T% R
                if (Objects.isNull(redisManager.getObject(requestId))) {- l$ B( I( s; Z  }$ v) E- }
                    break;
    0 Q% E7 W# R( K+ n* E/ d2 o/ }            }
    , R! z: h. O/ f3 g, q' }' e        }
    % @% z8 D. E+ \+ k6 L        final long endTime = System.currentTimeMillis();
    6 d1 c  Q6 o- m. V
    + h$ e# c: d0 B7 D, }, G- Q4 m9 m        final long useTime = endTime - startTime;
    6 w1 z# K7 I: }7 S- V: E+ t
    6 w1 D7 z2 S7 U- X/ c- V        System.out.println("一共耗费时间:" + useTime);7 `1 E5 y0 E# U- [/ s( X3 o8 w
        }
    % D$ g2 N8 x9 p4 c: q7 o# j' c" Y}
    . _+ G  @5 O$ OoutPut:
    : \3 c7 h/ U4 T$ A# {
    $ l/ M, R5 |& K( j& v' O1 p开始控制时间
    ) w3 R* a) X3 a" Q$ k  j% K一共耗费时间:10042
    * s4 j4 p  b) I- U+ j- j三:总结6 a! K3 ]! q7 _
    - s) }" l7 h3 B+ d& y  v) i# \
    本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!+ l! \+ X( S5 b& D- ]6 c4 a: k
    ————————————————
      E. E( {0 X, B# [# S% |& W8 V版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    7 G% t% D3 c: W0 u原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/1058933255 m# Q3 I( @- _! |9 {

    + p/ {+ s$ ^0 u9 c3 J
      P2 e$ z# A; x( Q
    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-9-8 17:33 , Processed in 0.467711 second(s), 56 queries .

    回顶部