QQ登录

只需要一步,快速开始

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

    # {8 I/ g. L8 M7 P  A! PJava如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
    * J/ D; K& Y- I8 J% o- n& Q
    - C: c  m+ J0 S一:时间控制的几种方案
    2 R+ _/ w' p, ~# U& `5 ?
    8 r. h7 {  w0 v. _2 j& f  D- k1.1: 从线程方面解决+ A+ J" w+ _8 A* W
    5 P* x& G& |+ S& p5 _+ a. I
    最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
    $ t% d* n5 X0 l, J) [- r5 V  L
    8 \2 A; U- O( f6 Z1 x1.2:使用Timer: p) I3 q4 b9 }- w

    . d& }, B( O& D, ^查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:4 Y9 `* k, m& \, k  S6 b

    6 m% G. J: B% ~, j' I# X  p1 hpublic class TimmerTest {2 J. `- P  T5 ~1 [
       /**3 A' g4 M( a/ C' |5 z+ I2 r1 T
         * 测试方法
    5 L0 x* A' [9 B" Y     */2 L6 w9 T( T# r5 X+ @: B: ]
        public void test() {/ h! [+ e1 x( U' D3 a. B1 o
            Timer timer = new Timer();
    ( O% s% G2 s) H* I3 i7 w8 t        timer.schedule(new MyTask(), 800);  v6 }( V- W# [1 E7 b) @3 d; h
        }
      ~! E8 s9 c* h: i& s; H( t7 i) @0 y- D, c/ f! j# b
        public class MyTask extends TimerTask {
    : \! ?  b( h* y5 r/ e9 q- Y+ h5 K, E# W
            /**' x. I7 f8 g' M* A
             * 运行方法
    1 w8 w5 U% u' D5 \$ E         */1 D, m3 E, P- M
            @Override5 C3 ]3 V# F/ u3 |. [
            public void run() {4 g+ m' L; q- H. g
                System.out.println("输出");0 W; y% d8 v; c  E# E
            }0 @5 N) l0 z; V- e6 w- p
        }
    6 b" J7 g' i0 [' }: \# p$ I}- t0 e7 [3 u2 ^) s
    这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
    7 y4 f; {& z$ P% Z+ h
    ' ~* m: u9 P$ e- S+ `( S& W1.3:redis延时5 a0 ?& N% Z- e; [+ e
    : q5 x3 h+ ^+ G2 }: g: }
    在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
    ' I) A! h8 M( [" q
    ! x* B' }% r  k4 s' j( M0 g8 B$ O% A4 Y; s# Q
    ' I5 r7 U3 \; o, B2 G, Z
    通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:* ?7 [/ @" V" f! _' u  h1 X7 ]4 g' |2 U
    1 q+ r+ y& ?' q3 M) ?6 Y
    1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制% }* a4 N: ]  V
    1 O* k$ o5 Y8 K! L- P) O" s8 Z" Q
    2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
    * B8 i) G; Q, ]4 ?! q% {. D+ A# }
    3:简单,真正的代码实现起来只有很少,下面会给出代码示范。; i8 p! U& ~0 v- Z- Q) F
    3 D5 ^0 E! g! c; M' g. S$ f2 A
    二:redis
    9 Z4 ^+ X6 S, B* T
    ( Q; f0 d& a- c& Y2.1:maven中引入redis0 D! R- ^% S* b( Y6 B, ?

    * C( \" s' J, T9 ^引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
    0 y5 W/ b" O" ]% S) i  U8 V1 |0 J2 E* W2 @3 o1 c; M
    <dependency>
    2 E, z! n; {- [+ }- E& n8 Q6 x  <groupId>org.springframework.boot</groupId>
    6 p2 n* o& U* X& p, `7 i    <artifactId>spring-boot-starter-data-redis</artifactId>, u& i. q& u' G4 J3 s7 k1 o% n
          <exclusions>
    9 q: ~! q" C7 j7 I7 G& R        <exclusion>
    2 _6 z' w& o2 T          <groupId>io.lettuce</groupId>0 w: L( b& A" v* m
              <artifactId>lettuce-core</artifactId>' P7 l4 e) G9 {' `' ^; u, G
            </exclusion>  \. f% W/ d+ p. h
          </exclusions>0 a) F1 ?3 C" ~
    </dependency>
    % H6 r6 Y& t& |. t<dependency>
    % h, m, t: \- _; V% f  <groupId>redis.clients</groupId>
    % w' e8 h+ W0 U3 H) m. {' c) b  <artifactId>jedis</artifactId>' I% [8 A  F! L9 y# }
    </dependency>
    0 j) \- g; L( J7 x% v; K% ~2.2: 在springboot中配置redis1 S# K. d3 @7 \$ e! h" q) K; W

    . X5 N( X  H6 i  G! O& kimport org.springframework.beans.factory.annotation.Autowired;" k4 d5 t8 k9 ?. n$ i% d( G* o
    import org.springframework.context.annotation.Bean;- [* }3 U. c' ~+ I
    import org.springframework.context.annotation.Configuration;
    9 \0 n  ]9 ?: }# A6 iimport org.springframework.data.redis.core.RedisTemplate;: m9 }" N  {5 G+ D8 C0 n2 N; h0 ~
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    , z6 |; b2 e% Fimport org.springframework.data.redis.serializer.StringRedisSerializer;7 \7 r, O1 f( K+ S) S  w0 z
    1 E% w- V( [- r& f
    @Configuration
    6 X+ b( Z+ L1 ^  fpublic class RedisConfig {1 B# w# [$ g$ V
    / q, B0 t: C9 L) q$ u& ]+ |9 k
        @Autowired5 E' I8 S" k6 O4 @
        private RedisTemplate redisTemplate;
    7 E( h9 [( i* a' t( V0 a+ N
    4 W2 _% u* J/ A4 |  b    /**% `4 g! c& ?8 m
         * redisTemplate实例化+ ~4 q; N$ s- u: ^4 _
         *% ?# c9 i) x% |. c( R4 x' g: h
         * @return; h+ n5 p' ?' l% e
         */0 U' W. D  Y( X$ j1 ]- L- m
        @Bean
    , @+ w0 }2 @* J1 u. F    public RedisTemplate redisTemplateInit() {
    * x5 V/ _: P5 V9 d$ L        //设置序列化Key的实例化对象
    * B$ r7 B( I3 l3 V  G% _  ^' V        redisTemplate.setKeySerializer(new StringRedisSerializer());6 G2 m/ k" Y) j8 b6 }  @* Z
            //设置序列化Value的实例化对象
    / Q( n) j7 K. ~" c        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());/ g: Y- \: ~( d' S
            return redisTemplate;9 G/ d  w1 H5 I; i
        }
    % I! _4 {. N1 a: o" s. F7 N  S
    + e; S9 o; x, x6 `, Y* D}! I- m& G( O$ Q0 I4 R7 T& i' p
    2.2:redisTemplate模板工具类
    : A8 ?/ r4 H% d2 e' [
    7 i1 L4 R* f- j" Z* M: Q0 U% B. f/ P@Component
    0 Q4 W3 M9 k. h" Tpublic class RedisManager {1 ]. u( B, W. G
    ( W" P9 D$ K6 L1 o
        private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);
    - L; @7 |3 y: m2 _3 S# h4 {' A! i8 g
        @Autowired
    # p0 g) `7 M; T% v" ?" K    private RedisTemplate redisTemplate;
    . R$ {* A0 q* m/ Q& ~( U& I! q  ?+ |, X5 |& d
        /**2 m) T! ?; J7 s) W% r5 ]9 w
         * 设置对象2 g# j$ V, z' K
         *4 s  `1 @7 x& j( l5 t& o
         * @param key key
    ' y) R4 o. u) O5 @' y5 T) e7 B6 n' A     * @param value value值) Y1 d! p( c+ P. ]. L& a
         * @param <T> 返回值泛型9 G6 d* w8 C2 j
         * @return 正确的值:<T> 错误的值:null
    + S8 w. q! N( |- `- |5 J) o     */
      N+ k9 R0 c* S& A8 g& f    @SuppressWarnings("unchecked")
    4 K& A+ M9 Q  x+ j3 Y  y) p    public <T> ValueOperations<String, T> setObject(final String key, final T value) {
    6 m% }; H; T* K- E8 _        final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    ( z( y7 D$ [& ?1 l2 U9 d6 G5 P        operation.set(key, value);* r* |4 M# P8 K
            return operation;: o  R; u, q' |2 S' s2 w
        }
    8 e) f$ r! R7 D2 i& F1 K- z3 K7 {4 f& C1 e
        /**- k" {' X9 O4 Z) C" c6 |2 y
         * 设置对象及失效时间 (单位:秒)
    4 `' z- f& j; A( W) s     *
    $ g" X8 j( j8 I& F     * @param key key8 L" O$ k3 J9 ?: u9 @
         * @param value value值6 G1 M! k4 x5 Z7 L
         * @param <T> 返回值泛型
    . ]1 K/ C0 Y% \     * @param time 秒值% N' Y9 B0 U% b" e! c
         * @return 正确的值:<T> 错误的值:null  E1 e9 q# D1 C( N5 [/ S
         */
    - v* \& V+ O+ \1 L, w. C    @SuppressWarnings("unchecked")
    $ \5 }/ E& {% [5 q2 b  q9 g    public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
    - }0 C( }# }+ d! B        final ValueOperations<String, T> operation = redisTemplate.opsForValue();& u5 K. N( ]: L5 O5 Z: _
            operation.set(key, value, time, TimeUnit.SECONDS);
    6 t, y$ R; \3 e  G) y, d! [0 Q        return operation;
    4 H& E9 |" {0 I, J& Z4 J( ]    }( w8 q* I# ~- Q" F$ k7 \0 `" a
    5 ?* P9 Q9 ^9 J' e' U

    - P6 i4 Q/ a8 v$ {; y& K    /**
    8 Z- c; O8 z* a2 |  @" P- ~     * 设置对象及失效时间(单位:毫秒)
    2 u5 ^$ M: N' ~+ [$ g     *6 D# `* q8 Z8 D% g/ @& I
         * @param key key* R7 B- q. c# w4 Y6 p$ L; W
         * @param value value值
    1 W" q4 m' v3 n4 O4 U( d8 z     * @param <T> 返回值泛型  V' u# Z; R' O1 F
         * @param time 秒值2 c, `, \4 @# S9 s/ v# Z: W  q
         * @return 正确的值:<T> 错误的值:null
    ) h- e/ ?3 h! I4 t1 Y     */1 g/ w: N1 `( a# Y+ x
        @SuppressWarnings("unchecked")
    " C& [& {* C& ?+ r8 ?& S6 R    public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {% {+ O1 @( @1 T' w, o1 l' T
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    4 W* `% `5 r: B& h  W        operation.set(key, value, time, TimeUnit.MILLISECONDS);
    5 {0 \! A, Z4 F" Z        return operation;/ k/ d9 \; T7 D0 ^; s. n" P) K
        }5 n' |0 J& h. I9 `
    + z; K  ~, b* R# J% Y' O8 a
        /**
    , x  ~% j6 a0 b& O. J0 y     * 获取对象) k; Z! @0 u, K( `: f& W
         *
    9 V$ Z. P( X7 p& T( L5 u- m     * @param key 键( }+ s( p7 O. L$ {) {) f: I
         * @return 正确的值:Object值对象<br>- n6 w, T! O. d
         * 错误的值:null
    % f1 V1 Z! `0 f9 K$ i3 l     */, n8 ^& U; |- l
        @SuppressWarnings("unchecked")' D7 E% P% _- W1 d4 }  F
        public Object getObject(final String key) {
    " v$ `) O$ i! _, e: [        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    ( Q% `/ L6 j/ ?* Y7 ~. b        if (valueOperations == null || !redisTemplate.hasKey(key)) {7 W0 S1 h/ L: Y. a( i1 f% u
                return null;9 U5 C- i1 h6 m' i6 w" ]
            }
      A% D) Q  y) @0 W9 X        final Object object = valueOperations.get(key);' z8 ^6 O2 E* }8 x1 J- k
            return object;
    - I: z& @% r0 h7 F    }
    ; N  x- H9 i' q- j/ \4 v( L* t1 q& n  Z& _
        /**0 p3 s8 J" S, Z: `& s% f1 P" t  s
         * 从缓存中获取string值
    ( s) d' ^, @6 R     *
    ; g+ T) E* |2 I0 s3 S     * @param key! o( b% J0 [5 N) e
         * @return*/. e& o  [3 y* f) k! `3 u1 i
        @SuppressWarnings("unchecked"), b7 _$ [9 P5 N$ L$ j
        public String getString(final String key) {
    + j  x/ C+ J, z( i( \: l        String value = "";8 j  K) r5 k" Q4 T
            final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();, g( O$ U8 E( K7 F$ w- ?( _
            if (valueOperations != null && redisTemplate.hasKey(key)) {
    # F% F3 x: M0 p& p/ C6 i/ R            final Object object = valueOperations.get(key);6 \+ t, v* x) q. o" n
                if (null != object) {. j4 G1 F" D9 f, S
                    LOGGER.info("--getString--object not empty");
    4 i. \! t' N: \2 K- R                value = object.toString();. a2 g! N8 v# \
                } else {, [3 K' p9 ^% u4 _
                    LOGGER.info("--getString--object empty");
    ' z8 s# b) q, \" J            }$ k* J; O# \7 |/ R8 z+ C0 S$ S
            }
    ; c" U% u' ~. W        return value;
    5 \. z& n7 F# C, s3 x7 L2 j/ s    }; m1 B: c, ^. v7 `: i% b9 k: R
    2.2:在redis中实现时间控制2 n4 B2 I2 _$ p. a( A4 e

    7 }6 l; \; E6 B. b; k- h2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。: L) s: n' s1 |" }, }* ?$ F0 U" G+ Y

    / s* G' G$ T7 S% e3 zimport com.youjia.orders.redis.RedisManager;2 H: u% V, P& K
    import org.junit.Test;
    9 Y& q! m; r! `8 W, r2 ]* i5 I; pimport org.springframework.beans.factory.annotation.Autowired;4 e+ e  L) w/ c( ~# X5 j% I. i
    1 a' O* H9 E* P/ @. S) z! k
    import java.util.Objects;
    2 D7 ?* i" \3 z" t! K, `$ [- \7 E- \( i& \: p1 s
    /**8 X3 s; I+ g' b( o- A
    * @Auther: Yrion$ ~! P1 I& Q/ L6 J5 i
    * @Date: 2019-01-11 23:36
    2 z, T( F8 k8 D# a */
    0 ?: [. e8 o( X0 P, O$ `  E1 ?' z7 X/ R0 b( U9 s
    public class RedisTest extends OrderProviderApplicationTests {
    + i" ]# a1 D) L5 a. g& ^/ q$ n7 ], @9 y$ G& h
        @Autowired
    7 j1 W% `1 k& x9 `8 {& s    private RedisManager redisManager;" P! U$ j6 g3 ?$ ^4 `
    ' k2 i$ [9 V# y! l* ]
        @Test' M: x% f* i7 H5 Z: \" E1 c2 _4 E, v
        public void test() {( Y0 ^7 ?/ S# f+ F: q5 @: N
            controlTime("10000001", 10L);
    ' }" l- C. ?' N8 H+ \# [    }
    6 C) c1 l8 t: _" I6 k2 Z9 S% Y# }& m& s3 T, N" c, r: Y0 I: M) o
        public void controlTime(String requestId, Long timeOut) {
    # e: u8 U8 u- _7 B! f5 J% s/ W3 y" H/ O4 i
            if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
    ) H- Y/ d* G7 `, r% _0 o; ~+ t$ G            return;
    * b: D8 C0 O7 q  G: `( c# `        }
    8 R  B: r: h2 w) q        //something code
    8 o/ z; X7 O4 j4 p& \        final String value = "value";0 Y, ?. E8 N, C, I3 p9 L
            redisManager.setObject(requestId, value, timeOut);
      V9 k8 U) C( @) Y# _) D. F! I        final long startTime = System.currentTimeMillis();
    0 i& d/ d9 E; H0 f2 X4 r; Z8 I        System.out.println("开始控制时间");
    ; R/ D% t2 u) _        //start
    ; t; R; ]# z. K4 L* q' f        for (; ; ) {2 g! D7 M4 V; K) U! s8 I
                if (Objects.isNull(redisManager.getObject(requestId))) {1 j$ K& \1 R! [! S
                    break;
    - l$ [, u4 b3 e( a            }
    ' ]7 c3 P6 S* k. `& l        }
    3 G" Q) R+ M9 i6 m* k3 L: ]1 @        final long endTime = System.currentTimeMillis();
    . X) a. J* ?( e& e# E8 Y8 J8 \
    4 i& W2 @2 @! E. o8 z, i        final long useTime = endTime - startTime;1 \% @3 T8 G. J, ]
    8 n# z2 ^0 J2 G! G0 t- ^7 i# N! I
            System.out.println("一共耗费时间:" + useTime);
    1 u) H8 g) f4 b( o% d    }: h" W1 g! D, {' k8 X6 S
    }! H% l% K4 Z! G
    outPut:
    - E, z& j3 N! M% p- d" \4 M
    % }8 K4 p7 m$ m9 N: u8 I开始控制时间* b9 B8 I) Y+ E3 ?/ ^; K# U" j
    一共耗费时间:10042! i% m9 [4 m: [. D, L
    三:总结" a3 h  l; g- R

    8 e9 b' Z9 i$ U! p; d8 D本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!( {. ]+ c* y% S- y& u
    ————————————————
    1 x+ R* _/ S5 F9 }6 a" q  e版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。1 d, P; f7 r  v4 y8 ?
    原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325
      ?/ _- G: j1 p; `) z/ h, t( y( V

    ( l1 J" {9 e) A& n* j
    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-21 13:17 , Processed in 0.434588 second(s), 57 queries .

    回顶部