QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1510|回复: 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
    / r! {2 F0 t9 [- ]+ d
    Java如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。% v! A/ m) P1 v  r/ O/ s" _: t
    ( d- B) w0 I# O; i5 ~8 m9 a5 {
    一:时间控制的几种方案0 V" s. Q& N3 i, g* y7 {0 p
    8 Y4 R% k1 b. m, [8 e) l
    1.1: 从线程方面解决1 S( g1 h1 D! N+ T

    $ c2 N# g# ?: `最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。( r% M& b4 x+ k4 e/ Q; f' r
    ; N- a( i- f7 B2 O$ Y
    1.2:使用Timer
      R0 J8 s2 h, Q3 ]1 [. U3 T; M' k) v8 t, g1 J
    查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:6 o2 t- q8 M- T( h5 b
    ) C: q/ A/ K& E2 l
    public class TimmerTest {
    / K3 g  W7 v) w  c) A# b   /**
    4 y; c( C  i" N9 R) {1 D     * 测试方法
    ( m# d& N; R9 I     */
    3 D% E7 R8 j9 u1 _. {* X    public void test() {; Z9 p( u4 O6 R' v0 Y$ a9 G+ l
            Timer timer = new Timer();- r' t" }3 {. o0 I$ l. b& `
            timer.schedule(new MyTask(), 800);# Q; o0 ^' s: H& G% }
        }
    0 G6 a( K5 P# `4 f# I7 L3 g4 X" f6 ?0 c8 x% S) I9 n
        public class MyTask extends TimerTask {
    ( J; Z  s- T2 w; Z
    / J% p1 v7 |# Q$ ?        /**  D" L6 g8 b2 g7 l7 }. C0 l5 d
             * 运行方法
    2 u: m# A1 y  z) i         */8 O/ L: Z. x7 g6 m) P
            @Override
    ) I0 O! H6 w* o/ b+ Q5 R$ f        public void run() {
    7 g* u1 N; s* y% W! t% }; p            System.out.println("输出");
    / Q/ H' z& \, j7 H0 r        }
    * d( F3 f0 @4 o8 n; l7 `' X6 Q8 W    }
    ) w+ y* `3 T1 u( |% S+ M! v}
    4 e8 j  S" m* r* M  b$ i" \1 Y. Z这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。% A: X1 f" ~0 G. m# i
    ! X& m4 U- [0 X* X4 h) y; q) {
    1.3:redis延时
    ; |) U0 E7 {; Y+ X  _' U; X$ x* M+ ^: }7 m; B1 k) P
    在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
    4 [2 m& I% I% R
      k5 X0 l) ]  e, W8 _% n
    $ r/ b) H9 @% |$ n: O; z" v6 p; s7 |, r; E4 s
    通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:$ _  ~& B$ o% l% ]
    * S* t9 _; x; h5 D
    1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
    ' Q& g( `# g- b1 i7 ]' j& B
    & U7 D5 W+ N( V4 c. m+ Q2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现* l9 u3 ?& x4 Z. a* A3 W- ^3 s
    ; u) b# [7 G7 c; p3 f  h
    3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
    . \  w& s$ C; F: s8 k" b0 K+ F
    2 V& V. `/ p0 m( M* R. W二:redis
    7 d; A8 v$ {" c3 k0 t; |$ o
    5 P' R4 z5 B4 z! o2.1:maven中引入redis& T* {9 t7 P, ^( w2 S0 [

    ' b' r* {6 o4 N- m5 S# e引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
    4 u4 q+ i( W0 a8 K1 ]; X. L( ?- T$ Z8 z8 G0 Y7 x4 a
    <dependency>
      }# ]2 M( k8 T3 h  <groupId>org.springframework.boot</groupId>
    2 _$ D" K8 U) u* ]: m. ?  ?    <artifactId>spring-boot-starter-data-redis</artifactId>
    ) B6 S9 K5 ~. b# q* _      <exclusions>
    - l; v4 f8 ^' ?( Z- o" N        <exclusion>
    $ }+ |) @& A! n/ @3 B. V0 Z  S          <groupId>io.lettuce</groupId>9 q5 S* \& N, o1 A2 J
              <artifactId>lettuce-core</artifactId>
    6 U  c  H) c3 y1 c% {. {( {        </exclusion>
    8 ^/ |/ H/ K+ i" B; \      </exclusions>+ _% o9 O8 B# o
    </dependency>  K4 R; `: j4 E  N
    <dependency>
    % Q7 a' h& k  d' g, s  ^  <groupId>redis.clients</groupId>
    : E. p( Q  H6 l) U& R4 h: Z  <artifactId>jedis</artifactId>
    4 ?: b5 z, F- t6 d. ?</dependency>
    4 H+ |5 a: B  T. e+ O6 s) W2.2: 在springboot中配置redis
    & ^; {( a& ~( \. F8 _* e4 C& Y, n8 I
    import org.springframework.beans.factory.annotation.Autowired;) j, E* w' G" G* J) _1 @* U
    import org.springframework.context.annotation.Bean;
    6 y& b$ b6 a+ Q+ d5 y! E: F* Bimport org.springframework.context.annotation.Configuration;
    ' Z8 z; O7 U6 nimport org.springframework.data.redis.core.RedisTemplate;; q- A- L, I: ^
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    - u  e5 F: t6 T1 L9 Q* h4 Timport org.springframework.data.redis.serializer.StringRedisSerializer;9 T6 W8 o) D+ K; U. c$ D4 @  x
    " `# m! y- H& M, ^
    @Configuration: f# q5 W/ X- l; F, |6 C: @$ @: x1 G
    public class RedisConfig {) d% C. O4 @; P* @
    ' `4 `2 Y# f9 o7 ^0 ?% s
        @Autowired3 O7 l) R7 l8 U0 E; Z& z
        private RedisTemplate redisTemplate;
    4 ^! v3 ^6 @1 h, |1 R- X) b
    . R9 @, K% {7 F6 y* d4 E+ L) J    /**
    % @( z! F: `1 ~/ B, G& v, b; t' }1 }     * redisTemplate实例化
    0 F3 E' `4 i2 P: s& Q! w     *6 h  ]0 I) Y) h' U
         * @return( P' i1 K9 `5 {8 S  D' _1 d" [; X
         *// h; E3 G7 k9 j3 a# _; q/ D% J' y
        @Bean! K" u  R  R5 I0 K+ c, d
        public RedisTemplate redisTemplateInit() {
    8 v2 I% f+ ?& K3 b! @2 ?        //设置序列化Key的实例化对象! c- g7 q; \% v4 ~' c( M
            redisTemplate.setKeySerializer(new StringRedisSerializer());
      q* t: I' t) _, w8 o) k        //设置序列化Value的实例化对象) o/ U/ L0 Q5 _3 |7 B2 s
            redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    6 `* n( w  _) I4 ^8 s        return redisTemplate;
    * q' t) _3 I1 d( G9 y    }
    , y2 B" l) |/ ~. d- f5 |
    * K* T) L/ H4 s3 s0 I2 o}
    - s# _/ L$ z; B# k0 s7 s2.2:redisTemplate模板工具类
    0 a5 h- Y! p$ S* v, U8 Z$ T, \& m3 c: H- O& I, ]" @
    @Component
    3 U8 l' Q( ?2 X/ wpublic class RedisManager {+ r' |3 M- ^# B% O) J
    3 Y% Q& p0 C% W
        private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);
    / a, w9 R# \6 r/ T7 ?: {9 |; H8 m- W+ o6 t7 Q
        @Autowired/ i0 `5 ]& T- o4 y, x
        private RedisTemplate redisTemplate;3 l$ b+ S- y) q/ x$ V' V1 u
    ; @" o% J7 L& L8 V0 B0 a: e1 E, v
        /**
    0 L5 U3 \$ j: p( u7 v! W- {+ X& t     * 设置对象
    2 {1 U! X$ b" [( Z5 \( L, w     *  i3 Z" L: r7 I. q+ n+ u. W
         * @param key key" \- a% J9 p0 @  H6 G. r! J
         * @param value value值
    6 U' ~1 d- d! j5 _     * @param <T> 返回值泛型
      X- c5 u2 s+ ^. Y     * @return 正确的值:<T> 错误的值:null- a  E' g. M3 S. K7 G
         */
    ) |6 q  f; m% m9 Y    @SuppressWarnings("unchecked")7 ^5 n0 L/ M+ a( H0 U8 B
        public <T> ValueOperations<String, T> setObject(final String key, final T value) {
    # x; u* b3 j8 `1 V        final ValueOperations<String, T> operation = redisTemplate.opsForValue();/ u5 z6 p* s0 L7 B- l+ u
            operation.set(key, value);3 d; a1 e& `4 Y
            return operation;& H; `) B3 \4 L# O" z
        }
    & U! g7 y1 v9 u* T# p0 i6 X9 ^, t8 p5 y
        /**0 k8 n& n1 C4 N* w# C6 a, G4 ~
         * 设置对象及失效时间 (单位:秒)
    0 r% _$ A1 V* s$ ~$ B  s2 f# C- b     *# X, u/ A- e1 G5 r! s+ {; V: g& M
         * @param key key2 p2 a; I6 p& c" k* B. B) j; O  y
         * @param value value值
    1 V# g6 n( T. j     * @param <T> 返回值泛型
    . J3 R6 o6 X- _8 b     * @param time 秒值7 o" O" z+ A. T- M3 n
         * @return 正确的值:<T> 错误的值:null
    , Y/ g+ `1 x! Z% E5 w4 T; T8 M     */- @# ~1 Y1 y' |, h+ v- o
        @SuppressWarnings("unchecked")
    9 o" V0 p$ ]. q& h    public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {( i& b4 M# b, B5 @
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();0 a8 k/ k1 ?. G
            operation.set(key, value, time, TimeUnit.SECONDS);9 `. y5 y2 x* o2 {' l
            return operation;$ Q7 Q  z4 R3 e4 V, z
        }7 ?7 _# G" ~% X' [

    6 j6 p& M4 m5 C, H- B, K4 v2 t  O/ k! g  a( D% K# J
        /**5 ]' o. d- u/ Z( |0 q
         * 设置对象及失效时间(单位:毫秒)
    ; Y4 x6 Z7 U; `3 r8 o     *
    - b0 y: C6 L1 d9 |3 S9 a" y6 a     * @param key key% X( ~* Z0 K* ^; C/ v* x
         * @param value value值1 F: }" A1 f$ F; P4 H+ V$ X
         * @param <T> 返回值泛型
    0 U! B) X( `7 e6 t     * @param time 秒值# w$ ?$ g' @/ E/ D6 O
         * @return 正确的值:<T> 错误的值:null( Y6 }/ Z, [7 q, m9 g5 s
         */- `, T6 u, b8 q# V+ f$ Z0 u- m
        @SuppressWarnings("unchecked")
    + m$ C. D9 X5 t+ D5 Q    public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
    7 y$ L& @8 @. `. J. c9 P) \* w        final ValueOperations<String, T> operation = redisTemplate.opsForValue();9 p: Y/ ]' i" Z" g, M# M9 s
            operation.set(key, value, time, TimeUnit.MILLISECONDS);) n: T+ A- U9 `: {' Q7 A: D$ T
            return operation;  G+ a% L4 a/ U: Q6 m
        }4 H/ ~* v+ s! a9 E0 {; r5 g& t* @
    : _' J4 n: s5 N3 a+ @8 J
        /**
    : M1 C1 W# I- y$ Q5 \2 Y  D3 p     * 获取对象$ c+ K; t6 U  e! [
         *- B% k9 V0 ^' L& }) q/ j! x' o2 K
         * @param key 键
    ) ]) x; H: d% S2 R     * @return 正确的值:Object值对象<br>: K, g; h1 V% G0 A5 a/ g
         * 错误的值:null; [) _, w  T/ x# X
         */3 B5 v3 d" {/ t* g
        @SuppressWarnings("unchecked")
    & o  c( ~5 U. R+ `6 M) @    public Object getObject(final String key) {
    4 [+ Q0 J( w: K& o4 a; P        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    4 q. c7 L# h* r, v        if (valueOperations == null || !redisTemplate.hasKey(key)) {
    * Q/ m  }1 ?  [* @0 A& |5 S4 V            return null;6 \1 a. z9 A8 m5 z- |' Y7 E
            }9 C. l* Q2 v- o; ^! G
            final Object object = valueOperations.get(key);$ I1 T& b" t/ f
            return object;
    & z  e) q* n. d: z6 k- o9 _. S    }
    * Z/ X9 ~/ g- g- Z3 R3 m3 k8 g0 K7 Z4 J% }
        /**
    5 H5 N* H& C1 E! v$ k2 I     * 从缓存中获取string值; R, a, D* r) M- {4 {0 N2 P# S- j
         */ e0 G2 b1 R* Z
         * @param key
    $ r1 @. q  x* Y     * @return*/
    " ?, p' G8 Q0 g; h4 ~2 G. A1 {    @SuppressWarnings("unchecked")+ S; k% C& _7 G) O" G
        public String getString(final String key) {' V  H# \( p8 m5 |; \
            String value = "";" M) O0 s" P$ |. W- t
            final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    1 ^) a# y) O& n3 a, `1 J, P        if (valueOperations != null && redisTemplate.hasKey(key)) {
    " r+ j9 _- e! H6 I8 E! S            final Object object = valueOperations.get(key);
    ( d  x+ @9 R6 p            if (null != object) {1 w+ N+ Y5 P! P. N7 n
                    LOGGER.info("--getString--object not empty");
    ) |+ @4 t% Q9 z4 a: @0 ]                value = object.toString();
    ; Q2 p$ Y7 |/ f3 t            } else {
    - V: m5 x" D) @4 R" j8 d                LOGGER.info("--getString--object empty");! }2 ?1 L) u8 w- o1 n
                }
    - q: b3 J1 U" S6 r; T        }
      l+ x9 Z/ l% ]! @  g2 j        return value;
    * V6 C/ [: k5 j    }
    ; _: i* e3 m! a7 @: Q* ~2.2:在redis中实现时间控制
    ! R% L: ^0 X/ I5 S7 d9 ^$ w  R' \
    % ]" t& o  H6 Y% Z6 p2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。' t4 E) z3 G/ a
    $ D6 d- }" c) O
    import com.youjia.orders.redis.RedisManager;
    # q7 ]% R' s% Oimport org.junit.Test;
    $ D% Q% A% S4 v0 \. p5 N0 r8 ^1 Eimport org.springframework.beans.factory.annotation.Autowired;
    8 z& b  U: k$ i" P/ v; }1 o
    # J" r) e0 K3 F8 V+ J. }$ R; ximport java.util.Objects;
    1 @4 \( c( I0 N7 J" @. }
    : |  y0 y/ j4 P3 P# T3 C% _! Q/**; o% Q6 O& c+ y# V1 t1 g6 j
    * @Auther: Yrion
    9 P! B* m+ V6 b  x  F; p* G3 f6 P * @Date: 2019-01-11 23:36
    % ]  g7 U  p; b3 _1 P% w */
    " i* x5 }4 W' P& l1 j. s% V$ U1 f; i1 w* r& F9 O
    public class RedisTest extends OrderProviderApplicationTests {0 e- F" B# B0 Z; i) g: N" s
    5 y0 N2 y: J; c( K
        @Autowired
    0 Q0 ~1 D  k# @4 o7 T! S    private RedisManager redisManager;
    " e- \2 i; V$ ]( g  |/ J
    & @! r! ?% o7 }6 ^* M! x    @Test
    * n: w: g% t/ n0 d$ e" i" d    public void test() {$ N0 k6 S5 P( U% e7 a3 P( i2 F5 @
            controlTime("10000001", 10L);/ w$ l- O6 M$ f5 `: x
        }
    7 t: B4 x0 e+ U. e( m  {* c7 _% X  `0 W' L1 H. }
        public void controlTime(String requestId, Long timeOut) {
      S0 h+ {' w. M; \* u4 s
    " I: d  G. L* c* I4 r        if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
    6 ~% _  M4 X' }' a2 m            return;% P$ ^- x, U$ A
            }( y$ ?% Y$ B. k( I8 ?' E% W
            //something code
    1 t/ A2 K6 h7 @% o% T/ u- ~        final String value = "value";
    0 ^% {; Z, |: f6 P; [, c& \        redisManager.setObject(requestId, value, timeOut);
    ' b3 v& k2 m) a( B  ]6 J5 V5 h        final long startTime = System.currentTimeMillis();( x3 O3 _" i3 H# }
            System.out.println("开始控制时间");- \. k( G9 \  b8 e
            //start
    ' L: Y  _* F: |) k2 Q        for (; ; ) {$ R0 p$ _1 X. a& e
                if (Objects.isNull(redisManager.getObject(requestId))) {
    * k) I. Y; ]1 @  `                break;
    ) a& ^1 `- k9 f3 X1 _2 ~            }
    ) ^5 O6 U" j% a/ ~$ o        }/ H. Z  _& h. e- H1 n! a: Z
            final long endTime = System.currentTimeMillis();# q) S1 ~% v# i0 F) K0 q* n
    ! ^1 r+ K* D, y
            final long useTime = endTime - startTime;
    2 M9 k0 G) M9 I$ e7 k7 c: Q
    : b( Y2 Y4 s" w        System.out.println("一共耗费时间:" + useTime);
    ( F( s0 u7 J' U, b    }) d6 ?3 E* z; G1 y) U" ?/ s
    }! b9 r# l9 ^' w9 t) K; @9 L
    outPut:
    & w5 y! s" H$ z) ?) H
    5 C! n. w4 q& O- x! ~9 W! e% u3 O开始控制时间
    ) E. n* H0 t+ S: X+ I一共耗费时间:100425 ]$ B& j! `: M
    三:总结1 Q( p8 h$ m& [4 }
    # F; Z" S1 |: A: }4 F9 r$ Y8 Q
    本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!: x# u* Y( ]  `9 \
    ————————————————
    2 M2 J0 B7 t% x$ x# Z! [版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。3 L( C( j4 _& A1 n
    原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325
    4 ~! j4 f  |2 E3 @1 S: T5 y. M: V
    % U1 V! L3 B/ ?$ e: K
    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-27 20:03 , Processed in 0.427822 second(s), 56 queries .

    回顶部