QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1595|回复: 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 n  q0 o& n: D% ?& Q  Q2 Y
    Java如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
    4 k9 v3 [' {! J( C- M% ^9 W" J+ n, t: ^; S
    一:时间控制的几种方案
    1 ^$ ^' x* i: Z0 [& n9 D- d
    0 E9 W! ~: E0 b/ ^) z/ l( x1.1: 从线程方面解决
    " r8 S' l$ c9 @- q, Z1 p+ }
    & Z1 F, a: E: z, E% t( ?最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
    ! \! q+ A  `% y
    # L/ H3 _" K4 M8 M; ?  C1.2:使用Timer
      A0 F4 r- I+ B- J  [" N. @, a; w( V" e* I% J% X
    查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:
    ) G; S8 X* o. c. m3 H" G2 G+ H5 F( Q( u5 n
    public class TimmerTest {! B6 O8 V# f* u) q, o7 D
       /**
    $ w7 e: k) j( T; m. }$ ^     * 测试方法
    " m, r8 I  m: T% h$ h. `     */
      \, d3 T' C( l, n' G$ t, c1 E    public void test() {
    # s" p  M6 q  k" Y* V        Timer timer = new Timer();
    2 G: l5 z9 ]* z& P! I- M" y+ d/ _        timer.schedule(new MyTask(), 800);
    , P' {" f5 m  T; v- \    }
    : w( V3 ^+ S8 }/ |: p
    ; Z" i+ L' ?1 d1 Z' H- M# y    public class MyTask extends TimerTask {: e1 [# e! @& K0 J% N/ M5 [
    ! G  }- R/ j& I! Z. @9 e  O
            /**
    4 {" _8 n- U/ \0 q* \9 l         * 运行方法
    " _, Y9 J, n. q7 U7 F1 P& B9 W         */
    % G+ S% S" y& H9 w7 @# |# Z# u        @Override" m* y0 T% B1 a( B! b* z% G, O
            public void run() {3 @9 x7 z+ e* L. g" T1 \
                System.out.println("输出");/ r0 g" I& s% h  a
            }. D; w# x# S# g0 T' X
        }
    7 H0 v/ j$ `4 R! ^% c3 W0 _5 B}
    ) c. @0 _6 O0 }! x# ]这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。, U: Y+ g* ]2 N! F! _  j. t  F
    * H" w& _2 s0 T0 W# [' h. @
    1.3:redis延时7 t: L# e9 v8 e. d- c8 D. k
    , P" p& J8 _* d! o5 ~( v
    在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:) M2 V  H9 E7 v' w& X" ~+ d; V; y

    # X2 V8 y3 z9 F' A
    # _& M# \- Y/ y. M  @  {5 u% `
    ) C& {+ y: @3 V, Y通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:3 O+ S# H& [- L+ e. F0 }4 m5 ^
    - ?* `% S, x, r5 r' n. e5 |" P
    1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
    3 L3 U0 i6 ~' u& ~" z' B! ~. m: {3 t* V; P' K
    2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
    7 g! ]# ~, W: z! l
    3 ^. U8 G9 Z3 \( u7 }9 o" h3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
    , u- }! z+ C+ a: C; O6 l, B2 g' @
    二:redis
    * A' b( b" w: E  b5 V8 d/ s
    1 }7 p" F8 a. o, ~  m/ X) x2.1:maven中引入redis; n8 p4 U6 @& S1 F- R

      x7 ]; ?5 Z: o7 x3 d2 ^' w/ w引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。' H  C2 W! f3 X, v- h0 w6 w
    , u& D& X; G) R
    <dependency>
    9 a+ v/ R, c* R# {- w9 ^  <groupId>org.springframework.boot</groupId>0 T' s5 b, @, }9 F; O2 E
        <artifactId>spring-boot-starter-data-redis</artifactId>4 I* a6 Y/ H0 P% T% {* G+ D
          <exclusions>
    2 P0 `' l1 _' J$ e& z6 J        <exclusion>+ @# c- \4 `& l" B* G7 C
              <groupId>io.lettuce</groupId>& Q" ^0 l* @5 B' r" L# w. M* B
              <artifactId>lettuce-core</artifactId>5 o$ Q7 h# U( G3 R! j5 ^# O
            </exclusion>$ T% k9 X, L) W; n) g# n! h
          </exclusions>: d  f, c2 {. i
    </dependency>! N8 T  x$ L; d
    <dependency>* k# G1 Z4 c: j* [7 T  q
      <groupId>redis.clients</groupId>
      n7 j; X2 N' g8 C8 d6 R, S. J  <artifactId>jedis</artifactId>
    ' N: b) L+ H( \# f# N, D</dependency>
    ( |( u) \* E) [  j1 u2.2: 在springboot中配置redis. N1 c' g9 a! @4 k

    4 j+ K0 S0 N2 W& l- v( Rimport org.springframework.beans.factory.annotation.Autowired;) _, H, E/ m' `0 H: X$ b
    import org.springframework.context.annotation.Bean;
    / e+ R9 R3 `8 s) A$ j/ jimport org.springframework.context.annotation.Configuration;
    ) l' F# A0 C, b* Cimport org.springframework.data.redis.core.RedisTemplate;2 t/ D3 n4 T, y
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;5 e8 Z9 k# X, e0 t  i' ?) C
    import org.springframework.data.redis.serializer.StringRedisSerializer;' J$ P* N2 \& v8 \
    ( `) ~% K# L7 A- {* R) n
    @Configuration" L% h& Y( [2 x
    public class RedisConfig {- Y2 l1 o" X1 t" n% d
    9 S" _! U7 c2 R! C, `2 j
        @Autowired
    5 H. ]/ Q5 y8 }! J: Q    private RedisTemplate redisTemplate;5 n1 H  k* |6 r" O

      B1 u2 i( d$ K9 |$ ]+ t0 i7 N  s    /**
    ) i4 \: y$ L) B- i! x# j     * redisTemplate实例化) y( ~# i" E! {% F/ O7 u- o
         *
    & T; x# ]' E/ b# f9 U     * @return8 E& E, S+ O$ S0 d8 g
         */
    9 s$ t+ o% w# G* K/ b- C    @Bean
    $ L0 [: m8 u8 j6 l    public RedisTemplate redisTemplateInit() {
    2 n4 k1 h; s0 O0 {% A        //设置序列化Key的实例化对象
    8 ?9 M  _0 D( M$ q- q5 o; B        redisTemplate.setKeySerializer(new StringRedisSerializer());& q6 H! n! {! K7 C) M& p
            //设置序列化Value的实例化对象5 ]6 N8 u# k) l9 l
            redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    , f. o: G( y- Z6 k4 ?0 x        return redisTemplate;2 p7 b6 }) }, e  d8 I  U! O
        }
    : ^$ l7 ^$ G5 e  n1 P% T9 V, i/ p/ K
    }
    ' g) j& f3 d* O& Z- B. ^' x2.2:redisTemplate模板工具类  x2 G4 V) n2 Y. Z' {
    % C1 ~$ l* d3 o3 }
    @Component1 w1 x. _+ {- I7 g/ n
    public class RedisManager {
    3 d' N% }  n& @  o  r: v
    0 d% t. |) X" {1 g( U4 U) W    private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);
    4 ~# }' g8 S$ {/ i# h6 ]7 h+ M% x0 q- B$ q) Q7 L) M0 _8 {4 l
        @Autowired5 Z% y# @% @5 }8 i+ x# C7 j; P
        private RedisTemplate redisTemplate;
    : p0 Y* Y/ I: v; R! x
    $ r7 ~0 A" n9 ^8 S8 R3 @! o5 J    /**3 }- u1 f7 @: {# }( j$ o) J$ @
         * 设置对象
    ! ^, H7 p% j* X$ C     *
    # w# p' j" E6 B     * @param key key# R) {4 h% |2 F, E2 _) P
         * @param value value值
    6 E: M+ g& c0 B     * @param <T> 返回值泛型
    % |, c2 j* O- D5 M* l4 j) m8 K     * @return 正确的值:<T> 错误的值:null4 c( k, J, K; y  L8 M
         */
    3 `) e9 C" v2 ?$ ~- o    @SuppressWarnings("unchecked")
    % ~, r- v6 [. @0 l2 E( d    public <T> ValueOperations<String, T> setObject(final String key, final T value) {. f6 n0 O" z) o2 Q7 N+ x, m- R4 z
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    3 S+ i- m& Z' Z        operation.set(key, value);$ B7 [3 ?* ?& q, F- h$ |
            return operation;
    3 Z8 d! h. E# R7 r    }
    % X3 v; q0 y) V4 g3 g- R, P- i! ^: ?$ Z$ j# Z4 G
        /**
    6 ^! t- \& P' ?; _3 c* y     * 设置对象及失效时间 (单位:秒)
    " [2 G7 z; d" i2 X+ y% v$ Z) r) W     *
    * R: I3 k7 X- B" w. r     * @param key key: u* R7 K! r/ h( [: ~" k
         * @param value value值
    + z% v  ^3 ~+ v( ]     * @param <T> 返回值泛型
    5 k" r, e* C2 ~( q/ g     * @param time 秒值9 c; J" F8 `1 r/ ~! ^
         * @return 正确的值:<T> 错误的值:null
    6 H' }2 x- A" Q; e! P1 A     */% R$ v9 B8 I& Z
        @SuppressWarnings("unchecked")' H  g5 W& [  s8 v7 W0 C- j
        public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {" {7 E# v4 W+ \) l; [! k
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();0 T0 `. Q5 Q6 @! B, k
            operation.set(key, value, time, TimeUnit.SECONDS);
    8 ^; D9 T4 E3 P  g# ]4 p        return operation;6 S* V- Q' V& P9 |  Y; v
        }
    % }# m" O- O9 {. z! y0 E% f; b% E, \% P2 l5 t% G3 m

    & P- O6 \1 B* V    /**0 v+ g8 O3 c6 x% y
         * 设置对象及失效时间(单位:毫秒)
    ; V, v; `( W- v$ k, |     */ ?0 o1 K) H' V* L8 A
         * @param key key% Y1 v5 z% \2 u" h' T6 d2 e
         * @param value value值) q0 R+ M' ?2 V( E+ _/ I) a
         * @param <T> 返回值泛型3 {3 a/ y$ f5 r& {
         * @param time 秒值
    4 n2 L1 |; u" H4 \0 D6 V; }0 l     * @return 正确的值:<T> 错误的值:null
    , F: A, k" [" {, ~     */
    9 [% U7 T: F; z, g5 F    @SuppressWarnings("unchecked"), r. k; K+ \5 n8 m
        public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {- Y6 R3 Q, {% x5 ]! x4 [1 H) i6 p5 I
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();  A. |% F# }( m) l# e$ T- }
            operation.set(key, value, time, TimeUnit.MILLISECONDS);
    ! `% M8 T! f: O  \4 ]+ R        return operation;+ U; Y; F9 m/ d% E
        }
    " V3 ]1 o( a' [% }
    : E5 S! U/ e  ^" Z' M    /**9 h- c3 Z; e- l5 t/ l
         * 获取对象
    ; X2 |  D7 O& ]1 ^  Q     *. ?2 v6 C- i. S% d7 N  x, R) o
         * @param key 键* y- N; n# B: E: I3 ^
         * @return 正确的值:Object值对象<br>/ [% {3 A5 T2 S
         * 错误的值:null" U/ G( `% h, c% C: g
         */' [: P& c9 o+ h  `
        @SuppressWarnings("unchecked")& ^; b' e; c1 O& I+ Z2 p7 s
        public Object getObject(final String key) {7 K' z" \" {9 F9 \
            final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();! [/ ?& j! {+ t  W/ b2 I; s7 V
            if (valueOperations == null || !redisTemplate.hasKey(key)) {$ u! \7 ?% R. X3 z8 v+ O
                return null;6 n% |9 L2 k; Y+ _: w8 t
            }$ f# t6 l) h& t. P
            final Object object = valueOperations.get(key);
    , F  I9 P2 \/ p+ Y& L$ q' t* [        return object;$ T% P0 w( x: q1 N! o, F+ d. F
        }
    1 m8 [/ W. E3 w1 r2 R
      S; T: a: {$ N$ q* I  y: g0 o    /**: C3 |7 z7 ?8 W* b/ [$ h3 P
         * 从缓存中获取string值
    , n+ B/ R( O* ~6 m  c     *
    9 N4 {8 M! C2 e! W1 m# d2 S     * @param key+ y# F. k1 i! b; Z* |2 I: U
         * @return*/  k( z+ U- w8 @7 n1 H3 F/ H6 s
        @SuppressWarnings("unchecked")' |0 I. Q. [* F8 A! l1 k# _2 H
        public String getString(final String key) {
    0 j: R- D' f% {8 R  h, H. L        String value = "";
    # C3 ^+ I) b8 o3 E2 Q% x        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    ! j9 D& c5 k6 h* E6 }4 }        if (valueOperations != null && redisTemplate.hasKey(key)) {9 H5 Q) l: n% O7 Z
                final Object object = valueOperations.get(key);* X3 f  _/ q+ i1 {. u) i
                if (null != object) {
    . |- B% x( P9 i3 S% F4 Z# }                LOGGER.info("--getString--object not empty");4 u4 s, P2 N5 n1 r- @4 ~' {
                    value = object.toString();
    8 K  q. k  q: q3 C8 p8 j0 T6 S8 A) c" h            } else {7 ]" O# [) M& o
                    LOGGER.info("--getString--object empty");) M2 ?" L/ b% A+ g
                }5 K8 X0 T2 _7 c1 L0 T  V
            }  C+ w& |6 ?! p8 I: r/ ^' l1 F
            return value;3 J8 s* \7 H# ?. k1 ^, p$ w7 L  c
        }* @/ N! c4 {& J7 `, R
    2.2:在redis中实现时间控制% o6 j* l- f. \* D) A5 Q* V: z
    % R, n' S, b6 ~& X' l7 t
    2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。% M5 ?6 W; ]& I: c0 F' W

    / z2 l% O( E! Y6 Qimport com.youjia.orders.redis.RedisManager;
    1 Q9 O5 D6 `! ]# G% E/ Wimport org.junit.Test;0 M$ O( O" c9 p" C
    import org.springframework.beans.factory.annotation.Autowired;
    4 {" e$ i8 l* Y0 B3 Y; V4 v! Z8 p6 o
    8 P, a  P$ [5 k3 j& a: Zimport java.util.Objects;
    ) ^( m! q9 N4 r1 Z" a, b7 j/ s" j$ W4 V0 [# K
    /**
    1 K) L/ j+ \. n * @Auther: Yrion
    4 k2 L9 m3 z3 c& G& H * @Date: 2019-01-11 23:363 g" e) X3 R+ X3 {& w6 w
    */
    ! B( e2 `* S. j+ v9 L
    2 k) b" K8 Z0 s' W, U4 upublic class RedisTest extends OrderProviderApplicationTests {
    % e6 Q0 R  @! g$ W
    " j, s1 W* N" {) @! r  |+ p    @Autowired
    $ p0 q, Q2 d; z$ E- P    private RedisManager redisManager;3 }1 \+ H5 |  k9 n5 \1 {

    # n8 _' o5 [+ C) j' e    @Test# o+ k) K7 D1 P9 ?4 o
        public void test() {
    ' j* T& ~+ U6 E        controlTime("10000001", 10L);/ a5 p# ]: l6 N% y3 m7 U' t1 _. E
        }
    5 L4 h$ E/ P* g
    . B. ~! N0 g* @2 ^6 w    public void controlTime(String requestId, Long timeOut) {9 K7 w! D' t- p5 V% _

    0 M" {( F: v6 G5 T        if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
    5 n) {% P  p5 i6 l3 \            return;
    / t' g& E5 q- _2 n6 G$ ]        }" u5 O2 h% Q2 D" e
            //something code" }, q# @9 ]& A
            final String value = "value";1 j) O2 ]( N8 E. e) b: y
            redisManager.setObject(requestId, value, timeOut);/ Y& g" i8 ]0 c- E; h7 a. H% a, i
            final long startTime = System.currentTimeMillis();1 D1 D" q, c: J3 C
            System.out.println("开始控制时间");
    3 z; C7 @% a6 f0 M        //start  q* h$ |% `2 A/ g' g
            for (; ; ) {1 b8 j9 _% ^' L
                if (Objects.isNull(redisManager.getObject(requestId))) {
    , r8 O+ V' N, I/ `; M                break;6 x, K) @2 [9 I9 A' z
                }
    9 ^0 B7 c% C8 p) k! \( N4 ]        }
    8 ?! g- l8 L* d; |) t/ V        final long endTime = System.currentTimeMillis();, O0 y+ r6 Q& ~+ d8 h4 B( a

    ( I- M* w+ L$ d        final long useTime = endTime - startTime;
    9 j9 A% U* a4 D  D5 \+ s: |5 B: C8 `; u) _+ F0 a
            System.out.println("一共耗费时间:" + useTime);
    , C( x; F* y1 L1 ?. u# [    }3 ~: ~) [  {3 `' P4 l
    }$ q# y- I5 ]" _# D' T3 G4 `2 t" u
    outPut:7 c/ ]5 L& \' `% J: D
    4 V3 X7 M  y$ J# @3 }( m7 Y/ e4 Q
    开始控制时间
    ) x0 c5 R# F) Y9 D0 Q一共耗费时间:100422 S: {# _) M- k( @; {
    三:总结  c9 g3 p7 a/ R9 m" r

    , j% g9 D9 l! |% h9 K* ?7 Y本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!* ^6 N) Y( j/ Q" V0 V5 B
    ————————————————  ^" R4 w1 N& N0 r. a  [
    版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    / F/ m- x2 l& l: S$ M原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325; R' p% q! i1 C/ `* {
    7 O# ~7 w& O( D$ T
    1 _' o# Y% [7 X) v
    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-20 13:27 , Processed in 0.448487 second(s), 57 queries .

    回顶部