QQ登录

只需要一步,快速开始

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

    3 D, ]! @1 L2 m# p" f* g) L! _Java如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。1 ]( {8 n% \0 e" K" }+ t
    5 w- s' }. X4 k) u/ Y# d3 B" C# |
    一:时间控制的几种方案
    # a4 p6 g, W/ R0 }) m
    1 z" ~$ X3 K" T4 T! v4 V1.1: 从线程方面解决
    6 [+ Q0 Q; J- Q3 A
    - p7 u! s7 v7 E1 Z( T4 P最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
    4 t( d7 S6 E9 ~% q1 p" v0 }
    4 {' L, w4 w' @: o1.2:使用Timer. [. V# l) K# W0 p0 O0 I
    9 H2 r, g" ^7 T" N  O) D8 x
    查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:& e. [# e- N5 J9 M7 U

    4 p) q8 D: ?6 x) \public class TimmerTest {
    : p- b- L- W; [. T& W% M, P4 x) Y   /**8 X( L  H" ^7 b3 U$ H* b- h% H  @
         * 测试方法
    + `, G( W: P: L, g8 y     */
    % ^& t9 I, Z; E+ \, i* V    public void test() {  r0 t; {2 \8 h, \5 ^: b
            Timer timer = new Timer();& C4 B5 }# n/ c
            timer.schedule(new MyTask(), 800);3 h* O/ g4 b9 t  X' x3 N# K+ w
        }! Z& e8 e6 ]: J

    / }1 U- L# `4 e4 W! H    public class MyTask extends TimerTask {9 N7 G# U/ c; F# \6 T# s/ {, L

    % M5 [- D5 P/ E# ~0 A$ Y; P        /**" ]+ K3 Q- Z" d; a
             * 运行方法
    5 z, Z3 p- `$ b3 d         */
    ) C3 A& l0 E) ^( Y% r9 h0 p' ~        @Override
    * S+ s4 a! E- s7 t. ]% a3 q5 R        public void run() {" W: I5 U# H3 k0 ?3 v/ d
                System.out.println("输出");
    " }# Q) v0 q; a! }9 D" H; k" u        }
    5 K  d6 ]" L9 |9 N    }* m* F( u8 f' i- J9 W6 S
    }
    ; K" N6 ?8 \* c- G! K5 M, G# [这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
    5 N7 L# Y& J0 @- J5 S6 _4 x' e8 A9 M
    1.3:redis延时2 ~3 F, `/ C1 }+ X

    % `6 h( s0 h3 o8 n( @+ L% \6 I" x在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:, Z( t$ v: \* r6 L: e
    $ p  V9 L" N6 E! X6 T
    + \! Q3 w8 h3 A8 B2 o% }

    3 |* C4 [* `+ I, a. `$ g4 I& k通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:- w- q, B9 j! x3 H* r& b

    % [; ~8 Y. Q0 z+ t  t) R$ L1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
    1 f: W; A" j2 h& D$ ]4 j4 Z- T1 _2 @# o: x$ C+ G
    2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
    7 o' R# z) E# E5 W* z6 O$ j& [
    8 i# O1 H& m5 u$ I* x* t3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
    $ v3 u$ g* @) Z8 }8 o& T
    % I0 d6 x# V5 V- m二:redis
    . x3 e6 C8 V1 v2 _, u5 J' ?0 e: R
    ( q( y) H5 P; W4 O2.1:maven中引入redis
    7 d9 d4 K5 L  j$ H6 [5 ]
    7 z7 S' ^* c$ R1 Z: c) n) z7 n5 ?引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
    ! `, n- S* c7 _/ T# Q5 l; x$ [+ R# R  T( G: i" V8 w5 e
    <dependency>9 I0 t) K0 D/ C! Y) P0 X
      <groupId>org.springframework.boot</groupId>
    . G. d9 ~. ]" H: |' |! @6 l    <artifactId>spring-boot-starter-data-redis</artifactId>
    1 l* R4 e& L- t( O; B# i      <exclusions>& T. `3 y+ h& p* i+ a+ m
            <exclusion>
    . B- G' R! E6 y1 j+ L# _) H- x( C. ?+ A          <groupId>io.lettuce</groupId>
    7 a' w( P, L2 x6 W% z- r          <artifactId>lettuce-core</artifactId>
    3 I& s" Z' @& }        </exclusion>
    . @; N$ R2 q( n3 E7 g; z1 e5 d      </exclusions>0 J: {0 m8 i1 ?3 X  e/ m
    </dependency>" g, S. j8 D% E/ |
    <dependency>( t5 N6 o& B) ~/ Q  F
      <groupId>redis.clients</groupId>7 ^1 H, {7 {6 k5 y' q
      <artifactId>jedis</artifactId>9 R* x* C: |" {( G* o) d; n: b
    </dependency>; i# U; p% G6 {- p! t: w
    2.2: 在springboot中配置redis3 N# m' g+ `3 M8 F$ O. E
    # [1 r; Y; N; [; `; L
    import org.springframework.beans.factory.annotation.Autowired;" d3 r9 `5 `, @, y
    import org.springframework.context.annotation.Bean;
    2 S6 i2 U& x3 Z+ [9 l6 pimport org.springframework.context.annotation.Configuration;/ D. F& J- k; F, a4 o
    import org.springframework.data.redis.core.RedisTemplate;
    , g7 Q- I, J2 e4 [, C' i: limport org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
    2 f& w: G$ U4 Ximport org.springframework.data.redis.serializer.StringRedisSerializer;) [  S; @: a; u/ p/ }8 |; M* v

    " u+ M2 j$ T: i) y" O@Configuration
    ; J1 {! ~( e/ N% a9 Zpublic class RedisConfig {) r, Y  D& s( ?: k. p/ ^

    " f' `% P* R$ {( T6 E    @Autowired
    2 l6 E. p5 t, K1 L; G    private RedisTemplate redisTemplate;
    1 ]9 M# o, y5 H9 v
    . h: T9 F3 @& Y% ^) D/ C" @    /**
    - B' h% {$ D3 j     * redisTemplate实例化* Y2 f' [+ Y  J
         *
    $ `3 B4 [. U$ y8 H( H% v     * @return* e& Z; z( r& P1 i
         */8 V# Z: ]! ?3 [8 c* e0 H$ C
        @Bean
    & K0 i% z7 i; c+ w; k, ?( c    public RedisTemplate redisTemplateInit() {6 q7 l) Y; Y; p8 r. f4 l* n$ {
            //设置序列化Key的实例化对象
    6 N/ Z3 J! y9 k0 I5 g# R0 ~- U- B        redisTemplate.setKeySerializer(new StringRedisSerializer());/ _1 B5 T: Z4 L
            //设置序列化Value的实例化对象
    + X3 |- I8 V( c" v/ P) S  ]7 p+ i        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    . }1 G' l% ?! b        return redisTemplate;" E- J  q; ^. C
        }2 t6 p7 h4 S6 S+ F/ v
    ( g& W: `" y$ u2 g% q& y
    }
    " \* P* D. p# j% k/ ^2.2:redisTemplate模板工具类' X) x; C( M9 L  d5 r

    / K0 Q- K8 v( w, G0 m5 o5 Y4 Z@Component
    5 q5 G( E) c$ {5 Npublic class RedisManager {6 d1 I! u7 J$ r1 V. r
    5 i8 m- [4 d& C* B% c
        private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);# n" p6 J2 [" t

    + F1 g) m$ v  `% k    @Autowired. L9 f& z" o; ?% E# J7 t# O0 U
        private RedisTemplate redisTemplate;/ ?6 B, S# Z) O5 }
    ; w+ j5 M: I$ l4 p- _
        /**
    $ e; M4 n" c7 b2 t1 N     * 设置对象5 s' F1 Y( ~& x! i
         *& H: c9 m6 X, }$ }4 r4 l1 b, Z
         * @param key key
    . \) f/ [. B1 f3 I. v. L     * @param value value值( i7 |9 [, J9 B: r, A
         * @param <T> 返回值泛型
    . \, X- b; g; R" V1 R! v     * @return 正确的值:<T> 错误的值:null4 Z0 j8 [; q, T) \& W; |9 f
         */  p+ S7 l  m( v! N1 j
        @SuppressWarnings("unchecked")
    0 Q. S% h& ?9 f2 d6 J7 A8 ~; }    public <T> ValueOperations<String, T> setObject(final String key, final T value) {& w- x" m) n1 A
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    9 j) q# R  I4 j' s        operation.set(key, value);0 K, O. Y7 x1 N4 B7 e- b
            return operation;4 E2 E7 O# ~5 b, S
        }
    # }# `, v! L; P& }) n7 F: m. I0 U& [/ R
        /**. V/ C) Y+ n, u, p
         * 设置对象及失效时间 (单位:秒): ?7 f5 t$ T' F/ h# I* |# o) N. J
         *
    $ A  a! K9 I6 w8 m) {; C     * @param key key
    * q8 t* i1 @$ {: L/ K. F. [     * @param value value值1 W9 u7 i1 V" E! O: ], |/ \
         * @param <T> 返回值泛型. m+ ~6 D) H6 O
         * @param time 秒值
    & N' u  c: H+ E& o     * @return 正确的值:<T> 错误的值:null
    % `* K- |" q. z8 s, y' u3 U0 Z     */2 E: {4 P( f# w* \3 `
        @SuppressWarnings("unchecked")
    # b  K) ~1 V; n+ P8 e3 D) q! R    public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {/ N( o1 P! k7 ?3 V" t) W
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();6 m$ D2 W* r! [. A0 _4 h1 o
            operation.set(key, value, time, TimeUnit.SECONDS);
    7 I9 v2 O5 E5 {% ^        return operation;7 R* }& F1 u; X8 p2 `( E( H( b
        }. X) j& b0 o$ D8 j7 L- }9 W$ u# S

    4 E  L( @2 o8 `9 x8 {5 s0 d
    : P1 d& F! B; h# J8 c    /**. C1 r, |+ t+ r6 {, Y8 Y
         * 设置对象及失效时间(单位:毫秒)
    ' p/ s; E4 R! E     *
    ( R4 }. f. {+ d. w5 r( Q     * @param key key7 q1 ]8 j9 ^! F7 q& N+ }
         * @param value value值. F; z0 S- _, y$ f( X& t  I
         * @param <T> 返回值泛型
    - m) K$ a6 \+ c3 `! y  C' C+ b     * @param time 秒值
    ( B/ Q0 d+ W$ z: Q& T# k* U4 {     * @return 正确的值:<T> 错误的值:null
    1 ^* O- W; \$ B5 g; P+ v     */
    " t6 V3 m6 x" I6 w/ G. N4 A, j0 K8 a    @SuppressWarnings("unchecked")* V2 I' a1 Z8 H& L3 Z: g& ]
        public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
    & O! ^+ u% }0 S+ h: |. X) O        final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    % i- M" a$ c: B8 R8 ]9 I4 N        operation.set(key, value, time, TimeUnit.MILLISECONDS);
    ( k' M+ }3 k) |! l        return operation;
    # o1 \, `8 f1 N- F' H2 X! B    }  X* m% G4 |" U2 j7 h
    8 `. M# a- Z) w2 Q' S
        /**
    9 C5 x9 i0 Z, o. {8 a4 x     * 获取对象. G0 u6 Z8 c. T
         *8 }3 G2 N% B9 P& |: @8 f4 R
         * @param key 键5 i; q% ?3 L+ G  r8 c4 q
         * @return 正确的值:Object值对象<br>/ d3 ^/ z* c. O, `' j$ U
         * 错误的值:null) [! x4 K# A/ E" W, T- C
         */9 w: |0 h- e9 b- M
        @SuppressWarnings("unchecked")3 e+ P, S5 @) v  g+ G
        public Object getObject(final String key) {
    * g& W* H& C- y, T# t3 O4 @* e/ \& x        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    ' Q! g0 F+ h. s/ G) W& G        if (valueOperations == null || !redisTemplate.hasKey(key)) {
    3 n/ V, Z, V! f- B& z$ o/ t            return null;
    * X' a$ x7 U8 C0 ^/ |0 e        }- u$ n3 T2 `  M8 E+ _
            final Object object = valueOperations.get(key);
    4 y# S8 o; u  N  p        return object;
    ' n/ n1 \  [) \! e) U: E    }
    4 g6 j, |. r6 u1 l8 W9 V; O7 ]
    ; C. T# O3 z/ H8 W& h& X    /**4 p3 v: [! d1 ]% S
         * 从缓存中获取string值
    : m7 m, n) v6 q- n     *
    ' |$ I2 V2 T) F# @7 Q  n( ^7 V  k     * @param key* g% V# [% L" j: Y3 n, c; H
         * @return*/# P' T+ p/ M. u- c- W( k3 }
        @SuppressWarnings("unchecked")
    - z/ k* c; U: Q    public String getString(final String key) {
    ! [% j0 f% }& a0 w) Q9 H$ m. y$ I        String value = "";
    " _; t2 d9 q: T4 B% R        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();4 f0 Y' P9 _2 p" G3 b2 c* O
            if (valueOperations != null && redisTemplate.hasKey(key)) {
    . ~. c  |/ y" F5 X  _3 h            final Object object = valueOperations.get(key);
    2 {6 c1 ^2 K8 }6 h) K* C$ I            if (null != object) {2 k7 X; B% W& `* i
                    LOGGER.info("--getString--object not empty");
    4 p8 D) n8 e" i  U0 M3 e                value = object.toString();' n- U# X5 J1 p! h( E# O
                } else {
    + P9 ~- l2 ?- h. `                LOGGER.info("--getString--object empty");: Z# A" P/ K' B' R1 n
                }' P2 t9 X1 v* Q$ r, }) s$ g- z
            }
    ! ?7 ]! f; J% Y3 |) l, {        return value;
    & o. y. A) U0 n9 I    }  B" F7 t4 ]" P& d4 ^
    2.2:在redis中实现时间控制
    4 s' P& q% H' d' T
    7 `) G7 T; a0 _. p( E5 N0 w2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。
    # i4 B3 Q- n+ s( G+ J7 g8 T' T; ?5 a+ p# h) a
    import com.youjia.orders.redis.RedisManager;) }% B/ t0 y0 A. e; D
    import org.junit.Test;. V& v* S$ [) p% d% ^( M7 D
    import org.springframework.beans.factory.annotation.Autowired;! M0 p0 K  A% r3 U

    2 s. z3 ^6 H( s; B. R: X( Yimport java.util.Objects;2 L; o$ t& M% Q6 c$ S

    0 K7 I8 l0 z" W/**% R! K. e+ X: u/ U! X" T( c# T
    * @Auther: Yrion. V2 Z, K; ?- d+ P4 z6 M
    * @Date: 2019-01-11 23:36
    8 l7 [4 i7 j1 }8 k0 Q0 |. b */5 N4 n( t( ~# q; B+ D( M% c
    , @+ ~/ `  Q* D
    public class RedisTest extends OrderProviderApplicationTests {
    6 _$ N2 C- [( R/ I+ R. A! }6 r( n3 E- w
        @Autowired
    3 W: b3 ^1 S3 ]    private RedisManager redisManager;
    ( m4 K/ @- k2 S; x+ s$ U
    . l2 Q, U4 a8 R+ x( g( Q    @Test( l; Z" U9 Y) p, C& D
        public void test() {
    ' ^& J8 a; d" ~: ]  `: Q* c        controlTime("10000001", 10L);
    4 b3 ^* D4 M$ {+ c5 C. O/ J    }
    6 y/ U. W% B6 I) {  G5 ^8 c4 ?5 l2 [5 c1 E1 e$ R2 N
        public void controlTime(String requestId, Long timeOut) {1 s- G2 w/ M6 W' F( u3 R
    0 O) L; X7 {6 v. G4 ?" B
            if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {) m" t8 ^5 e* Y& v: Q4 X% e
                return;- H( {5 {- [: A& f& F" V
            }! o3 y+ p. z5 n  M# U) K  W* \
            //something code/ e0 j4 N2 g/ f
            final String value = "value";4 b+ Z" C& v6 c6 x$ g1 r! M
            redisManager.setObject(requestId, value, timeOut);
    + W4 Y5 ^8 q5 h, g. e        final long startTime = System.currentTimeMillis();) v/ s& s; n" m7 `( x- c: m) k# U  y3 o+ {
            System.out.println("开始控制时间");
    ( E% ^) U' t' [* Q        //start
    , V: B, Q# ~- Q' H6 P        for (; ; ) {" ^- t+ Z& d' s3 c* R2 z& C3 j$ w
                if (Objects.isNull(redisManager.getObject(requestId))) {7 N, n( u5 q, f0 Y' n
                    break;
    , E6 b9 S6 x" k- s$ o5 U            }" H) Y6 }! R* M$ ~- }, }  A" _
            }* n  k6 q* N1 I
            final long endTime = System.currentTimeMillis();3 S/ u& ?% J. k5 c7 m1 ~' K

    2 [2 V+ \7 k  ^5 t2 t        final long useTime = endTime - startTime;
    ) l/ J/ H6 u0 R, y  B
    2 r' t8 g- c, F        System.out.println("一共耗费时间:" + useTime);: Y8 j* y" q% M- I# `0 x8 v- t
        }
    3 c- F' E, \& O/ k0 t6 W}- i! L' T. u/ A3 t
    outPut:
    + Q/ r+ @7 C" j
    ' [4 M# I* m+ e: K' q开始控制时间
    ' Q9 u7 V, P2 P7 [4 z5 `  U一共耗费时间:10042
    ( m* G# A  G! f" q三:总结1 M. U3 P& u. \/ S% j) `6 _# N3 u

    ' z4 d" V2 d3 h' a& r6 ^本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!* A+ {% _% ^! j& D4 @
    ————————————————$ i6 r+ L' m* a3 i9 P* t5 w
    版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    2 [4 Y: g! y9 l# o4 N! p, r原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325/ U% A4 u: d8 x7 h3 `1 X* O2 h

      D: B* _' _. |1 s! {
    & D" V/ L5 A" A
    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-9-8 13:43 , Processed in 0.393141 second(s), 57 queries .

    回顶部