QQ登录

只需要一步,快速开始

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

    / x7 f& H% S; x( g5 F2 PJava如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
    % s/ R# ?) T+ k/ D
    7 e* c8 j  a: F) ]2 z一:时间控制的几种方案
    # |4 m; f; E% U& W/ a! \! O/ A" ]0 R7 |8 M# y6 ?
    1.1: 从线程方面解决
    9 T8 ^7 s+ D) x6 Q
    $ _% i1 f' q9 o最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。4 \/ {2 D0 ^7 k. J! R

    5 U4 i2 L' S$ R1.2:使用Timer
    $ d1 ~( t' A5 C: p3 u" Z4 ^9 i) }- @4 M4 N0 X  ^- \- A
    查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:7 M. f' J, k" ^5 A# Z6 N
    5 z' ~2 \1 g* i! t2 p' t
    public class TimmerTest {. B  l: M; g, f, U& u
       /**3 L- |4 }0 B9 _1 m7 b- K
         * 测试方法3 C2 H) y+ L, \" k9 Y  P
         */
    " Q# s$ ]. f& S/ Q    public void test() {
    : k, ^+ p+ j( `1 u        Timer timer = new Timer();
    ' T( E6 ]$ D$ U/ e0 N% a0 C. `( N        timer.schedule(new MyTask(), 800);1 g3 i& |4 }; S+ o
        }6 i- r8 l6 d! p/ }9 g- V- X! S! P
    - n# G. U6 {: ?# y
        public class MyTask extends TimerTask {
    # a3 f. C4 ?' _9 v" S# N
    8 E2 L) ]0 s( c6 z; C+ C! g        /**
    7 C: |( j5 B2 m# \) o- K. r         * 运行方法! w) l2 Z4 B8 r
             */3 U/ Y5 {  P9 C& v
            @Override
    - D4 h# \) t3 E7 Y$ U- ]        public void run() {
    ; o# d9 p( H. h/ F5 F) ~# Z            System.out.println("输出");5 Z! ?( X. \+ f! v* @
            }' [/ x5 X/ J6 M6 t  G" m6 F: P
        }4 g; D. Z! Z$ U( A$ O
    }
    5 M% i& ~! m/ H' }9 |这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。9 Q2 `# M* |2 {: i/ V, w. P
    ! I5 g/ {6 b: E# Z! b$ K& R7 b
    1.3:redis延时
    % ~* X& M& K3 h: U, g, l, U2 T
    1 I# L  N# R# v) K; r  t, L在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:+ F% F4 _3 J0 m, y8 V0 J
    0 I2 w$ K7 z/ {5 ?; ^  s
    - l7 E. J+ {5 z* D3 ?, t" c5 h4 Z7 b) l
    & f/ l- D  m2 s+ V/ P
    通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:4 j) \1 ~( c4 y0 Z$ i
    $ V% g* M# |; j2 f
    1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制
    8 j* n& y& n5 g! }6 Z) k
    7 S6 |5 T3 t) ?& |( W4 [5 q2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现
    ; V, j# L/ j1 y' W4 g
    * F! z; N: R. B  x# r% Z0 _4 u: r3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
      j  Z; A% p: H; H3 s1 [% J3 P' Z  |+ @. X% N* x$ I  ]
    二:redis7 i3 W* d/ u0 d% W. a: {

    1 k% x# Q' J1 W  p7 M$ H1 Q2.1:maven中引入redis
    1 Q$ L0 y* c- Z: E! K0 r
    ( R# g2 x( W' u0 |& I- k, E* y引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
    ) k. f, D$ D2 w5 @+ J0 Q3 k+ k8 k' Q1 `$ B3 C
    <dependency>  M! B6 z" p* V5 U$ F
      <groupId>org.springframework.boot</groupId>8 f4 b- x* F6 F
        <artifactId>spring-boot-starter-data-redis</artifactId>
    " s" T; W, ], G9 i8 q' o4 h& o! Q      <exclusions>
    . m8 I. [% C' M) s; ~7 Z        <exclusion>: K' y+ z' q4 }, c# z
              <groupId>io.lettuce</groupId>6 r, D: s" `4 {' W0 T
              <artifactId>lettuce-core</artifactId>
    ( |. Z/ ^  o7 D# V$ f8 z; l/ Y        </exclusion># a& q/ i( t) {$ V
          </exclusions>1 D. ~" `  {. E, ?) u* b( r& A
    </dependency>7 Q3 g- Z* G! W
    <dependency>3 ^7 r! p' C- j: A3 f
      <groupId>redis.clients</groupId>$ u% H* U9 y( [! s6 W
      <artifactId>jedis</artifactId>- P1 q+ ^  U4 d4 N1 B3 y" q% c
    </dependency>- J& X3 c+ i' ]1 ~6 r& s
    2.2: 在springboot中配置redis
    ! I' a, O+ ~  }! L: ?/ l
    # b, f& k0 b7 I) k& Ximport org.springframework.beans.factory.annotation.Autowired;
    ' w0 b  t& l& K% b( c0 P2 timport org.springframework.context.annotation.Bean;
    % A( ~3 R2 O2 e& I9 N! Fimport org.springframework.context.annotation.Configuration;* a) W  o* E/ B6 G7 y# f
    import org.springframework.data.redis.core.RedisTemplate;/ R' s5 S* h' C' [7 n
    import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;$ _2 V5 M  l2 w3 u) b
    import org.springframework.data.redis.serializer.StringRedisSerializer;7 w3 s; T! T& i. n4 L

    ( Y# Y; B$ [6 v, j% ^( v/ N@Configuration
    " G) p% U2 d  |" c  Qpublic class RedisConfig {
    + s/ Q' j) L* h( Z, }" d0 c+ S  {; e. v! @1 c1 k. p
        @Autowired2 o/ D) b" o. d$ v7 @& @0 Y
        private RedisTemplate redisTemplate;
    " b" G$ Z4 a- K$ c* B+ t# V7 j/ a% r- \+ K) v- w9 Z& {
        /**
    # c# {" G9 n: m     * redisTemplate实例化
    & Q9 K! I7 V  |* F- G     *
    0 W, Q& J9 f8 h1 t) i! R1 R     * @return) }3 X  c: |" f5 Y4 u2 u
         */
    1 _; j; j* ^9 O- r8 G$ c' j) `    @Bean9 u& Z8 ]4 U  b' W# M8 H* q9 ]
        public RedisTemplate redisTemplateInit() {: ^" R+ F5 r. X1 m: G3 z- m
            //设置序列化Key的实例化对象
    * ?2 y) K/ F# p8 @: [' E7 U        redisTemplate.setKeySerializer(new StringRedisSerializer());
    + _" o* a' Y& Q: ^3 i        //设置序列化Value的实例化对象
    8 l1 \% {, c! |  x: ?/ A) s        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());: r5 ~8 J( v2 k0 e' R
            return redisTemplate;) a( {" Y$ I3 y5 M9 q
        }
    ) {& P; ?7 |6 p2 t8 T- `9 U' q5 ^; _  V4 t
    }
    7 B3 d, h) e/ a, \9 J2.2:redisTemplate模板工具类
    & o* `# @# N, W$ R4 _& ]
    $ n, \  a0 ]1 j2 }$ B( |& Y6 x@Component( ^+ j; o6 [4 _: x/ _( M
    public class RedisManager {  r  f: d( q/ M  O$ m4 c
    * W9 N; r5 t6 d  x1 W
        private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);. V4 l$ e  X4 R

    6 D$ f, l( Y4 q4 u9 V8 T9 U. y    @Autowired
    * z3 ~: `$ ]' @/ B5 r0 m% |5 r: ?    private RedisTemplate redisTemplate;
    ; G' Y4 E, l0 ]/ s1 V! l8 V  {6 M* f6 [' m0 H4 q
        /**6 o+ T; P" j7 a8 j0 B+ I9 ?
         * 设置对象3 ~9 _* P" ^2 S+ C# `  I3 Q4 R2 G1 n
         *
    3 r: a% J6 c4 H% t     * @param key key
    # d7 G* }, d& _$ a2 T* ~     * @param value value值, n3 `. u8 j' j( R9 C) Q7 B8 J& q
         * @param <T> 返回值泛型, l) A$ G0 W: k, y9 D- V' Q
         * @return 正确的值:<T> 错误的值:null
    ) H  p: |- V1 b     */
    1 Y5 z- Q* p: m: v  o    @SuppressWarnings("unchecked")
    0 T- A# r5 @% l& w$ b  X. ^1 b    public <T> ValueOperations<String, T> setObject(final String key, final T value) {
    6 ]  A$ T  |( E4 M$ e  `4 p3 y9 x4 ^        final ValueOperations<String, T> operation = redisTemplate.opsForValue();0 p, L& g% O' m7 ?; r
            operation.set(key, value);
    ; x6 u% J5 H0 l' i        return operation;
      h* x8 W, B* d, y    }* f4 J8 e; w( T: H

    " \: I7 Y! `3 ^9 b' p- J8 S    /**
    8 U1 k8 `- r# o7 J( V     * 设置对象及失效时间 (单位:秒)
    : j) X% z) d; S     *
    & Y) b% r7 u# C# j3 B- n2 r4 N3 }     * @param key key
    : E  s2 I0 g- I5 ^/ W" K: `     * @param value value值
    ' y3 Z2 I1 S. S3 W% R" F$ N     * @param <T> 返回值泛型: p, w* F, o; ?
         * @param time 秒值* |, Y" M! R6 _, n4 G& W4 i+ Y' T0 x
         * @return 正确的值:<T> 错误的值:null+ \7 j2 V: {( N  ~# k" p2 A
         */' r2 Q4 W. H* f9 ?
        @SuppressWarnings("unchecked")7 c, l+ k; R' U6 B4 i
        public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
    - b" a- _. F. S* A        final ValueOperations<String, T> operation = redisTemplate.opsForValue();& b1 v" O, k# ^+ m6 ?
            operation.set(key, value, time, TimeUnit.SECONDS);
    1 ~3 g' f) P# @& Y( [3 S( o        return operation;/ i, W% S9 W+ w4 T
        }$ @( ~( Q: c9 N: E7 L

    2 q" P2 c3 M3 a5 T. l' r/ n
    & S: d$ h/ o8 w2 q( p7 Z4 e# m    /**; _8 _. L  `/ K6 U7 z$ n. l
         * 设置对象及失效时间(单位:毫秒)
    ( Z! j$ N5 X- c9 i& N2 D     *9 P# V; s/ Y* A" F) \4 e! i. E
         * @param key key
    0 y6 M1 _* Y$ `7 }& d     * @param value value值  m5 R5 w) q: G) Y2 P
         * @param <T> 返回值泛型6 s2 S+ w9 ^, F0 H! o
         * @param time 秒值/ U3 h5 }8 M9 k$ \
         * @return 正确的值:<T> 错误的值:null
    ) H7 u5 J) T1 r( z4 R     */7 B- Q1 j% }% Q) j4 g; Q7 a& E
        @SuppressWarnings("unchecked")
    ; `: b) E" I/ ~/ k9 \# j! h& P    public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
    ( k+ L) t" A/ ^        final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    * j4 X* W! M. V8 O6 {; h7 u        operation.set(key, value, time, TimeUnit.MILLISECONDS);* Z" b3 q- [3 m0 E- ^
            return operation;) M3 G3 L& j+ E5 @
        }
    & E' }: `( G3 `) R. f' O  e- p! ^, `$ X# Z0 S9 [
        /**, k' |+ U4 x5 ?; E+ l% ~
         * 获取对象
    ; T! L3 u; E* N     *
    6 G! T& k. @+ U* O# b. f$ i7 ]     * @param key 键: L6 N" {- K( E. p' A8 K+ T! O9 p
         * @return 正确的值:Object值对象<br>$ I  k. `" {/ m/ C- k
         * 错误的值:null: d% R' w, h+ d2 S; D; B7 ~
         */
    : h' H$ ~! {3 ~3 c6 p1 u5 P. n    @SuppressWarnings("unchecked"): ?0 U" R  N* ^4 N! g6 b
        public Object getObject(final String key) {" ]2 L1 }, V' ?2 ], \& D
            final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    # D# p+ \/ q9 o' S6 r+ e2 \1 ~) I        if (valueOperations == null || !redisTemplate.hasKey(key)) {
    ) g3 o' Y7 K" H/ z8 Y            return null;+ y. p5 V( W% w: }
            }
    ( s( s0 d* |: C* n7 `        final Object object = valueOperations.get(key);% n9 [$ P, }1 R
            return object;
    / [9 i0 P8 U8 \0 c  Y    }
    2 E; E6 `6 t: @; ~
    $ A: J7 M' ^& w" \6 p5 f9 z; j    /**
    5 X# P: S2 X6 b/ D. V( [     * 从缓存中获取string值, ^2 y! |' v- F1 ?$ w: C
         *
    ( B9 V( U% D3 U0 |  I9 _9 A     * @param key. w/ Y3 o" C: u/ E) o" V
         * @return*/' g% U  f& e0 E1 C7 |, U! z; u+ W$ j
        @SuppressWarnings("unchecked")* }- V- [7 G4 c. I4 Q) \
        public String getString(final String key) {
    5 e$ @; z# F6 ~, ]% t' |  w        String value = "";6 z: ~$ W2 H% {: g" I, Y# O' I5 A
            final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();, x) k# p* T  U' _
            if (valueOperations != null && redisTemplate.hasKey(key)) {
    , h- |9 f' D2 }# i            final Object object = valueOperations.get(key);
    . ^3 Z7 g+ R) a! o* n( Q& }            if (null != object) {; M2 |. c+ I" Y  e6 w
                    LOGGER.info("--getString--object not empty");
    & e; t1 N; E1 v8 T9 _+ x6 I7 Y                value = object.toString();! h; u/ N( t' s/ e" E5 z4 c( v
                } else {% [! g3 s% `2 e
                    LOGGER.info("--getString--object empty");  W- V/ J; O# O! y  r  _
                }
    8 ]& o7 Y4 u4 O0 F3 z        }3 y9 I" T, V2 q8 h/ d* ^
            return value;
    + U" ^3 f5 J# Q    }
      w" S3 y6 s4 [2 ~4 J4 p2.2:在redis中实现时间控制) c. M& J# z6 Y8 u( O3 N1 N8 r* }/ E" I/ i
    # m3 p- {" t' i, l; p. `
    2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。
    . V! K2 l7 ^8 _5 \( q
    & W( M: B% ~! Y# T" cimport com.youjia.orders.redis.RedisManager;
    1 n9 o6 `. A7 n( _import org.junit.Test;, W/ i, B* A- l: G; t$ d( `
    import org.springframework.beans.factory.annotation.Autowired;! I* q. m9 |3 g9 P( T/ H. ?. I! T, ^3 o

    ) H. C# W" j- V/ [5 H4 f  Mimport java.util.Objects;
      h. q6 M/ P  ^# b
    ) S6 x  Z! c8 x6 F$ f0 m/**
    : b9 u. w+ @8 d- x * @Auther: Yrion6 {( f) s) C3 N( l; f  z
    * @Date: 2019-01-11 23:367 N+ l4 f  {2 Q* m6 r* v( \( D; q" x
    */7 y* i  ?# X% V/ l6 r; Y( a& G5 I
    ! ~. ]4 T- g4 `0 P, W
    public class RedisTest extends OrderProviderApplicationTests {: Y" |  l) d/ T8 I( B
    ! s5 L/ Y- A' b) Y" @1 c0 W1 S
        @Autowired5 P2 ]# ^, V  V0 w8 j+ k
        private RedisManager redisManager;
    ! P" B1 u# X$ _% S$ O. Q
      N; O# v! m4 q  Q! y( Y    @Test! A5 ]" ?& o6 M- j3 z% H, e
        public void test() {$ x! L0 g! j, E8 C% k; [
            controlTime("10000001", 10L);
    4 L% G9 u/ q$ B" V& T    }
    ( {0 s. a$ Z1 {! `3 U# ?" @' Y+ F8 k# ~
        public void controlTime(String requestId, Long timeOut) {: G- Q8 E7 B( }7 _

    5 B; N: Y0 t5 c" V# f. D& u        if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
    5 l5 p- Z; k+ e$ P1 o            return;& @" s0 R$ u2 P9 q
            }0 V) K4 q' y; [5 q/ N/ t
            //something code2 Z/ i8 G& [1 g9 D% S, }1 C
            final String value = "value";8 \5 A$ k3 S  K; P9 s0 ?( W
            redisManager.setObject(requestId, value, timeOut);
    . s6 I; j. w  o3 D        final long startTime = System.currentTimeMillis();
    5 {; ]' r* x  t$ L7 [& _        System.out.println("开始控制时间");+ Y( r+ `) f3 j* w7 U# H  W! [9 p8 l
            //start. T6 U( m' X3 }4 A, n1 Q, y& F
            for (; ; ) {
    ( {! A  o9 |/ a1 a( p            if (Objects.isNull(redisManager.getObject(requestId))) {
    3 w, i% q  o0 ^# ^$ k; T% o                break;
    6 v' ?" E( P) z0 Q$ p3 q            }. X7 B: O) w/ T3 H$ w
            }
    / A; S( ]' l: a! E: H        final long endTime = System.currentTimeMillis();
    * w- G" N6 Z( f0 T1 w
    0 M8 w8 W  T( Q* M: P; H        final long useTime = endTime - startTime;: G: |/ U) o7 V- {: G
    $ U! R: M: I: x
            System.out.println("一共耗费时间:" + useTime);0 l1 E$ |2 q* d# }; @0 a$ J3 }. k
        }, N( z: [3 y8 Q; {6 ?( t0 w
    }9 s% B2 E" Y- @1 G  {2 H9 |
    outPut:
    / Y5 i4 o% l; c: X8 ?3 W. K
    + W' g. o% h* Q/ M0 ^开始控制时间+ Y, M( I- L5 _  J  Y) A/ ]9 Q
    一共耗费时间:10042
    8 l5 G* g7 V  [& z; ]8 s2 r% T. O三:总结) r) @0 B6 U6 q% V0 V2 m
    " q, M$ t  L5 s1 a
    本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!
    - ^1 ~1 k6 @6 n, t4 S% ?  [- J+ N————————————————
    % }" w$ a. g0 D. m2 D; f5 e版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。" q% h8 x4 s, _+ }1 M. e% E' Y
    原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325
    % {; |# N1 n3 c0 R+ K$ |# S
    / n  @5 Y6 r4 ?6 n; N' w8 X: p9 G+ \
    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-6-17 11:44 , Processed in 0.438125 second(s), 56 queries .

    回顶部