QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1617|回复: 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
    7 h& ~# S' e1 J' r' z) B$ u, @* U* y
    Java如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
    ' ^4 e8 d4 p; T3 l, ?6 g' E1 l3 ^! [5 N8 r
    一:时间控制的几种方案
    & d; B  W+ x; L- |. `
    . s* x4 d6 P0 ?! o& N  L& A+ J1.1: 从线程方面解决
    6 e7 T$ b4 R+ c; X( D
    * L1 J: d# W. z4 \最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。& |4 n: F) _* P
    1 h* I) Z/ _5 {/ g: E+ I3 t
    1.2:使用Timer
    $ p! Z5 o2 Q, s6 \6 D! B. {7 W# H; i
    ; Z- {+ F" R* x# c; o* c查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:% B" Q4 `7 G% A1 b! f
    : U" C7 ^7 \9 S6 T" b1 I
    public class TimmerTest {8 c5 M/ H2 N5 g5 H  R
       /**
    % \( p& B" w! T' R7 b     * 测试方法
    ' F- b. a7 s9 \& W" f     */
    7 T6 C5 M# D' _! D# u    public void test() {
    0 m4 Q) w/ C, G: b( r0 U        Timer timer = new Timer();
    - N; H, y- y$ w! s, K. M! m* L        timer.schedule(new MyTask(), 800);
    , n% A, w7 x. P; L7 L& t9 m    }- P9 N4 Y9 `* i" y% ~" h
    ! N, G% p5 Q. V* r$ P
        public class MyTask extends TimerTask {5 g7 H% m" s8 f+ A3 n; Y
    9 U% |/ I& Q# n& k9 Z3 ~
            /**5 G# h; }# ?! L8 {8 [
             * 运行方法5 {5 G& ]' n2 h5 ^" b
             */! u  m( a7 O6 U1 d8 M
            @Override
      y3 H9 c: F0 N9 O. j( p% }5 t        public void run() {9 }3 i9 |1 b3 `# u& n* p/ D
                System.out.println("输出");! q+ Y, P9 \9 Q$ ~0 d
            }! H1 K: q) A& x
        }$ n9 h, S# q* K" G! F
    }
    + A4 n& ]; U2 _8 z- {, W这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。& c& m/ y: `7 U# _* u

    ; m+ S1 u0 A, n6 o7 a1.3:redis延时& k' W3 n) A4 @; d0 E
    8 Q- K- B: k2 u' F( U8 u
    在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:0 Z2 Z( Y: p/ ~; G. r! a

    : r$ h$ v# q5 {4 a7 k8 }; T( \# e+ a) h9 m) Y; M( j

    7 a# d* ~% K( U通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:# w* Z2 E, J/ z& n5 V" u7 I- T4 d1 t: e
    6 [. u2 Q' S( h' Y& s
    1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
    8 |' ]3 g% `1 ?3 `3 i6 ]% t( D
    # n* i' d. G5 b+ X: q3 c2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
      e6 U* F2 ~. L2 B( a# P/ ?* q* N% I; Z! \! X  s5 G
    3:简单,真正的代码实现起来只有很少,下面会给出代码示范。7 F, A* a% n7 R% A0 l) M/ d
    & X5 t6 z- u9 G0 {1 ~
    二:redis/ x0 n9 r1 ^  |

    / a5 i! g: w0 @; }8 ]- ^: T- Q2.1:maven中引入redis# F, o0 d/ _( b# |. L3 \6 p

    $ i5 Y$ K' m4 @( P% y- u0 I引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。- b; }" Z% M3 S; l5 `2 U* z
    : g' [9 J; @* x2 e
    <dependency>
    - h; u. T+ t5 Q: b1 f  <groupId>org.springframework.boot</groupId>
    # i& j  G: G+ f& x! N4 Y    <artifactId>spring-boot-starter-data-redis</artifactId>
    9 p- |' l  l0 ^- ?2 c% D      <exclusions>
    - d. |+ R  x  a: w        <exclusion>
    , r$ l2 ~. s+ X/ N          <groupId>io.lettuce</groupId>
    0 {: Y) R, m( ?7 H. K6 s8 V          <artifactId>lettuce-core</artifactId>
    5 x. I" b/ R3 S" S* B        </exclusion>  ~" f( i; v7 j2 l8 R$ Y8 c
          </exclusions>% k& L5 V, C. y
    </dependency>
    4 e* K2 Q, ^& x6 x7 p' a/ w<dependency>$ O2 T! A% A# r' k
      <groupId>redis.clients</groupId>' i  o5 L( I/ z+ A# |( e8 F
      <artifactId>jedis</artifactId>4 \0 ^0 i4 @& N0 ?* d3 q/ `" ^
    </dependency>/ S, m0 x, L. B2 J" d* b3 K
    2.2: 在springboot中配置redis
    * s2 @5 D. M2 e) b4 h9 n9 @* @% v  U: @* R2 H
    import org.springframework.beans.factory.annotation.Autowired;$ R+ Y! G$ |* W. P- W$ [1 S8 e$ e0 t
    import org.springframework.context.annotation.Bean;
    7 M) \* n8 k  V' i; S) Eimport org.springframework.context.annotation.Configuration;2 \$ W) X* Q: ?% c& ^
    import org.springframework.data.redis.core.RedisTemplate;' ]$ j' I) J. f4 Q8 w
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    8 K, V! S& i$ b6 @7 Limport org.springframework.data.redis.serializer.StringRedisSerializer;
    + t* o1 t* P; G6 S
    / d$ N' T- ~4 h& T3 n@Configuration& W4 r4 X: f. I2 Z- r8 }
    public class RedisConfig {* P9 ^8 d! E  J  }8 K

    9 [! X; \( }1 _6 g- X    @Autowired
    " T2 `) _$ K  S& P( Y* g7 \    private RedisTemplate redisTemplate;/ P0 d" {1 m) S7 I9 F% Z4 _! r

    3 ~6 U7 O# J, y5 `# k    /**
    , d2 x- B- [4 X     * redisTemplate实例化. e8 v' a+ D8 e0 Q, n. I
         *
    , D8 [+ P6 h4 {" I     * @return  ?, ]( b, P" Y4 }7 q+ H
         */
    3 ~# r, Z( j% y1 i' Z. Y- M    @Bean
    ; N5 Y% j& X  U7 C3 d6 D    public RedisTemplate redisTemplateInit() {
      N$ y( U9 m6 o& A. Q+ Q; ~* [        //设置序列化Key的实例化对象
    0 B0 I) A; B' k# [* f        redisTemplate.setKeySerializer(new StringRedisSerializer());# A( w. ~4 f+ E
            //设置序列化Value的实例化对象
    ) l6 k& u: f& o/ l6 Y        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    : |8 g! W0 T1 h# A        return redisTemplate;+ k% B* s$ e  l( \. z/ Z
        }
    * H0 a5 L# W9 H& B; x
    , b4 r0 R  j* b1 b" j}
    ) E" I: `9 I" N0 Y# _# v2.2:redisTemplate模板工具类0 a) L: E& U7 z# m5 G
    + t% ~4 u# Q% y0 X) A
    @Component7 E$ M% d; x! p% w' I8 s
    public class RedisManager {: @% s, C$ l* d8 D/ r( K

    2 ]: }- _1 Q, Y  l5 P, b4 q2 k    private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);1 [0 }# F  }# X2 J9 q

    3 H% S- h; B8 a5 a    @Autowired
    + ~& b, d/ l# M# m7 {9 S    private RedisTemplate redisTemplate;& ~, g5 N" F+ ^2 h% l/ c
    " f/ R; G, o/ h% o
        /**
    * Y( Y* H! B( ?7 x     * 设置对象: s9 u" r0 Z; ~+ K6 x- w
         *
    - L& ?( X9 d! a     * @param key key
    : C! z" B) S" l  [% {& \1 _1 Z/ q8 t     * @param value value值% q' ?& x6 |& k5 k+ o1 j
         * @param <T> 返回值泛型, J# M, I  Z6 n$ N" \( ]" X
         * @return 正确的值:<T> 错误的值:null
    / R" o7 F- E# E% q5 o. M     */, y! x0 c1 \* W# U0 E
        @SuppressWarnings("unchecked")4 w+ I3 r$ S) U' O
        public <T> ValueOperations<String, T> setObject(final String key, final T value) {
    & ?& W6 y4 D/ q! N5 t        final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    5 \8 N: o- f, c% n        operation.set(key, value);
    + o) g5 C! C0 U        return operation;
    ( P8 X. y; ^5 m: E- Y- |8 {2 b0 b    }
    + d, N- y( M9 f5 e  i; h! E4 s% c6 @# I  T! R9 b
        /**2 H( A6 |+ I8 f7 E8 {, C
         * 设置对象及失效时间 (单位:秒)
    5 U) p. a2 u) C5 S     *6 d' |+ A; _2 Z+ M+ U
         * @param key key
    # R" \! z* X4 H. W& z     * @param value value值$ B5 g0 O& F4 p  K) @# D- R
         * @param <T> 返回值泛型
    - k  r, G# p" I9 p  U     * @param time 秒值
    % l* T) v+ U. Q: H) q; G/ l+ h     * @return 正确的值:<T> 错误的值:null
    ; F; l  ~) T% U, B# o7 v     */
    0 r9 l. ]2 E9 b& ~; ^4 ]8 u    @SuppressWarnings("unchecked")8 ]5 i3 f0 g8 O5 C" U0 ]
        public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
    . f4 H) L* B- ?        final ValueOperations<String, T> operation = redisTemplate.opsForValue();0 l4 G, N/ f9 f, U
            operation.set(key, value, time, TimeUnit.SECONDS);
    * g- U7 E$ E" J( a- c% W        return operation;* `- g5 H7 u; R- R, Q
        }
    . F; I  Y+ F% U* p- k0 m
    ! U/ V6 ~6 o0 e( ^* k3 i
    ' T5 o. d% {9 X: U' Z! ?' p    /**& V" V: E; o# D
         * 设置对象及失效时间(单位:毫秒)
    7 B: [1 A3 l, _. S- }0 Y     *
    8 G1 z+ W  h, k; M% I! Y( l     * @param key key
    8 t/ `- i$ c& `     * @param value value值
    ! o% w4 b* M! M     * @param <T> 返回值泛型
    + I2 D* I$ O$ g& ~* y1 _     * @param time 秒值2 ~" D/ o% l* |+ v) V2 L5 L/ `
         * @return 正确的值:<T> 错误的值:null
    0 j$ {9 y3 f; V+ X: f     */+ ~% _& T- D, }& s5 h
        @SuppressWarnings("unchecked")- P# R. r0 R6 T# `
        public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {6 m$ h+ {$ }, j. h* z+ U' e# [# `
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    ' D4 k" o, k7 I        operation.set(key, value, time, TimeUnit.MILLISECONDS);4 s! j  R# _* j: r& h5 a# j4 ^( A
            return operation;3 E8 [5 B' r& [1 t2 ^5 r- M
        }: e+ o8 q; L5 F; c7 [3 R. }

    1 L( q3 H2 J# P- C4 g5 J    /**
    - ^0 O( R1 C/ ]: {     * 获取对象8 k4 J& c4 `6 ^$ S* D9 m
         *; y' I. u9 r' D/ {' C% \
         * @param key 键
    7 A+ [. a$ o2 ?     * @return 正确的值:Object值对象<br>
    1 c7 r6 v: o3 A  c! e' a     * 错误的值:null
    , ?* c4 `/ Y* B$ @0 o& L" A: X     */
    / w7 G' r- l6 a3 O4 V    @SuppressWarnings("unchecked")
    0 s& ^  j' O5 G% \* p( o8 C    public Object getObject(final String key) {' W7 O3 R$ `. u2 e; k; t# C2 M7 b
            final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    * X# X3 e% [, R/ `( C) [8 s        if (valueOperations == null || !redisTemplate.hasKey(key)) {
    6 |. J% [1 d* i            return null;
    % }& n, e" M3 X! r; K        }( S( r7 \4 M, l
            final Object object = valueOperations.get(key);7 j  [' ~0 I$ S; p& U
            return object;. k$ `+ f5 m: r2 o
        }
    : _8 M  i( c- B/ [6 `: X0 ?8 y
    . X! B) g4 E4 f% T7 ?1 b3 x6 Y: O1 R5 U    /**. B8 }" G+ d& B
         * 从缓存中获取string值
    ( p* ?& l! @8 I8 Z# q3 |     *+ p: b5 A$ @% Q: H8 N
         * @param key4 l% }% \8 P7 h# |$ U
         * @return*/
    6 T4 L2 H( H. l5 b% y    @SuppressWarnings("unchecked")
    ) K; f4 P7 I7 e, H/ M- f    public String getString(final String key) {
    9 ]7 X8 ^) c3 y+ e! M        String value = "";
    * d! _: S/ H7 ?        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();0 `, n9 K4 I+ ~8 l: u
            if (valueOperations != null && redisTemplate.hasKey(key)) {7 G* p- J: Q( ^0 F- C$ k4 w* a
                final Object object = valueOperations.get(key);  [$ |  Z, N* Z9 J' A, v
                if (null != object) {
    4 {$ H8 p4 S5 n% |9 @                LOGGER.info("--getString--object not empty");
    % p: C0 Z2 x5 e# c+ c" z7 n& [8 B                value = object.toString();
    ; X9 ^) X& q( z, H1 u; d* d            } else {
    5 z( ]$ A+ P* g" f( M                LOGGER.info("--getString--object empty");
    " A: Y1 V' d$ Z  C+ U            }
    & E% ?& U/ [0 b0 N        }
    ' h1 [9 [/ ^: R        return value;
    9 P) J' U) _& z: T! ?3 j    }
    5 C4 Z0 D- a" s& e9 s2.2:在redis中实现时间控制  A$ }3 i3 d$ b2 U; t! I

    5 C  E4 G/ o& C% f- e2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。
    ; K1 n; X# }- G2 r
    ! r" j! [# N- g7 P# Z1 Rimport com.youjia.orders.redis.RedisManager;! s$ v2 N# ?6 f9 r  h* M: ^
    import org.junit.Test;
    $ X& H9 S2 O% w7 fimport org.springframework.beans.factory.annotation.Autowired;# k1 m' H6 ], J6 ~
    0 r2 I5 R3 a. T
    import java.util.Objects;! F( l2 Q; H; g/ F

    2 l0 Q) J8 y: H& S. w  j7 r/**
    . E& @1 o/ C0 I. Y4 b * @Auther: Yrion  C0 J% g& }8 \5 F+ i  d
    * @Date: 2019-01-11 23:36
    ! ?- ^7 k) M! O5 g */
    3 t7 q5 {+ B, m$ Q: l: G2 V+ g3 z% M' H" G9 q  U3 N& T$ q! f
    public class RedisTest extends OrderProviderApplicationTests {
    . l2 l; E# u) p7 q8 u
    4 O) R8 `  ]+ Y$ y    @Autowired( e# q8 g- _3 u& w; Y3 l- N
        private RedisManager redisManager;! L) s  n2 Z5 `2 E$ C

    - U% G% v: }: {# u" i    @Test
    * C, y2 i' \* J' A7 ^    public void test() {
    , M- R$ d% m- R        controlTime("10000001", 10L);
    # M) J/ W5 k4 j1 }7 E    }. x  |+ y( L5 w- v/ r2 V

    " A5 I, ]* C5 g, R4 m4 v8 x; ~& T- k    public void controlTime(String requestId, Long timeOut) {5 B9 K6 ?! S9 ^5 I( y

    " P1 j/ W1 o1 [' Z3 O5 ]) a. s0 D        if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
    , A" d0 c# L+ B, g3 X            return;" N% W+ a/ \( q. R4 S' [3 ?
            }
    # k' ~3 h1 j/ I! K        //something code0 b5 P0 j  [# v1 b3 Z: y3 V- ~9 m4 [
            final String value = "value";
    ! L- L. W" `8 X        redisManager.setObject(requestId, value, timeOut);& O' Y( e- D# d. r/ \4 c
            final long startTime = System.currentTimeMillis();$ k! H) ~  Q  ~" Y( B/ w
            System.out.println("开始控制时间");- m' q9 f, t& i
            //start
    # b8 ~1 \8 n6 t3 n+ L' X( J+ H        for (; ; ) {
    * R1 X) T" E" y3 {, F& N1 z            if (Objects.isNull(redisManager.getObject(requestId))) {
    0 E, r* k0 V$ o                break;) ]8 q/ m' j& V$ `2 d) S8 f
                }% _* l) x1 K& |2 T( a$ W1 m
            }
    1 g$ I, u" G% K        final long endTime = System.currentTimeMillis();# j, t3 z& v! A0 E6 [& x

    ' h4 }: \# y6 g/ y# t: j        final long useTime = endTime - startTime;3 Y' a& j! _# H; Z# w+ o5 ^

    3 e6 V) n& f) b6 d; K        System.out.println("一共耗费时间:" + useTime);# w( ?& N  `4 [: q0 v
        }
    * K+ F' T: W0 X! v$ p}5 B" J: H, l. m3 m& ?' v8 W# o
    outPut:4 t* d7 _6 U& S1 Q* H! `% S
    " c$ ?9 w; K& F! J9 T
    开始控制时间
    2 ^. I6 L3 Y2 L1 c6 u7 d+ n一共耗费时间:10042: ~3 t8 y; v4 e% M0 C! _2 [
    三:总结% C' C8 n3 h' j$ t% {0 `

    1 a7 [* @/ F5 J! Q9 b& S2 I7 w本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!
    7 Z; h' u' t1 n7 f————————————————$ h; N! O1 H  V  Q+ F4 Y% M9 J+ {
    版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    + `9 I- K! i: X$ q8 I7 f原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/1058933252 a: G9 ~, A' i! ^+ h$ k

      J1 q# `! |/ Q5 k7 O
    $ ?! Y6 K- ~, a; W' L# N! 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-14 03:13 , Processed in 0.449747 second(s), 57 queries .

    回顶部