QQ登录

只需要一步,快速开始

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

    4 t+ S" Q; R; S) D  rJava如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。3 o1 ]% j, G5 r" }
    6 }4 |- [. B, \# {
    一:时间控制的几种方案
    2 G/ ^" O0 c2 `+ t  s+ A% J' u6 C. l. U" @- B! H2 Z+ Q
    1.1: 从线程方面解决
    0 A) y% u& a: R# ~. b' L" m0 G0 t5 R/ ~
    最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。) C7 a7 p6 s5 Z- }6 H6 P" u, v
    ( O5 q$ M! K( ^/ V0 K
    1.2:使用Timer& j" C9 \6 t8 s  z6 ]8 Z6 L

    " e. e& @: b! W; w查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:7 @) F7 v+ p/ O  R

    $ v4 {( N5 O7 w( `5 Z% x. hpublic class TimmerTest {
    ' P7 g' Y2 u4 K$ s% p   /**2 g8 \' ?2 a; [6 Z! ~9 L! `
         * 测试方法+ Y) {& C) S  n: [- |
         */; K2 W8 q6 s9 O5 e% _0 x6 w$ ]
        public void test() {! j% M" C* Q! n# g1 K# w: M: \, ?0 |/ O
            Timer timer = new Timer();! k/ F0 q9 e  A/ M' X) I
            timer.schedule(new MyTask(), 800);
    + f( K( i9 ]+ K) S# S    }8 H* J8 ~9 |: v8 w2 q: H. f) Y- `- t
    2 }3 j& W3 A8 |
        public class MyTask extends TimerTask {
    6 {8 x6 ^, X/ }0 p' D9 _0 l/ q) z; S( X: c) N4 F" y
            /**
    9 H+ b6 j( R+ h2 H         * 运行方法
    0 W! b& y8 F6 L# @! [/ ^         */) k5 v7 T6 @: O
            @Override8 \6 u% W: y- R
            public void run() {
    ) O- h) X0 K3 _6 l) F            System.out.println("输出");8 p: E0 K6 }3 p% _
            }
    & U2 K  a( T7 d& f& V# n. `7 H    }
    ' d- G2 g0 a# n; |5 w}
    , Z/ {( Z! X6 J  r7 M% S7 j这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
    ! Y7 H& Q$ H+ o$ j* H* U0 T( y* z  }0 e; q: z
    1.3:redis延时! \( T6 \; f  Y

    5 h* z4 s. J' t- w: i# m( e在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:9 }! y( O' X  N8 s* k1 r' g, y
    ; A9 |9 r" f: b$ F8 F" G
    % B9 B# v+ t+ `

    8 H8 B) f* t7 @) f# q通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:# T6 K( E" U0 d* [

    % p3 e" v  n* J% q+ c1 m4 u1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制# P6 G" c# F7 k1 k  }

    8 z* g0 u( W! G# ]- L; a+ V* F2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现- {) i% I  Q( V- J7 O* t. k9 T

    0 d1 o5 j9 M4 K- G2 n- @% S, `7 s) L* J3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
    4 D4 e: C  e7 |+ N6 Y& K5 a) T; Y  q- j" P$ M7 c
    二:redis
    & Q# y4 c, h6 c  _0 V. {! F( a
    4 r$ z! c! ^3 p' _8 `  I& B4 g2.1:maven中引入redis! Q3 u4 R, E% V* r9 P7 i) k: s

    9 e- k: D2 V- T) v9 x' ?! N) m. O引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。; X% S" Y& i2 e7 _1 V+ b- C% }  \

    1 y% }, i3 h  @& ?9 {  {<dependency>% S% G  h( A6 P6 T
      <groupId>org.springframework.boot</groupId>
    + e/ A+ S* y$ y5 _    <artifactId>spring-boot-starter-data-redis</artifactId>! c/ R* E5 U  e$ t7 F& M; x
          <exclusions>
    ( T' O$ {& K" \' [7 x% |        <exclusion>! u" ~% J. j& X) W
              <groupId>io.lettuce</groupId>' W0 ^+ P4 V) a* I$ m4 L& b  ^
              <artifactId>lettuce-core</artifactId>
    ' x3 p8 f, X3 s% O- x: Y/ q1 T        </exclusion>/ _1 t+ O9 S: d* S) k, z( ]9 S: c
          </exclusions>
    . g+ a2 W, b+ K4 Y</dependency>
    2 _( n/ o: S0 R: p# ?, _' u; Z<dependency>+ N0 a, ^* ^9 Q: R0 U
      <groupId>redis.clients</groupId>8 J2 |& ?+ N% b3 E8 k
      <artifactId>jedis</artifactId>
    * y" W# W& ?7 Y2 x4 R$ n/ g! D</dependency>  D1 C/ v) _8 b: e+ S( u/ U
    2.2: 在springboot中配置redis
    7 F. `: e$ \' z8 g
    " |" x6 Y3 o; ?/ d& limport org.springframework.beans.factory.annotation.Autowired;5 ]5 T! w  b% e8 C8 ^" L. A
    import org.springframework.context.annotation.Bean;
    4 [1 T# |/ x, s+ x. x+ X2 |+ T* Zimport org.springframework.context.annotation.Configuration;. x+ @5 j! @( M; c) T
    import org.springframework.data.redis.core.RedisTemplate;
    $ R- Z2 f  u$ ^" Z: E$ oimport org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    % G) C1 g2 l* n5 Y* c0 _import org.springframework.data.redis.serializer.StringRedisSerializer;
    , r! m* n2 c9 S/ R1 a4 p3 K- N; ]4 {6 f( y+ ^  F& K
    @Configuration. ^; c2 }& i5 P& K
    public class RedisConfig {
    3 e6 `4 {' P( P/ l$ Y% Q8 A4 \; K! I$ }. t* n1 F
        @Autowired2 ^& @* |0 K' m8 _
        private RedisTemplate redisTemplate;
    / l: q) Q1 b& M: h, K8 f& T  ?0 g  n( v; Y5 v3 [( Z
        /**
    " A0 L  M) l# w' b     * redisTemplate实例化
    2 w3 {0 |4 U/ ^6 ?* p( k     *1 ]! l; M* m8 j5 m7 C  ^( x
         * @return
    9 n5 J) z4 Z% z) {9 @     */
    , o) z% }, v/ @7 I3 \$ W    @Bean
    3 ]% S) j$ k2 ~    public RedisTemplate redisTemplateInit() {
    6 y* ^9 F4 e: ]6 B1 ~3 j        //设置序列化Key的实例化对象/ X' r3 Y6 v) i! v6 t5 J6 Q
            redisTemplate.setKeySerializer(new StringRedisSerializer());
    : s+ J4 a# J9 ?  I6 N        //设置序列化Value的实例化对象
    ! U& F3 k4 ?6 x$ `' z1 [        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    . j2 `- |& `9 v" b: F- t        return redisTemplate;3 }- k" ]4 e- H& y
        }5 u1 ~7 k& i. q/ Q
    5 L1 P. R5 u, c1 ?
    }
    " g9 N+ n$ y" r! r; B1 t2.2:redisTemplate模板工具类5 L" d" R  G9 K" G, F6 P3 {
    : F9 K/ Q& o) [- x8 i
    @Component
    ) B& U# {% [: N$ wpublic class RedisManager {# c+ w2 h6 R5 Y
    7 o+ ^3 a1 s0 U" b# _3 i  K
        private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);2 Y: y4 |! H* K" L, f2 x

      i1 [0 k6 m* k* e% |0 n/ `$ R* O$ k+ W    @Autowired) K  m5 h) N9 M! u. Z" P5 A( A( P
        private RedisTemplate redisTemplate;
    & ]% F' I, W* B( Y; E/ q3 P  p3 b& l6 R$ u0 U# S( G
        /**% {, H3 ^/ \% _9 ^% q
         * 设置对象
    5 {& T, s7 Q6 H     *
    ( J  G# B# {/ D8 h/ c' X     * @param key key
    8 n! P: n  V8 ^7 I1 B     * @param value value值8 a# W# u, J+ @, H; h8 l
         * @param <T> 返回值泛型
    6 ^5 y+ @# }8 l4 T/ n. ~3 J% B/ G     * @return 正确的值:<T> 错误的值:null  R% e& R/ h: W" P+ P/ S% `
         */( S) I/ Z/ Z5 p& `# j
        @SuppressWarnings("unchecked")
    ! J1 ?. n$ \$ I9 R" L  p    public <T> ValueOperations<String, T> setObject(final String key, final T value) {
    9 {# Z' |( i$ `) H$ \$ Z0 ?        final ValueOperations<String, T> operation = redisTemplate.opsForValue();% i+ S+ U; C1 I+ |: r
            operation.set(key, value);
    2 y3 U8 b$ t$ f7 Q/ a# W4 w        return operation;/ W, T+ o  b/ }7 \( f
        }3 B) I0 h8 @( \7 f7 C6 ]

    ( K' @0 s, x$ ]/ G7 I- ?% L: S    /**6 `6 `" K$ t8 F4 v
         * 设置对象及失效时间 (单位:秒)
    ' g0 B7 ?% G* c0 M     *
    ' u! l& ]# i$ f) t3 ]+ s     * @param key key
      e( ?4 B8 I+ G     * @param value value值
    / e7 S/ O+ I1 X. y) H- y% G7 A     * @param <T> 返回值泛型
    # L1 R7 r# Q) Y( G: t/ l$ o& _4 G     * @param time 秒值
    # l) y/ Z6 t9 m     * @return 正确的值:<T> 错误的值:null
    5 ~- N( ~6 j( ~9 }     */
    2 T3 z& j+ O9 D5 P; _3 e( F5 l2 x    @SuppressWarnings("unchecked")
    ; d6 j0 n6 W0 I6 n4 S9 R3 a    public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {' O9 b  X8 Z+ t2 [; G4 Q
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    . Y6 H2 U0 K8 Z: C/ {2 ]        operation.set(key, value, time, TimeUnit.SECONDS);
    ' {) W1 a4 _0 b        return operation;
    - _- h, z3 a7 u6 Q! m    }
    4 O3 ^2 J0 a/ O7 L2 F& c  G. a6 i* u8 ]- s' `5 `4 b) d4 ~
    * F0 {1 U2 K7 X. Z1 w: z
        /**8 L0 Q% _9 X2 Z8 ], l) P' }8 T
         * 设置对象及失效时间(单位:毫秒)
    ; U' w/ N& ]: @4 k% [9 V     *
    ( ?/ H$ T, D1 ?% V     * @param key key1 M. B; {! q. v/ e
         * @param value value值6 d; u8 j6 j: @7 x8 q
         * @param <T> 返回值泛型
    ! s/ d) Z8 n9 `. `. T. P     * @param time 秒值7 z  T& Q3 j1 o# X
         * @return 正确的值:<T> 错误的值:null5 O3 w$ Q. H' J, a
         */
    # U+ M! u! X/ a    @SuppressWarnings("unchecked"), W9 P: G5 j0 X9 n- C
        public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {! Q# y- T# c$ m5 w
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    9 m& Y3 r! |- O9 f        operation.set(key, value, time, TimeUnit.MILLISECONDS);$ `! h' A( M0 J2 v$ [
            return operation;
    2 X& r/ ?' j1 d# e    }: C( A/ }6 ]1 q, u

    : _( @5 ^/ P; ~+ j$ f9 }5 q    /**, `: L( `; r* ?$ e9 q8 B, \% W$ g
         * 获取对象
    1 }9 t" Y9 u- y% I! L( l     *
    ) ^7 u6 t3 k% d$ a$ s0 P1 D8 x     * @param key 键. h" o$ X  J4 Q7 X$ A$ Z
         * @return 正确的值:Object值对象<br>2 ~9 s2 X5 }( S7 v% g- p" @* B
         * 错误的值:null
    6 R  P+ R6 D3 a8 v0 _# k" i     */
    4 i+ ]  k; R* Z    @SuppressWarnings("unchecked")) X+ o, c( J) s6 a# k! x. K# ]
        public Object getObject(final String key) {7 X+ O  i+ c7 y9 a; ?
            final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    # @  K& d$ O. M. u8 q        if (valueOperations == null || !redisTemplate.hasKey(key)) {, f5 F" n" n, E
                return null;
    9 l" O  W% O/ T6 q- D        }
    9 D- Y1 H- I8 K5 V$ ?, u        final Object object = valueOperations.get(key);
    * f' L- i$ S! }8 p+ K) I4 {        return object;
    + ^0 K5 M5 T6 u7 }6 c1 o; y    }
    5 _$ |& l$ L' d: ]: C7 {+ j" {: [' X  t  n
        /**% i2 r2 w! c$ k" X
         * 从缓存中获取string值% d/ H0 B9 `& }; \& b/ z
         *
    % q3 C" r. _) M; o1 A* [7 h! @. h     * @param key: X3 I' x/ O5 }9 b  O! }! Y- J
         * @return*/
    $ R; H2 n3 P6 m, J7 Z& [    @SuppressWarnings("unchecked")
    # r( D1 H+ W1 q    public String getString(final String key) {2 F9 X1 M8 O1 p2 f' n8 a$ g; e; X0 ?
            String value = "";3 }2 S2 V8 a$ `* |2 k, o. B( N
            final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    3 G4 Z3 V; [5 I% R' X' L        if (valueOperations != null && redisTemplate.hasKey(key)) {
    " F5 x! t: |$ C  e/ F' ]            final Object object = valueOperations.get(key);
    4 _9 ~9 ]+ M, n$ Q5 \7 D9 |+ t: L2 o' h            if (null != object) {9 X: n* u9 u+ f  y
                    LOGGER.info("--getString--object not empty");' c% }4 s7 H% n4 g# T* F5 g- [
                    value = object.toString();
      {, D  G8 e: k# A- M            } else {
    1 L) U7 n& ~) e5 ?                LOGGER.info("--getString--object empty");" q; C# K' `1 Q6 v& s3 ]3 l7 r
                }
    3 c4 f- G4 {7 Q! t: k3 Z$ ~* a        }
    1 L6 O1 L7 Z6 h  v. D. P; ]7 y        return value;
    " d' C% M) n$ T" C9 |  o    }7 ^9 K4 ?+ V1 Y. d0 Z4 v9 U' ~
    2.2:在redis中实现时间控制) c( F0 U! j6 \% `5 Y

    : V& o( n5 ~5 I) @2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。
    ) t9 N/ q" n* G- f3 l  R  J& e' ?) P
    import com.youjia.orders.redis.RedisManager;6 K' Q1 }4 j7 O
    import org.junit.Test;2 l. F; A$ b8 O5 z  f2 H
    import org.springframework.beans.factory.annotation.Autowired;1 H( N, s% }9 h8 W4 J- A; f
    3 {( S- Y: N5 X& J8 @: w8 M
    import java.util.Objects;( [; `/ w+ @& _2 X$ O5 Z: U4 [& A
    ! W/ `) c+ {: I4 }* n" g% _
    /**# [* D6 x8 ?2 |7 a6 ?
    * @Auther: Yrion& L9 g( r: F5 l5 p
    * @Date: 2019-01-11 23:36. u0 i& T" u$ p  ]( n) \
    */
    " {) F3 ^& _  D4 j7 Y3 r( _: l1 `9 u2 d0 W2 ?* g
    public class RedisTest extends OrderProviderApplicationTests {) w7 r1 ^8 R, U* q* u0 o
    , b. K5 W$ [% G
        @Autowired, j7 Y0 n9 c8 b) N
        private RedisManager redisManager;
    ' J" l: T! |5 r3 U: t* k# p8 U# \& F( t5 n7 q
        @Test
    # m8 \2 ]" P) Q  @6 k  Z* g* `- ^    public void test() {6 f9 a. ?& f' o3 x' o) A3 ~4 ^8 e
            controlTime("10000001", 10L);( ?" ~% k; d" Y2 d' W$ |) v
        }
    ) _; z, o6 p' n4 }8 N1 m7 N! t! k7 @- C& a6 e# o( G( y2 v1 c! J  q% b
        public void controlTime(String requestId, Long timeOut) {6 h; T+ C2 X, m/ Q6 q+ \
    6 q2 F  M& W+ {& B- ^- h7 r5 `
            if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {" k2 Y$ a0 v+ Q' J/ u4 H) k
                return;
    , a# `, [! D3 j3 i; S: T/ Z        }
    * B- b9 W9 g5 o* S  k; _3 d        //something code+ I; Q  m: s6 U2 {. w
            final String value = "value";
    2 H$ }, d3 Q4 }  J        redisManager.setObject(requestId, value, timeOut);
    * ^- c% l8 p: ~& a6 n& @6 N        final long startTime = System.currentTimeMillis();* ]5 v$ [5 J- |* M2 h9 U6 P+ D/ G' ~
            System.out.println("开始控制时间");4 m! R( [0 k' q' @3 M$ z" w
            //start0 a0 D# s% l1 f) O" F( |) V( m
            for (; ; ) {
    ! k& n% }, ~4 p, z( o1 h            if (Objects.isNull(redisManager.getObject(requestId))) {. a& c; ~. a, Y( Q, Z
                    break;+ I8 Q$ _/ R& d; s& d/ y% q
                }
    2 ~: Q  o2 `% ~8 W; }/ W% C        }
    $ s1 q! ~2 V# K0 I/ G- ]' R        final long endTime = System.currentTimeMillis();% h+ w; j' Z. D9 m% d* I; b2 M
    & S; {( G* j- G# c0 n/ @6 X" q
            final long useTime = endTime - startTime;
    1 [& q$ y; B9 R% M) U5 \9 e) n  q' g! Z- ]* G
            System.out.println("一共耗费时间:" + useTime);
    1 ]* ?& d6 C* b/ F  P- P    }' ^0 x  S" \2 }
    }
    / L0 b% R0 Y! T" E5 X: [outPut:
    $ b$ O& Z( D. @( O3 {! r; z$ f. Q% n, Y3 D0 ]- ?. T1 C  N
    开始控制时间; o4 r+ O" ^; i% f/ U
    一共耗费时间:10042
    9 e1 h1 L7 y2 S& p- }' }/ w8 N三:总结3 G: ^  z2 I- \* K- W, Y1 K+ W
    8 i/ H0 i" F$ F) h" o
    本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!9 i, v- _: H# Z' g+ b* s7 i+ u7 Y
    ————————————————' V( j) `( t6 V/ Y/ ~
    版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。9 p% H; E; J' m8 z' ]
    原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/1058933259 i, f" P0 d8 c+ t

      Q" R- d* s& f% m8 l* v6 x( _5 h% `/ E4 Y0 m, p
    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 17:36 , Processed in 0.352669 second(s), 58 queries .

    回顶部