QQ登录

只需要一步,快速开始

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

    ) l: F  A. o! Z4 gJava如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
    & G) ^) W/ m4 D( Q0 I, Z6 J) U4 U$ Q
    一:时间控制的几种方案
    # \* S6 ~& A3 I5 R- p5 F
    + c3 Q7 o$ u9 t. T  }- {5 B1.1: 从线程方面解决
    % T! u+ }9 k8 q5 @, S  J! F# r) n
    5 ]5 ~# `* v. K* k5 c最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
    % T: L" @' ^- d% x+ [& V# W5 V+ }  _4 v! P3 I, K: |
    1.2:使用Timer6 q, G) L7 b3 e. i! g

    5 \% ]% E0 u5 a4 }: N查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:) ~: K' L. I; b  p3 {

    & X. |+ x9 ?9 y0 r6 _public class TimmerTest {1 C( `, x# A9 x3 G1 X, D
       /**
    ! n# _) h. M$ u6 b& R! {     * 测试方法" X2 K1 n3 M2 u/ T! C& {) `
         */
    8 ~" ]. c" J2 W5 B5 ]* L5 c    public void test() {
    # u7 X2 H, u$ f' r9 }- k0 d        Timer timer = new Timer();
    $ L7 h1 j/ ~/ ?7 f        timer.schedule(new MyTask(), 800);
    ! [  O  |, ^; g0 U    }: `$ A. X  Y! L

    0 B. h9 E0 S  `  v( \6 p$ K6 F    public class MyTask extends TimerTask {# Q% w0 p2 ?3 V9 T6 m
    / S! e3 N) m9 ?2 I& O8 R  ]- J
            /**+ g5 h; e% t: Y* s+ b4 g% W/ ~
             * 运行方法
    & T8 F8 \- s+ o8 h! G* k+ W         */
    , ^# S3 s4 q" w8 S% j( s        @Override  w+ K' J6 ?+ |5 e9 U* j& b8 Z- X2 O, ?
            public void run() {) u0 v8 J# s' R7 U% ~9 C
                System.out.println("输出");2 ^& h# d/ \7 V  U9 r+ u: J
            }3 T, ~0 d  J0 V. D$ Q
        }
    0 D* T- o* t. w! P" u% c7 n& ^}4 M) t+ S: Y4 |9 O- h
    这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。* S2 Q2 y5 N" j+ O5 [

    " @3 U1 _4 m% L0 Y/ [1.3:redis延时
    4 S5 @! a! H8 V- C
    - Q9 H) V, k/ P7 \, q7 S0 R在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
    ; B  j( {% L% g9 t# b6 v5 `6 P
    " P2 p6 q, J! {) A
    6 q$ F4 _/ E/ \' ]" F' R+ o6 z9 k
    , R" k% V9 Q* s: H% r通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:: R7 V# G$ d) j& O4 y# G7 m

    # P) O: }8 m2 e8 P, n4 Q) B9 J1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制( Q( t$ a, u) x+ N
    ( C3 w4 G3 [5 M1 p; ^
    2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
      u9 b7 R" G8 t( n8 T1 n3 l7 v* Q6 k  v6 E3 t4 P6 c, {
    3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
    ; A0 D; O/ F9 j9 n1 G( {% u" N2 o9 x  |; c3 _; n
    二:redis
    ! h4 ~1 i# y; o) m; t' h7 w( ^3 ?
    ' [- K# y4 R! a- {* M/ }2.1:maven中引入redis
    % s1 |. ]( G6 z) t. W# k  I/ U" x* j1 z6 ^) g* V/ c) Z
    引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。* b+ |6 J. e1 b9 S8 z

    2 t6 n3 z+ \( [<dependency>. k: j$ K; a3 t- b3 h+ }
      <groupId>org.springframework.boot</groupId>! {* w7 w# Z7 a) v! w
        <artifactId>spring-boot-starter-data-redis</artifactId>, ^9 l" [# ?  \
          <exclusions>' v# e& K  I) I
            <exclusion>
    5 S( ^" J9 c4 _0 x          <groupId>io.lettuce</groupId>
    & B  k& g2 W! S) `% {6 a# H          <artifactId>lettuce-core</artifactId>) H1 F- Y0 T* _6 b7 {( u+ n
            </exclusion>
    3 n) S9 L8 G8 s' w1 U* C: n: g      </exclusions>2 k3 l; ~4 U5 W2 f) T$ q& X7 |
    </dependency>5 h3 G8 V9 z) O) u) R# p- W
    <dependency>
    0 i% p" U8 s( D3 Y4 o  <groupId>redis.clients</groupId>: X) W8 a9 B# `/ S
      <artifactId>jedis</artifactId>
    ! Z1 l, _/ d5 V" {9 h8 H; j</dependency>
    2 G$ i7 U$ B: ]- w3 s( O! g2.2: 在springboot中配置redis
    7 M  p( [; i! R/ g; y7 t5 p! \: k0 Y8 }+ y
    import org.springframework.beans.factory.annotation.Autowired;
    ' r. ?, Z4 B& qimport org.springframework.context.annotation.Bean;* g; ]- J; G; q& t$ m/ Y6 H
    import org.springframework.context.annotation.Configuration;
    ; c/ }' ~! {* ^import org.springframework.data.redis.core.RedisTemplate;- ^% K) `+ x; d7 g  a+ S- \
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;" @! `* ]: R* \$ z/ m
    import org.springframework.data.redis.serializer.StringRedisSerializer;) \0 }  ~. ?( b

    3 {' b5 ]( @" K& ^@Configuration
    0 P- ^# O& ^0 O- T) y. Wpublic class RedisConfig {
    ) f1 u" J. H4 O' W9 t4 a0 `/ @1 L6 e4 u7 k/ Y, q1 q
        @Autowired
    . Z! h* D5 V" S9 P* ^( L7 _) w4 X    private RedisTemplate redisTemplate;
    . q; B) y% {! s7 C' U& V: M: ^. Z3 Q) o+ F8 D3 y% W! P. T
        /**
    7 s8 z0 \. @% {. c  ]: M& l6 V     * redisTemplate实例化
    " C( K! X8 c% t: s5 t     *4 }. k) _" @8 p+ ~: ]  A
         * @return
    . e# [5 F2 r% k# q% |     */
    / q) }/ W- B8 S% J2 E" q& u3 k" E    @Bean
    6 D5 U8 [; q1 j: a) R0 m! o; X: L  u* L, j    public RedisTemplate redisTemplateInit() {2 m4 _6 n7 A5 L  W3 m% ]
            //设置序列化Key的实例化对象
    9 p/ ]: F3 s7 [7 B* x( a        redisTemplate.setKeySerializer(new StringRedisSerializer());
      v/ V8 e$ c2 i8 U% y        //设置序列化Value的实例化对象
    & V  E3 d& V) h2 W# L        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());( t% O; A( h7 O7 o
            return redisTemplate;- c, ?. M) M' D* r. B3 x+ T, F
        }& I% s) }. U' U* v, \

    9 E5 m6 d! `3 F8 l2 B7 b}
    8 A4 E9 i) r5 q* r5 {2.2:redisTemplate模板工具类! w2 W3 P2 G. x

    7 L6 I; L: Z9 m! L@Component
    0 t1 ^  Q# @" z4 Opublic class RedisManager {8 F5 A9 e% k; ]& Y9 ?( S$ d' C. V
    $ P. @" \* G' p; ~8 P
        private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);  W. q1 m8 f1 X  c

    ' k* R' Q9 I. C7 M) G    @Autowired
    # i1 r8 H; l2 M1 f+ e* E) T    private RedisTemplate redisTemplate;4 u- Y5 C! c7 `3 C7 q% o0 y
    7 w5 q" @+ _8 E  p9 U2 c: E
        /**8 D) a! j; a4 w' L% F, Q
         * 设置对象
    : z; R, d# \5 [( J+ f     *
    4 E/ x# Y9 W! @( s, E     * @param key key
    2 C7 z7 l- ]$ O3 h% |3 q; f  {) W     * @param value value值# X1 N3 G7 J; R
         * @param <T> 返回值泛型
    * H7 G, s- M' h     * @return 正确的值:<T> 错误的值:null3 K% L+ b7 u7 ~+ T0 Q
         */+ @5 f5 G" t* @/ F3 x1 p
        @SuppressWarnings("unchecked")
      O; A6 U: t3 }  S    public <T> ValueOperations<String, T> setObject(final String key, final T value) {
    ( ?5 O* e5 v% {' `; Y        final ValueOperations<String, T> operation = redisTemplate.opsForValue();5 V' [, W$ x8 ]: l8 G: n; ]
            operation.set(key, value);# B/ n. t0 R5 ^5 [1 D
            return operation;2 n% a) y3 |% c4 Z
        }0 r2 q' Z: I( }9 V% y# M

    0 Q& ?& R/ {* J9 z% v; L5 |/ T    /**' `) T- U" o/ c1 c) u
         * 设置对象及失效时间 (单位:秒)
      v* I/ W; S( L, O     *
    0 h$ b: X1 ?. f2 @. p/ V/ W' R     * @param key key, w% j; y. q+ w' ?4 C
         * @param value value值
    ; h  m6 ~8 Q5 |0 t2 V+ o     * @param <T> 返回值泛型
    8 M* u) X6 O1 `0 I4 d/ S( N1 I4 t     * @param time 秒值
    * l; I* v. j$ Q8 E     * @return 正确的值:<T> 错误的值:null
      j: n( L9 D9 s% f8 J: ]. f3 k     */
    - Q, H  V* V$ `) C* h    @SuppressWarnings("unchecked")
    0 b2 _0 J" D9 U) R$ s+ n& W; k    public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
    9 ?5 l. p5 H/ u1 e        final ValueOperations<String, T> operation = redisTemplate.opsForValue();" }* i; X- R" g) Y9 J- V
            operation.set(key, value, time, TimeUnit.SECONDS);
    5 [" t  S) V* Q6 u0 [* j! ^$ Q        return operation;* e. }" ^4 S3 |! o8 |+ ?
        }
    6 i8 d+ T+ z1 i! y8 ^
    " ]6 E2 {& ?* X; H8 N4 T, v+ z0 B
    ! x% N1 e- L; S6 o, D" G1 H! i    /**
    + t- w+ B) W, d( @" a     * 设置对象及失效时间(单位:毫秒)
    * p- }1 }/ m" v4 F3 ~! r8 L8 b, _     *% B8 X; ]. ^, s( U0 N: ~- [' _
         * @param key key
    0 N2 c$ U' n/ W. b; n2 d     * @param value value值; j1 `$ Q. c7 O: I+ \0 N
         * @param <T> 返回值泛型, R4 ^6 _) K  g8 ?; x( L
         * @param time 秒值0 j: N) f) E7 }: t+ U
         * @return 正确的值:<T> 错误的值:null( M' \* {9 x; c5 Z+ N
         */- a( X. g# I- F/ @" x) g" G
        @SuppressWarnings("unchecked")
    ) e3 e+ {- b0 f, H% Y# p    public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {) b. f' K  `- H* Y- y
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();2 X7 L% K- R/ D. l% T
            operation.set(key, value, time, TimeUnit.MILLISECONDS);
    7 O2 Z. j3 I5 W+ \) ]' l        return operation;
    6 k) H9 g2 N* j- n9 ?' C0 O* t9 Y    }- k; f8 s6 a) I3 F7 g; ?! i

    ; O8 n, P' }, |7 s+ K8 d% _3 N    /**9 \, W  m4 w: k4 O2 w3 V2 x
         * 获取对象
    7 y) g* ?, Y& d' c* [- q     *
    + n4 o( l. f1 U- B/ A     * @param key 键
    ; C/ c1 p0 v- ~+ f     * @return 正确的值:Object值对象<br>
    , S" t+ ]7 ^# y/ Z: }% H     * 错误的值:null
    ' W0 E: `$ ~8 c' M0 V     */
    * K$ U6 c0 ~  W0 z    @SuppressWarnings("unchecked")( q! n  }. _7 m
        public Object getObject(final String key) {
    : P( F4 W, M. H/ w9 {" {        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();1 [+ g+ `; I, V  b2 v0 |
            if (valueOperations == null || !redisTemplate.hasKey(key)) {
    4 \+ E6 A0 ^7 p8 D            return null;
    5 k9 b, V" h8 k9 l" K        }
    7 B6 d: g. Y4 O! W- x7 _0 q  o        final Object object = valueOperations.get(key);2 E2 w6 A4 w/ z8 I) D9 J1 g2 o4 A
            return object;
    4 F% Y4 S% H8 \& c    }
    ' {/ l5 K9 \0 S* Q, g- {5 |1 \, {! o. B" ]3 {5 P7 Y
        /**
    ) M) l3 z9 ~: h; D" L' `) N     * 从缓存中获取string值/ n8 C3 V3 g+ J3 e
         *
    # H3 o. _* B3 l; w2 _4 e     * @param key( O: a; N, N; M$ b! C
         * @return*/3 H% t5 |. X; }1 D6 X
        @SuppressWarnings("unchecked")+ P1 R6 e* D* M5 s' K6 @$ \
        public String getString(final String key) {3 L" R6 }# i* G8 l9 B/ G
            String value = "";
    7 i, T9 x8 F' S% J' G/ K$ r        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();$ g- q$ N2 Y0 z
            if (valueOperations != null && redisTemplate.hasKey(key)) {
    5 V0 z. i; m4 i7 ?. V+ @* i            final Object object = valueOperations.get(key);4 k2 m8 ]7 c! u+ K
                if (null != object) {; U" T& r6 C5 g$ A1 ^
                    LOGGER.info("--getString--object not empty");/ F8 ]. @- B- [# X8 [
                    value = object.toString();; N' T5 h# q$ Y0 d
                } else {
    & U; C/ D: V: F                LOGGER.info("--getString--object empty");
    6 p! K/ p8 q& C            }
    ! P* ]4 X7 D& R: T( ~/ z# `        }
    0 Y$ m. X" C0 F! Z0 R        return value;2 }! v  p$ Y; _# ]+ {, \0 W
        }/ F6 \( s# W6 Y4 ~
    2.2:在redis中实现时间控制
    + {- N+ K% ^; l6 j9 K( G
    # {- v. J1 k, p/ u) Q+ {' H5 w- S3 y2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。
    3 ~: A+ d4 I& [: W& m
    , X  o/ z4 E2 k+ d5 Rimport com.youjia.orders.redis.RedisManager;
    - j* B5 }3 e5 @2 i$ Cimport org.junit.Test;4 A, T7 S* x% W4 l8 j8 [2 w: E
    import org.springframework.beans.factory.annotation.Autowired;
    % T6 k$ B3 n: _( U* Z
    ) H5 O8 \& l6 i5 ^5 k. vimport java.util.Objects;
    & K4 _7 s8 e: S4 ^" j( w: d8 D/ m/ G" Y6 `$ ?) h+ j3 Y
    /**
    + D% o+ {- |+ W' \  | * @Auther: Yrion
    & w) p$ H- S# D * @Date: 2019-01-11 23:368 y! Q" @. W# W  u8 W+ `2 |
    */
    6 P% w7 e/ H6 w% Y+ u' h0 I
    # g1 I9 p1 Z* u3 k; Q& u0 d8 }public class RedisTest extends OrderProviderApplicationTests {: V6 g) ^: e. ~" p# g
    # u4 C  @6 ^) Z- Y- l9 U* u, I
        @Autowired6 U7 \' Q4 I; K7 H, G; `( O
        private RedisManager redisManager;
    + A. A. u4 e! _/ f  z" g* Z5 A$ D1 l" W2 _# Q% s! }9 N) y
        @Test2 [! B8 R$ Z. ]1 @9 I% L
        public void test() {1 C  p# |4 K& L; c8 V& \9 g3 ?
            controlTime("10000001", 10L);) V0 |. r- q4 r1 ?+ ]- c
        }* l0 K* k; a- H, b4 j, ?

    * Q5 \$ |# ~- d) E8 M    public void controlTime(String requestId, Long timeOut) {
    1 w4 i/ F" J8 T2 f* J9 c
      q8 y/ j9 ~, ^/ \6 h        if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {( A1 j+ T+ I& \! P( p; ~/ H* _9 a
                return;' P+ D7 k& }) n; M- u! N8 t3 ]
            }) h8 I+ p% d' S/ s  t
            //something code! _# w6 Q4 {, o8 B
            final String value = "value";4 k% s' i/ f5 w  g" o+ y
            redisManager.setObject(requestId, value, timeOut);
    0 f- X0 y1 d% V) ^/ [; v        final long startTime = System.currentTimeMillis();
    8 }% V/ N' c3 n* \0 r        System.out.println("开始控制时间");
    $ t" @% o: m( n. {' l        //start  N% t* T0 {* O# }8 @3 x, O! ~
            for (; ; ) {$ ]3 F2 P. t2 x0 Z3 \1 X
                if (Objects.isNull(redisManager.getObject(requestId))) {3 o# i6 ?0 V. v! u6 Z3 z
                    break;
    # ]' h6 N* b8 L) I* t* h            }+ C  }! t  i# a1 W- O
            }
    4 a( c) K5 h* r        final long endTime = System.currentTimeMillis();* C$ ?2 y0 ~: O0 f9 M; o0 J
    . u& L) ~8 C, r, V7 o
            final long useTime = endTime - startTime;
    ! p& o. ~& \! \" j) s  d
    : z$ ]" ~0 Z( \        System.out.println("一共耗费时间:" + useTime);, b; z5 U4 g& `6 Z2 b
        }1 _8 g) F4 [) E
    }
    : U( [! {3 o! E" L: voutPut:; L4 q5 ~# o  Q: o; i; s

    ; C& }0 J  `7 S8 E$ y) L开始控制时间
    8 x# i" ~, p+ S5 t; ~一共耗费时间:10042$ P8 ?! I& z! A8 j+ P  ?  d
    三:总结
    # g- J% \" u. P! F
    1 M$ \2 H6 l9 j( O+ F6 ~本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!
    $ g$ B% B/ ~% f4 k( a/ W+ b' N————————————————$ v" A0 F/ l" n6 Y4 A6 w% L8 b
    版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    2 X/ M# l! r. y, f  B$ K原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325
    / s1 z4 u  b0 D5 Z9 D
    2 S4 u% P; N6 W3 N" T
    # j( L6 O* ?  ]3 y& 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, 2026-4-20 11:40 , Processed in 0.419588 second(s), 57 queries .

    回顶部