QQ登录

只需要一步,快速开始

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

    , Y+ k+ w# ^" }2 t" z) g, dJava如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
    5 k, S+ }- q' c* L% B) w0 v  n$ y9 ]  o# _
    一:时间控制的几种方案  i' H0 v" g/ b# H( S( h
    $ T8 o0 {7 g; M  o3 C, B
    1.1: 从线程方面解决
    ) i8 o3 k% U# x! Z2 R
    6 h& C& v/ i& O8 p2 z' E; K最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
    7 Z7 O( }; ~; q% @8 g5 X! u
    ) Y0 M! G3 y7 R/ x1.2:使用Timer
      ?& H8 U  K7 M4 c% {: v: l, i; u8 b, B' C9 i
    查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:  H' n+ ]' q! Z5 x/ S" w- y) o; D

    8 V! V: D. Z" v, j3 }/ Spublic class TimmerTest {
    - x2 {" {/ g% R4 j   /**) N$ R7 k$ E- d8 n2 A) h2 R1 h+ C
         * 测试方法
    5 X0 `0 g& a& z% F     */
    6 J. A6 l5 Q8 l' L! N, \* F( R4 d. U  O    public void test() {; G; f. ?! L; @, b2 a( j
            Timer timer = new Timer();+ M) t: `9 V3 d! R1 N+ L( ?* j5 p
            timer.schedule(new MyTask(), 800);
    " |' O, r% @: w8 z  R! _; i; f    }9 L0 ?* M# ?8 v

    ; v' A7 \7 u; O, U3 F% u; P6 k" g    public class MyTask extends TimerTask {
      z; e/ S! k4 Y& l" F/ B& e! z* q* `2 u
    ) i) p. N( o: A) [        /**% p! C8 A) c( i- c( D
             * 运行方法
    ! ~- }5 ~. x0 Y) _' ^( x. Y! E& Z         */
    , E/ }1 a* M4 ?0 q$ I        @Override
    8 z8 @0 P. E0 i* o        public void run() {% M6 s* R; Q( [0 U" g3 h# ]6 Q
                System.out.println("输出");: U7 j& |1 g* B
            }9 D9 ?8 W; E6 w: Y& A" s2 J
        }
    8 h" S3 a( v9 s7 T* g& M2 j6 o9 v7 m}# M8 ^) Y- u' ?% [& z' p
    这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
    , A/ m- z- g3 C* z& n; i7 W6 J+ U7 l# s6 |6 |  k7 K
    1.3:redis延时9 S5 D6 i. H) S% C* Z+ K

    ( b% M. S9 u1 J, e& n0 J在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:2 C$ e' X, Z3 o. \6 j
    ) Z* G" Q# n6 `- F

    3 a6 g8 }& K3 X- @' E0 c+ Z* n9 ^. @+ o; j& i; \3 O
    通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:
    4 P! f& A3 E' U1 W6 R4 f, U
    ' G9 p7 d7 U3 d3 z1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
      _) L% _7 s6 ^
    6 l5 B% s+ @) `, M6 A' D4 k2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现' q6 k5 @0 T! ?0 I& g) y

    * q9 D# O$ F1 {& U# G3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
    . p$ m, o3 i1 V8 K9 `. v
    , Y; g) f! a1 w, }4 W0 A二:redis
      v* k% D% v; M- W$ F! s9 l# i9 ]0 v6 a* N( m- E4 k& d
    2.1:maven中引入redis) J8 ^- l4 y  N3 o5 y1 h9 d
    6 \9 @* W: z& {5 w
    引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。  E- g7 i8 G: w6 y, @/ `
    8 S9 o( y, G8 j2 L" M
    <dependency>
    : ^& M: p' x- \5 H0 n6 C  <groupId>org.springframework.boot</groupId>2 f' G2 ], D3 x' G4 W, Q9 X
        <artifactId>spring-boot-starter-data-redis</artifactId>
    + M: {7 E6 s% z6 y      <exclusions>
    5 O  `2 w3 R4 J5 z! y0 d        <exclusion>
    : H; _& W" H, s8 x, O% L          <groupId>io.lettuce</groupId>
    + z: U$ g" {, u4 o6 {  t+ {          <artifactId>lettuce-core</artifactId>3 H% n" B2 t  k: _
            </exclusion>
      G8 }; V8 f1 p+ k( O      </exclusions>8 Q! @& Z% a$ s
    </dependency>
    4 I% T+ I+ S; x3 |* P2 x/ x: x% T<dependency>
    . D5 a+ O" i) P# K: t% r  <groupId>redis.clients</groupId>- h5 E, t$ u8 N5 F+ t  A% \6 d
      <artifactId>jedis</artifactId>$ d' s2 a( q5 B1 ?2 I$ ?
    </dependency>4 E, V; {% x7 ~+ s
    2.2: 在springboot中配置redis7 b: r, q. {+ }8 j4 F
      C" }$ o4 \# R. z
    import org.springframework.beans.factory.annotation.Autowired;2 u+ z4 L0 L# p, z) ?9 y
    import org.springframework.context.annotation.Bean;
    ' w7 h8 @2 i9 ]8 c3 d" Cimport org.springframework.context.annotation.Configuration;
    , A) t2 s: u  r: j! _3 I# nimport org.springframework.data.redis.core.RedisTemplate;+ F. X$ B7 |. s, Z9 q( c$ P
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;9 y; L- t5 p0 J
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    : u/ H3 |- [5 j4 U5 q' K( k
    " Y: I4 X2 `& ~  Y  H; }@Configuration
    . w% i# i1 C% U- _6 b- W  `public class RedisConfig {+ y2 |/ j0 U- x9 Z! k9 @

    $ M1 A8 `8 ~. G, X    @Autowired( X, l9 C9 f7 b9 V
        private RedisTemplate redisTemplate;
    ) {. f, r; w& I# B9 t" X% a
    + U4 o7 K  K6 P. D: x    /**3 w. N0 i8 H' e8 h' P% C+ k
         * redisTemplate实例化8 i' I' X+ \* W6 M3 N
         *
    + @' Y6 M! F* h' I0 p     * @return0 k3 _8 p$ U& b$ b/ b0 r
         */' s+ z3 ?- N( q/ ?+ e1 B
        @Bean$ X+ h0 D5 n& T5 P( e* C" y
        public RedisTemplate redisTemplateInit() {. |; \3 y) b& w1 `1 d/ d* ?
            //设置序列化Key的实例化对象
    % \/ ~- c) h! o4 r' h" X1 o        redisTemplate.setKeySerializer(new StringRedisSerializer());
    4 z& x3 R6 x. Y3 R" F( z4 j# C* o$ ?        //设置序列化Value的实例化对象
    8 U' y2 f! Q- }- [9 B        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());' Z. M4 T0 ]) P% O6 Y- S' ?
            return redisTemplate;
    : I  {4 X) L. ?+ K6 C" h: y    }- M: \! Y: Z& N# y  O

    9 g; v9 U' r# U  Z, b3 |}
    5 ?5 Q3 N  z7 F3 R) p/ p( I/ X) w2.2:redisTemplate模板工具类3 K9 }8 X) E% W3 Z

    * k2 A/ o9 K) ?* e" O6 Y! r@Component
    : D% U0 z* K: {+ M/ B  ^& Zpublic class RedisManager {
    . X/ d/ J: H5 L- j' z  X& Q/ e1 O: G
        private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);
    * F6 L% n  @/ n$ `6 l2 \7 I
    / ?, e! I- k* a# `8 }2 j/ `4 ^    @Autowired
    6 E6 _+ A0 w" b+ t    private RedisTemplate redisTemplate;
    ( P" e% }: v7 y9 q  a
    ' U2 l7 t  Z' M7 ]( J5 c; Y( M" ]3 a    /**
    % j! k7 k1 \$ M: H     * 设置对象
    ! V/ h' a8 P- A  k; t  \     *9 Y4 L7 G9 D2 c( g- ?4 x
         * @param key key
    0 C! j' R$ m$ m( m" d     * @param value value值9 c/ n, U. ?, r
         * @param <T> 返回值泛型4 K+ {2 t" l" {" M3 a* T7 o% I
         * @return 正确的值:<T> 错误的值:null
    + S2 r* Q; Z3 r) q5 ?8 ^. }, Y; C" v     */  U: i* |: W, x" f, k5 e; u
        @SuppressWarnings("unchecked")9 ]& j& _" M8 ]# P- X  q. ]
        public <T> ValueOperations<String, T> setObject(final String key, final T value) {' [5 S- y! S! G: \
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();; @8 e+ e, j, Z
            operation.set(key, value);- E# I! L$ d/ Y1 j8 ^) S  K0 L; }& P
            return operation;" n; w: h- H& t  ]1 h( C
        }
    * j8 Y+ |( F5 j5 J+ q
    " }6 ?& N/ u$ i- ^! t) i    /**
    ) }1 m$ l8 d$ C( O) x0 o     * 设置对象及失效时间 (单位:秒)
    , [- b1 Z7 L; Z     *
    ) ~' [( o! Y) B  G  r     * @param key key7 K: _  B3 k+ P3 I3 p0 O
         * @param value value值
    # U) c6 [8 f3 c6 ?0 J, V) B9 ^% s     * @param <T> 返回值泛型
    5 O2 P$ s: L8 z1 n: s7 Z* }; E     * @param time 秒值2 f9 X; v5 w* x7 I6 r
         * @return 正确的值:<T> 错误的值:null  B8 A8 f9 C9 X3 x
         */  g0 Q7 ^8 r0 Z; E
        @SuppressWarnings("unchecked")# I8 V' ~% W% H; F9 A4 u
        public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
    3 _- {% \1 f% z- Q% X9 }: F' {1 V0 a        final ValueOperations<String, T> operation = redisTemplate.opsForValue();6 ]( F( o& n2 K# U- ~4 G; C
            operation.set(key, value, time, TimeUnit.SECONDS);( {: y% ~6 ?! U5 d) o
            return operation;, {. T1 }& ?4 q) Q" a
        }
    ' d! A8 j. r0 F( f8 U8 g5 w0 [7 Y. N/ q

    ( a& N" t- k$ {    /**
    . y( j' H" A( x# i, B( o     * 设置对象及失效时间(单位:毫秒)
    " z; i" x! P' y2 T' M; ?' E2 t7 ~     *
    2 j' b4 K( K$ M' I9 ~. u9 i3 j     * @param key key
    0 ?  |- w5 x8 H+ ~! l" \     * @param value value值* }2 @  a+ H& I: `2 U* e
         * @param <T> 返回值泛型1 X$ G( ^) x( t0 E' ~' V
         * @param time 秒值* ~/ K; |% p! J+ s% t+ x2 v0 q
         * @return 正确的值:<T> 错误的值:null
    & K' y! N- ?+ [: q* p8 F; k8 U     */
    ' I' }$ ?5 s# s. a7 `9 c    @SuppressWarnings("unchecked")
      O, g4 t) C- ~    public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
    " m5 J) ]" d; {6 q0 _        final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    8 U3 b4 D* y- w" I: e        operation.set(key, value, time, TimeUnit.MILLISECONDS);  N1 i: v9 N2 o& R& v
            return operation;6 v2 N' S. c0 J" |0 i
        }  s3 I3 o8 Z' ]% |4 l

    $ |5 L: u) H6 [) c! T; k; L/ g1 l    /**- j0 t7 V6 f# h+ y/ x, i* o4 E
         * 获取对象! y7 `4 u7 v4 f" j. w' |1 a2 z6 ?
         *' H. N1 N4 c# P. |# c
         * @param key 键0 \  n5 K/ `. \/ B
         * @return 正确的值:Object值对象<br>
    " ]8 `) D, ]7 J! a4 N     * 错误的值:null6 k) ]  F4 J7 z3 v2 a
         */$ @  H, E2 d! z
        @SuppressWarnings("unchecked")2 a! X" F0 h2 F: o0 q. Q
        public Object getObject(final String key) {7 e, P( v6 p: Y$ l, p# Y- k
            final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();! J/ D' w* @; S; O3 _- R# g9 K' a( [, W3 g
            if (valueOperations == null || !redisTemplate.hasKey(key)) {
    " j+ }3 Y. r& J  [1 k2 T            return null;
    ' ?0 }; f  {1 y, T$ ~8 W9 A        }
    # g1 C1 n7 p: u2 z. X" L5 D        final Object object = valueOperations.get(key);
    * p! J% I' J; f        return object;) s, d9 M* C) k  ?
        }
    ( D: Y% R7 z) t1 P7 g3 K; X  O
    . \0 {6 a8 d* h0 m( b    /**
    : D7 S8 o8 g; I$ f0 M8 t, Y     * 从缓存中获取string值7 j; D+ F8 E5 Y5 _& L/ {9 e
         *7 M- {4 ~0 w" V0 N# y
         * @param key0 ?& Q+ w6 ?, ?+ k  ~
         * @return*/- f3 g5 e5 V8 k1 p  e! `) [
        @SuppressWarnings("unchecked")
    3 ~$ E' h- M4 }7 [) Q: z) k    public String getString(final String key) {
    . ?4 X& g0 ^% k0 |        String value = "";
    / _/ }- `9 t2 E8 l. l4 Q  |9 I" }$ q7 e3 I        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();3 e, ~' {' o+ G" F: |- r1 u
            if (valueOperations != null && redisTemplate.hasKey(key)) {7 P" z' P1 O" q/ G6 F% ?
                final Object object = valueOperations.get(key);3 K! ~# T3 p9 ]. h" u4 d. u* b4 e
                if (null != object) {
    3 X" B; X3 x7 c4 a5 z; ^/ q% ~7 b                LOGGER.info("--getString--object not empty");
    " P% S& a! X: P                value = object.toString();
      ^4 V9 a" G. w1 U% ~# g            } else {6 V) g9 K1 E  N
                    LOGGER.info("--getString--object empty");
    6 t  W1 D. n# I; Z0 I9 X6 M            }* k  |, T8 p2 v5 `! C
            }& s& E6 T/ C; \" u# L% a
            return value;
    ; y6 O$ q9 J8 u6 O6 E& u& d    }
    3 M# G4 X! o6 ]$ v7 v% q" x+ K2.2:在redis中实现时间控制
    # o3 R: W' K/ K# r0 u' ~/ N6 Z- Z6 ~2 e1 `
    2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。- Q5 ?1 X; W; x7 E0 r

    * _" P6 @9 D6 u/ {$ zimport com.youjia.orders.redis.RedisManager;% F7 A% R1 h& L; h; z
    import org.junit.Test;- I% g9 X$ W2 b& d0 i. l" Y
    import org.springframework.beans.factory.annotation.Autowired;* X) _% R9 }/ J, `
    6 T. X  f- Z! e/ |1 Y' Y0 e7 M
    import java.util.Objects;) D  L$ Q# H1 ]+ K+ U4 ]! g* o0 ?

    ( c8 M+ e. U) j6 z( n% ~/**# c0 ~( H* p( t# q, B, p
    * @Auther: Yrion
    ' a+ f7 h: L4 L% z+ _6 E * @Date: 2019-01-11 23:36
    $ r, o  u" \/ v */
      U( k5 L  Y$ m4 f) @' L. a, I. \7 k' g- [
    public class RedisTest extends OrderProviderApplicationTests {
    & ^: S0 Y0 s/ @) }6 Y6 b! M$ t0 ?- M* `3 G8 U; i/ Z2 }- n
        @Autowired
    - L3 K) E, J) Q    private RedisManager redisManager;# T" n( U/ B$ \5 A- L

    * u" p1 J5 i4 p  f- a% k) e+ J% y/ S4 l. _    @Test& J% N1 d1 h0 @
        public void test() {0 F1 M: d: q  S( h/ e
            controlTime("10000001", 10L);
    7 A4 C) y+ p/ r: z+ G) w0 w    }* t2 r3 c2 Y0 d, o5 R, Z

    7 g* J$ X2 s+ f& [3 H( W    public void controlTime(String requestId, Long timeOut) {. L" S& H, B3 O5 p8 k
    / ?8 [! ]8 C: g$ b( k/ P1 ?; E
            if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {, w5 y$ D! P8 n# ^( k) X! u' o: d
                return;
    0 S7 Y6 I6 l, g8 l+ |. d        }
    - c% T- n) e! ^. E3 a* U4 D6 t' Z        //something code$ L" r+ t- O* `" ~* x
            final String value = "value";
    ! E$ {9 g4 O6 \# L1 X2 ?: h5 b        redisManager.setObject(requestId, value, timeOut);6 Q+ Z# y$ w/ r0 q
            final long startTime = System.currentTimeMillis();
    . c2 o8 m. _2 w: T  p) n$ Z        System.out.println("开始控制时间");" b- C0 b( P" z- y
            //start
    9 `" N# \0 |2 A0 G5 l1 u        for (; ; ) {
    9 i6 P/ O( A' v2 ]* D            if (Objects.isNull(redisManager.getObject(requestId))) {
    * `( S% Z0 `( c2 y0 ?) E                break;
    1 [" h+ [! P- u" L9 |( m( I            }
    ) n, T4 v7 P+ P& R# l1 M        }0 i3 x; K5 R, b# N$ W/ \
            final long endTime = System.currentTimeMillis();7 t5 j* M! ^% o) _0 m

    ( F, B  A' n" @: \7 u. ~1 h& S/ ~        final long useTime = endTime - startTime;
    / S4 [  V6 V1 U" C# A: f
    7 o9 B2 ?" L9 x" O        System.out.println("一共耗费时间:" + useTime);8 M9 a9 s; R# S: F3 s; c
        }
    ) \8 P$ Z2 Y9 {3 x' W' n6 }% |}
    2 t" g  _/ _1 t- ]) @outPut:7 ~! d5 ~1 ?' l, `$ G$ w& e7 X, ~
    - X+ d( l' m" V% `
    开始控制时间
    8 e  C0 k+ z6 c5 F- @& @9 I! k1 q0 j+ C一共耗费时间:10042/ }- C( W7 @& `- E" W& E5 T+ a% @
    三:总结2 N- S  H2 P4 e/ u# k8 E
    & r6 |4 _. b/ S* [- s% e) X$ d$ m
    本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!/ [9 ^1 j1 ]: W3 W
    ————————————————5 O; D- o' {3 g
    版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    : s' X& S# e5 e  ?/ \; w4 X原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325: Y$ n) N) z' _+ @9 z7 Q1 o5 V- u) V

    . e7 Y( f) o  W6 M# L) I3 E2 A& a+ S
    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-6-17 19:52 , Processed in 0.514462 second(s), 56 queries .

    回顶部