QQ登录

只需要一步,快速开始

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

    / k/ U8 u0 D2 e2 qJava如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
    4 B2 {0 [% E, l" P. O5 ^+ f4 B( e
    一:时间控制的几种方案+ ^4 e1 k1 G' ^) m

    , d$ m7 o, E) B* y' S1.1: 从线程方面解决
    ( Z" B1 e5 d( F7 _! O' }
    $ \7 X$ y% E& r" j. C  o0 b最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。0 w: |) g* ~$ c2 j
    - A1 \& g" B3 L' h( s; B
    1.2:使用Timer* H3 g7 U! V: F3 V

    % _) c7 b1 h  X9 `查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:
    0 ~" p$ e6 A' F& S; I& |! l. N9 p4 P" Q6 p, r" ?% e* m7 t) R
    public class TimmerTest {9 X. Z; x2 b/ w/ i9 l
       /**
    ' _! f" m% C* E/ u1 K2 `7 V2 \     * 测试方法6 Z. M" t, v0 C" x5 j
         */
    ! M9 y" o9 L, n4 o    public void test() {
    ! Q# `4 C, C1 j) c  }7 Q        Timer timer = new Timer();4 L- `7 m6 t  o% V. _( n2 ?% l2 N! d0 @
            timer.schedule(new MyTask(), 800);# ?- W/ k5 {5 C6 j; z0 Q+ x
        }  ]' c$ V. H4 j: P- m+ @( I

    / K8 q* n4 u& n+ g2 k4 v+ Z    public class MyTask extends TimerTask {- n# Y% ~6 y- r# `5 a" U' s

    % i  R8 f& W$ x% `$ k2 {9 ]3 G        /**
    / o- _* L/ x  ?% i! E         * 运行方法
    $ s5 l6 @  O0 k) y1 Q         */6 p0 P! f, U4 A% F
            @Override
    ! G' t& I7 C3 K1 r3 `" x# ?        public void run() {: n. |/ @2 r- A# z4 w) W
                System.out.println("输出");
      G2 ^' r0 n( B3 f1 z        }, v" B1 o& s4 W' h" c
        }+ q$ t" }1 |& Y3 P4 S- c8 m7 G
    }8 p1 x3 b  M  I
    这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。9 {9 E4 X1 \( i) z  C( H

    - n0 e5 C) h  |; p1.3:redis延时
    ' ~# s- u! t! u+ i' D' v  X+ |9 d6 ^; E
    在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
    1 j( H* ]$ m; A" E$ i  S  O
    5 M% n6 u0 |! h9 P  i5 x5 d! I, |+ U8 `5 G6 X9 j- l7 [* R

      g& J1 m* I6 e! P- w# Z$ q- g通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:8 p! K( K5 R. X5 }; J, ]
    * T  k2 j- w5 G, E/ O
    1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
    1 J# w; S% y- m" Y; B4 M# o% L
    & g* W% C) h7 B2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
    # G/ ]' ]9 C* }# K7 Z6 X% P; ~+ y1 r
    3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
    0 S$ E0 S: t: A" `
    ( c! F0 |4 p* D7 U" D  j二:redis* E/ q. {, s# J, b
    4 `6 y+ ^, C$ Y5 w0 R% G
    2.1:maven中引入redis6 c- l5 i/ y1 `9 Y0 @! V! U

    5 O" E# t% i3 L3 h% k引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
    * _+ m( O3 o, _5 h7 j3 y9 H
    ! F( Y- Z: d8 W* x9 F" X<dependency>
    , [/ p) |- T$ q: {" [( g/ K8 w' c. v7 {  <groupId>org.springframework.boot</groupId>
    8 k% Y/ Y( }! C8 I1 k0 B8 K/ v    <artifactId>spring-boot-starter-data-redis</artifactId>% u3 d! d$ t1 G- n
          <exclusions>% w. J* O: h8 E  Z
            <exclusion>
    / _6 n6 F5 V  c! {- A          <groupId>io.lettuce</groupId>
    ' ?* D9 l- n+ @0 Q2 a+ J          <artifactId>lettuce-core</artifactId>/ K# R3 \6 l0 W* l( p
            </exclusion>. ?$ G- O( d! f$ K
          </exclusions>
    " j6 [  L% U, q8 z) j</dependency>- u. _8 K7 M: B/ D  l/ n* \4 G
    <dependency>- k' S( J. c9 F; F6 }. C" [
      <groupId>redis.clients</groupId>* m3 s5 ^8 Z* t
      <artifactId>jedis</artifactId>
    * h2 U) Z  l" o2 i, i) D3 J) D</dependency>
    3 Z1 |4 D4 l! r9 U9 ^2.2: 在springboot中配置redis
    ; y& g9 ~5 q0 t* ^5 |; U7 g1 _# u& V+ S
    import org.springframework.beans.factory.annotation.Autowired;
    ( p, l0 R* }: d* }# d* Rimport org.springframework.context.annotation.Bean;
    % j$ T+ f# v9 a& D- Rimport org.springframework.context.annotation.Configuration;
    ' r: R  v1 N- y+ t8 A5 k9 timport org.springframework.data.redis.core.RedisTemplate;
    , g& D) L, r: U  P5 q3 N+ yimport org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    - n/ E5 }7 p) F; o- gimport org.springframework.data.redis.serializer.StringRedisSerializer;
    ' L" v3 n( Z( c0 b/ u8 K! Y! P6 [$ u, @& z. R7 ^
    @Configuration$ N: y) z) `& h. g2 P
    public class RedisConfig {: @7 ~/ T, G+ ~2 T
      D1 X, V" x  j  O) {& G! V
        @Autowired
    . p% E1 j; u0 H0 C    private RedisTemplate redisTemplate;
    * x0 m5 V) A. w3 B9 c6 `
    . b- t6 w9 F: y    /**
    : A2 w6 w8 k8 T9 _; U+ C2 _) y; e/ p     * redisTemplate实例化5 J6 m" `; t1 P) F
         */ j5 t8 a( U) J6 F7 o/ e
         * @return0 D6 E5 Y% U2 l5 \1 U
         */
    ) I0 ]! y! E! g! V  F    @Bean* o  Q$ A9 _8 E* q
        public RedisTemplate redisTemplateInit() {
    9 U" a( |2 {; f* ?) R5 d- c. O7 h        //设置序列化Key的实例化对象
    9 ?- Y' P7 ^, ^# k! \1 f: ~$ n% O        redisTemplate.setKeySerializer(new StringRedisSerializer());
    6 d, k! L' Q  Q1 X2 v1 q: m        //设置序列化Value的实例化对象
    & ~; L! T/ S- S) u- Y$ m        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    8 n1 i  c4 g1 B: K$ O! x        return redisTemplate;- t; L7 [" }0 [& t' b& T
        }
    ) ^0 b+ [" ^# f. _, f9 e: H7 |- C1 {4 s/ K- v  i( X
    }# G- ~. q: k0 K7 k* i& _. i
    2.2:redisTemplate模板工具类
    9 p( p( }* r/ w
    - P4 {0 q" m' G- p& f@Component
    + V7 i  H$ j% l( I" M* S7 cpublic class RedisManager {5 L9 [" Q' J5 y* G) l$ J

    & r8 S$ O2 f) K( ~6 N. R9 |* F    private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);& |( F! s& b# G2 h: P

    - Z' r, y, f7 p* w# U    @Autowired
    % W/ p7 w2 X( d    private RedisTemplate redisTemplate;; _9 R7 [+ F9 S
    2 j' w8 }* |! G# F1 j3 @. A0 e, n
        /**
    ! r% J: `! y3 p! ?6 D/ w# D/ F     * 设置对象
    $ M" n0 c5 {5 s" c6 z. Q     *3 ^& Q4 B  @% K3 t% @+ o: Y9 e& V
         * @param key key2 u3 `/ Q1 r5 N$ }  r
         * @param value value值
    ; \/ z  _5 b: A' h% a% x& b     * @param <T> 返回值泛型
    ; c8 R$ A/ I7 Q5 D  J9 `* n     * @return 正确的值:<T> 错误的值:null
    $ J/ f5 i! n) h& t# \6 V! M     */
    ( g$ s. x; V! V    @SuppressWarnings("unchecked")
    / M  `$ e4 G7 _: K  \& p    public <T> ValueOperations<String, T> setObject(final String key, final T value) {- J) E3 y5 s3 j- _& S/ l, O6 G  ^
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();2 w, G! C% |* ]" S
            operation.set(key, value);
    . ?) m6 S* T7 n% k0 _& d        return operation;6 K; r, K$ U& b! v% S. b
        }* t8 f+ f" B; |8 |
    9 b, z' }% E) V( j& F
        /**
    ! i$ d" }  v4 g- Z! B& x, x# L' h     * 设置对象及失效时间 (单位:秒)# C& ]* M: {5 z. l% C& t
         *
    % u! ]+ ]  ?( Z( p: K; @# y     * @param key key
    . [9 E; _( S- E# g; @     * @param value value值4 n; d6 d& H5 t
         * @param <T> 返回值泛型) i, r6 P; \0 v+ ^; ?! ]
         * @param time 秒值4 W$ Q; i' b8 {  ~" R' I) {& L
         * @return 正确的值:<T> 错误的值:null
    2 ~- D7 ?, |  O( n, ?5 o- s! g     */
    + n0 {8 _% T5 V, l% F9 e    @SuppressWarnings("unchecked")
    # ^) X% N9 m% b4 l    public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {( i  v7 i" d4 j0 _8 T9 I- S! V$ Y
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();7 p  [- @" p0 Q) R: I% f/ O9 O
            operation.set(key, value, time, TimeUnit.SECONDS);" Z9 n4 p' I& u" |  k
            return operation;6 @0 s; j9 t9 {6 ]) A7 {
        }
    5 s/ N5 F8 T- [. x7 s9 L
    & k. \" l" H, D' y3 E  f$ O# p5 c; V7 @$ d7 y( |8 ]
        /**
    $ p( H& \1 Y  m/ N. o* R     * 设置对象及失效时间(单位:毫秒)
    " L; Z) O2 ]9 F7 K1 r     *1 i2 p/ s- ?. b( u# M: Q
         * @param key key9 q1 u7 D1 S: A: J* N( ~
         * @param value value值* z1 B2 L& u( N: E) ?. t) y
         * @param <T> 返回值泛型
    6 |- a# ]' B* Z3 y7 R     * @param time 秒值
    , |# M1 \8 u& ~' ]" j1 Q9 z     * @return 正确的值:<T> 错误的值:null
    1 @2 G, b: s5 I& C( _1 C+ [" u1 E     */
    3 S# F6 G) \7 g' q2 p    @SuppressWarnings("unchecked")
    2 m8 w0 Z+ @  B$ g, i    public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
    " M) U  X7 ?) Z5 ?        final ValueOperations<String, T> operation = redisTemplate.opsForValue();' ?3 c& X$ A1 R0 y
            operation.set(key, value, time, TimeUnit.MILLISECONDS);0 l2 O$ v4 b* E
            return operation;
    1 d- [% Z; b$ e8 o2 d2 F* Q$ R    }
    " ~$ N) X2 d1 b9 S: O
    * k. m6 s6 \, Z8 J    /**
    ' b) t6 x$ x; _8 Y     * 获取对象! p+ ]$ l  S3 h
         *( e* K) [3 E3 ?# g
         * @param key 键5 P5 M( i+ D5 @  N& [2 Z/ p, k
         * @return 正确的值:Object值对象<br>( g2 C4 {. v! c; G. U
         * 错误的值:null4 Z! a3 E% y3 M4 I6 z' K
         */: K6 ~" s8 h9 b( E
        @SuppressWarnings("unchecked"); W, r' b# C: r  O- z/ ^3 T
        public Object getObject(final String key) {
    : L  ~$ w( C% P8 a$ J; O        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();% i' _; l6 q; E" S# B: S
            if (valueOperations == null || !redisTemplate.hasKey(key)) {
      t! n& d, o1 z- o8 h" `            return null;
    * S+ X6 Q. W3 w. l! r8 `- {        }: M% n2 ~+ ]  a! Y  ]6 a
            final Object object = valueOperations.get(key);& `0 n" Q1 }" ?% \. p: G( e$ u6 A4 P
            return object;4 g% a5 ?/ M/ A
        }
    - ^" V# W) I9 Y4 H/ ~$ U# x5 ^# m. A  [: I$ l5 L" g, d
        /**
    ; t% d  F8 b; G  L( Y     * 从缓存中获取string值$ r& N- Y! i/ M/ m
         *
    : k# ^8 A$ }4 k     * @param key$ e* K( v/ [3 s$ _* u) U
         * @return*/$ f6 h7 F8 ~" I/ J$ r
        @SuppressWarnings("unchecked")! f+ w  a# L6 \
        public String getString(final String key) {# S/ ]5 b" R; a' y- p  `( ?
            String value = "";! O/ e) ^* _4 a) i$ H9 @
            final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();" X# _" G8 W6 N
            if (valueOperations != null && redisTemplate.hasKey(key)) {
    # s! G1 _# o0 J            final Object object = valueOperations.get(key);
    9 z& k$ G9 h; n3 U, _            if (null != object) {
    9 y; |! J4 r0 p) d                LOGGER.info("--getString--object not empty");: l/ O1 A8 a) V8 j/ y
                    value = object.toString();
    8 z' m5 W, z! A* }$ l$ i            } else {0 t. s2 V' ~" `( }
                    LOGGER.info("--getString--object empty");
    + p; S6 n% L1 I' f            }
    3 c1 q9 a8 D: z4 [2 [( ^6 ]        }9 Z  h" u2 I# h4 f
            return value;
    4 }3 A1 s; n9 A5 U- t+ m9 Z    }
    5 ~  J. n; w4 `" _) N2.2:在redis中实现时间控制
    . j- F/ ~  |, c/ M, s
    3 T1 q) f6 l8 H# c/ H! L5 V2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。+ D) p3 L, n$ Z4 i, b8 [7 m; R# @
    ; C( K3 F5 G% s0 B3 v5 D
    import com.youjia.orders.redis.RedisManager;
    1 G% w; P. @' R4 Z1 ^import org.junit.Test;
    9 Z! n" r  F* x& H# y  B, {/ i1 pimport org.springframework.beans.factory.annotation.Autowired;
    / U& _  O9 P5 q, z. t
    9 z# c: N9 J# `2 G+ S0 K. O7 M; aimport java.util.Objects;3 s" R8 _9 s9 n8 r9 M
    , G' B/ t2 x5 A; C  c( S
    /**/ t7 K9 @4 B  d6 o) i
    * @Auther: Yrion- u4 x/ p0 b1 \, b6 X, x
    * @Date: 2019-01-11 23:360 Q" ?8 l  }3 v
    */: V, ^$ P( s2 G2 V- ]$ b7 D
    ( K- z: _" ^  M
    public class RedisTest extends OrderProviderApplicationTests {
    % H5 a) q5 M1 b' `9 v: n  v6 u" I% S* a6 A& e1 O! ?" g6 S
        @Autowired
    6 t+ N# t- t/ ^/ \# s6 E. [    private RedisManager redisManager;! A' G8 a( B" ]7 K

    0 A! O# l8 W! R2 k( C; T    @Test% t. ^0 ]3 b0 B0 _* D* K, i8 i
        public void test() {
    0 [: ~& X* G; L! n        controlTime("10000001", 10L);: x4 p3 L( w" _: v4 x( \
        }
    7 T' E/ \" J; b9 I0 M) ^( F" F* M5 J6 w5 f* v
        public void controlTime(String requestId, Long timeOut) {
    0 E: g( _$ P$ H  C
    ! a% O3 l  r/ F  x  Y, t  o/ l4 ?. o        if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
      n7 {0 R: _( U" J" J            return;
    4 p$ [# q2 V. E3 g9 [        }9 b& z6 }% ^. W8 z, b, Z/ l3 }3 I' H
            //something code
    , a& w8 R% J- _% ]3 _        final String value = "value";
    % o% B8 _$ T* Z* l7 F        redisManager.setObject(requestId, value, timeOut);
      x" |) p& {. s        final long startTime = System.currentTimeMillis();/ e1 u3 d4 n. ?1 _
            System.out.println("开始控制时间");8 b# Z5 r3 B* {  J0 y% l8 N5 K
            //start. h/ a: q8 U4 ]9 o' J0 G
            for (; ; ) {
    1 `3 u; n1 D3 O' D            if (Objects.isNull(redisManager.getObject(requestId))) {
    / l5 t+ {0 J. L# A0 Y" e                break;$ M7 ]; I2 J7 O. `9 O- Y% v' k
                }
    ! O2 f+ s6 G. l0 ]) K& w# a        }( S. U2 Y) a! t, q
            final long endTime = System.currentTimeMillis();+ V& R. c+ P7 b" V* I+ F# _
    , \  K7 u( b6 h
            final long useTime = endTime - startTime;' H. V  `3 j/ r* ]1 k" n0 L
      o' Z7 ?2 {8 o9 C* N
            System.out.println("一共耗费时间:" + useTime);
    6 }' v6 s* N8 K. U    }
    " v. W& R4 C1 `}% X# G" V! v% F- k6 Q$ \/ I. J1 F. N2 t
    outPut:8 v) t4 G  j. x6 e6 R

    4 Q' Q+ K' l" X0 d3 `! X; ?( D6 O开始控制时间3 Q( A& ^" A. L! A* o- `1 t& |
    一共耗费时间:10042- B3 d" Y% t9 H
    三:总结
    # H4 T$ S$ Z" h% g1 c4 K' R" M7 S9 E0 w1 s: c
    本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!
    5 E# w+ @. `5 V3 S. c! o$ h6 u. S. x————————————————& M: D; O  D3 |6 d6 \7 [
    版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    ; s2 X1 v. I$ `  [原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325) x8 }. b, }) k8 m
    % t; A" T6 a: L, D7 n* u8 F

    6 ^. Q& q' J. T1 G# a$ |7 ]
    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-8-5 09:11 , Processed in 0.439518 second(s), 57 queries .

    回顶部