QQ登录

只需要一步,快速开始

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

    9 Z9 m) }" ~5 n- O0 h7 j" h0 EJava如何优雅的实现时间控制前言:需求是这样的,在与第三方对接过程中,对方提供了token进行时效性验证,过一段时间token就会失效.后台有定时任务在获取,但是偶尔会出现token失效,这是因为在获取的时候,定时任务正在跑,可能正在获取最新的token中,这个时候如何过一段时间(比如800毫秒之后)再请求呢?小王仰望天空45度,思考起来了。。。
    ! k" U2 M+ p8 p& y* Y
    ) F7 {: R( p* {0 s1 d0 G8 L9 Q一:时间控制的几种方案
    * y6 F+ n) R# I3 N
    % ^4 v8 Q2 v) E1 S1.1: 从线程方面解决. P: r2 k# D* ~7 l: }- B
    ; i+ W3 H/ `# I, O; U4 z- S; Z
    最简单粗暴的一种实现方案:Thread.sleep(800),但是很快就被小王给pass掉了。为什么呢?虽然这种方式可以,但是存在一个隐患,如果在多线程环境下,线程很容易被interrupt,这样代码就会抛出异常,这样线程就会挂起,导致整个线程异常结束。实在是不够优雅,违背了我们设计的初衷。
    2 ]6 y. t4 m. S/ ~$ k4 P1 N; v- S+ W* W+ v# t  m) h# q; ?7 ^4 o
    1.2:使用Timer+ j2 O8 B& {4 v8 y

    ! N; M' B+ T& K. e+ D查阅了jdk,我发现有个实现定时的类,使用它是可以的,在jdk中提供了定时器类,这个类的主要作用就是控制一定的时间来简单的定时执行某个任务。有点简单的elasticJob的设计味道。接下来看一下,用timmer如何实现延时。。有点惊喜,我们来写一个最简单的例子来看一下如何实现定时任务:2 F3 ^: ?5 i4 l, @  A* q5 |
    + r5 k. B! d1 ^% H/ G& d- d
    public class TimmerTest {
    - v0 R6 k* v! V/ T/ _   /**
    - E8 |9 L- J; ^! c# W+ ?7 A7 B     * 测试方法
    ; T, i9 Y, x# k4 d* Y     */, y: W* j- i8 I: w& `  h
        public void test() {
    , N1 W, c, i% W% x/ o        Timer timer = new Timer();, b% P$ E3 k( ~3 \* A  j- H
            timer.schedule(new MyTask(), 800);$ N4 i+ L, i2 s; G6 Z# N! c: Y
        }
    ( c- i" v5 g" V# h9 c$ A1 d6 P
    4 f0 q7 t; h6 w8 J# K+ M( o+ Q    public class MyTask extends TimerTask {
    ( s( {% R* x6 h8 I4 O# q" V! ?- y7 \
            /**
    " m) u6 q. s- }# r8 f* c         * 运行方法1 z' H+ K' H4 V' M3 J5 Z
             */
    & h% N8 F2 d! s' Y- E) f& X        @Override
    - k+ s% Z/ H* M! x/ A' c$ I  W& W2 N        public void run() {' w* l$ Z8 P6 f, J/ w% \
                System.out.println("输出");& S. b' Q# o# @% W' f% u. i$ u
            }
    : [; ]3 q+ @6 u3 L% i. F3 K    }8 j0 R5 ]; X% z) V6 P7 G# p
    }
    5 L# J0 B7 u9 U这是一个很简单的定时器实现,可以看出它只需要将方法对应的类继承自MyTask就可以实现定时执行,这种方法是可以实现延时的效果,但是它有一个致命的缺点:对代码的侵入性太大,为了实现定时我们不得已将对应的方法封装成一个类,然后放在定时器里执行。这样的、是可以的,但未免也有点太得不偿失了。为此我要更改整个类的结构,对于修改一个东西,我们要尽量按照最简单的方式最好的效果来实现,所以这种方案也应该pass掉。
    - R# d0 X% l( D2 u$ U$ e# j! I, S6 e* e2 }8 u# Q
    1.3:redis延时0 h" X2 Y( S% P6 ?& E- ~6 R4 o
    ) B. n  R2 O" F2 f" q
    在redis中存在一个命令:EXPIRE,这个命令可以设置键存活的时间。一旦超过指定的时间,redis就会将键对应的值给删除掉,因此可以利用这一特性,我们来曲线实现延时功能。在redis的实际命令如下:
    , r2 \& o" C' U( ^3 u0 T
    1 i7 k; z' q7 d' V5 x7 y8 N. k( s6 W% k) ?1 U! `

    , a' B, X# N5 H- V/ D, b通过EXPIRE命令可以设置键的过期时间,一旦超过预设的时间,值就会变成(nil)。利用这一点,加入一些业务参数,我们就可以有效的实现延时的目的。通过redis的过期时间使用redis的好处有以下几点:
    6 m- L4 j6 q/ K3 B6 U2 o
    . S$ R2 G  c, B1:对代码的侵入性低,不用额外起另外的线程来执行。只需要加入一个方法就可以对单流程的时间控制2 Q& q5 V9 i$ K$ P: N

    9 `# ?9 a' [4 u+ R* f8 K2:实现方便灵活,通过key设值可以加入一些唯一性的id来表示业务含义,从而保证业务的稳健实现- O/ u) y8 y5 g+ a

    ! P! [6 O5 c* K* V1 H* Y3 t3:简单,真正的代码实现起来只有很少,下面会给出代码示范。
    % i! N7 g$ O& W4 P. J) U8 h) x# @1 l- a# X
    二:redis
    & u4 W# {0 ?* K3 S, \; I7 s  ~3 I: p4 F
    2.1:maven中引入redis. X; M, M0 C4 z' h
    ; E( n( ^) Z/ M
    引入spring-boot-starter-data-redis,这是springboot专门针对redis出的整合依赖库,整合度要比jedis、和redssion都要好,所以推荐这个依赖库:有不少朋友问,如何深入学习Java后端技术栈,今天分享一个,互联网牛人整理出来的Java深入学习路线图,以及开发工具包,【戳我进入】学习裙。
    / Z7 m) X+ Q8 \
    # t3 X' C& }3 u6 b* q  P/ ?9 }+ L<dependency>
      o1 g7 k) Z, x) c# E  <groupId>org.springframework.boot</groupId>6 `- w& ?8 V, A/ q5 e0 K" j) @5 q
        <artifactId>spring-boot-starter-data-redis</artifactId>
    * y& o5 X- e  I4 S" T# N      <exclusions>/ @- k' k6 f  k  q: N+ u
            <exclusion>
    . m4 |  `! g* ]) X1 p* ^% w          <groupId>io.lettuce</groupId>
    , f( M6 k, q! a( @/ j' B2 N          <artifactId>lettuce-core</artifactId>
    ! L, Z! g! _' l1 M) P9 W        </exclusion>* G/ Z9 ]8 [* ?/ `' I2 f8 F8 y/ L) e* o
          </exclusions>( N% o4 M5 ^2 P# Q% u% r3 Z
    </dependency>% R/ J0 p8 E2 d; g
    <dependency>
    / C; Q, [: ^) G+ [  <groupId>redis.clients</groupId>& B; Q! U" A% f6 y
      <artifactId>jedis</artifactId>4 K8 J; ~7 l# P& @' d
    </dependency>8 g2 o' Y5 ~$ ?( l. H" X& ]$ ]
    2.2: 在springboot中配置redis
    3 E7 L# [8 b, S( c* F) x* e
    ; Y+ k" U- W  c, I+ `) aimport org.springframework.beans.factory.annotation.Autowired;
    + P3 b* m: p1 f" u7 pimport org.springframework.context.annotation.Bean;
    0 C# q6 a$ b3 X, [# ^4 Limport org.springframework.context.annotation.Configuration;
    6 _" d7 Q3 P0 z4 Mimport org.springframework.data.redis.core.RedisTemplate;
    & Y5 G1 n. I  U$ _$ Timport org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;* c8 O# J6 d+ g9 j$ f# [4 J
    import org.springframework.data.redis.serializer.StringRedisSerializer;1 L( r2 I! j& @1 o" ]

    1 ~! g9 O" w$ U1 g* q@Configuration, w  S  N1 z  M1 E
    public class RedisConfig {, ^& H9 w- A/ b

    6 \4 F( T8 |! c  o3 C( U    @Autowired
    4 ~# ^3 s$ B# s& A! ^, {4 Z: j" H2 w* ^/ }    private RedisTemplate redisTemplate;* X7 V2 R& ]5 W. |

    + S+ z" R) {" {6 K2 a, Y    /**
    4 G# V0 |8 ]1 L5 g" w2 ~! y8 b     * redisTemplate实例化
    1 m$ t3 X, P0 I4 P8 u* P/ u! }     *) n1 Y1 s5 G/ l1 H& l3 z# N9 u7 }
         * @return
    1 r- b3 u  B+ a& h     */
    3 S( I9 \* B& S" j# T6 Z    @Bean
    " P" Q6 e0 w9 U! ^* G- i: y* g    public RedisTemplate redisTemplateInit() {
    ! i8 x; B# Q2 p& Y( L3 _        //设置序列化Key的实例化对象
    9 o4 _6 G8 O$ |. J( }) ]# `, U        redisTemplate.setKeySerializer(new StringRedisSerializer());0 m; O3 B! U! N" L1 U0 x; Y: ?
            //设置序列化Value的实例化对象
    9 C* f( ~9 ]& \! Q        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    , S$ f# t- V# E0 X  R* _- P$ B) g        return redisTemplate;
    0 G  A6 {  }% {7 |    }( D3 R* c6 v8 Y/ }0 q9 O

    9 o* ~  h- {8 [* M6 s$ n* L}/ W' [  ?3 Q) F3 f
    2.2:redisTemplate模板工具类
    ; h# h7 H9 S2 _. h: }2 G! U3 |1 ^$ M2 Q
    @Component0 K5 r2 u: n1 T  z& ~" C
    public class RedisManager {: W  c7 D5 m/ N! o
    # z" g( w0 Y6 @( K7 Q8 S4 `
        private static final Logger LOGGER = LoggerFactory.getLogger(RedisManager.class);
    7 W& i) T, m, ]" S' T' j: u5 z% N" q3 }+ v, K% U, I
        @Autowired
    9 `4 B+ m7 g+ |& ]4 z" l# \$ A    private RedisTemplate redisTemplate;4 s; d: Y" q5 d. [, V; i! J3 }' X
    2 D9 a. A5 M' M* q3 ?& @" y! e8 X
        /**
    * {  G& D7 l: P% s* H     * 设置对象
    . X0 p9 x+ e3 [, h5 y! `( f# q; g     *
    & c1 k( y* M! ~8 ~4 \8 Y# W& ]2 z     * @param key key) M% p7 g; t% n( n3 }, M* i
         * @param value value值# o3 t8 x- b9 \  i
         * @param <T> 返回值泛型
    5 U9 u- S9 s: g% x     * @return 正确的值:<T> 错误的值:null
    # D* X0 D2 i" ]! |( C( X     */( Y, k9 n3 Z1 r' [: J9 d
        @SuppressWarnings("unchecked"); J3 N) R% H( K2 p4 D9 r7 K" }
        public <T> ValueOperations<String, T> setObject(final String key, final T value) {% U$ p6 n6 _6 C" p  o
            final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    ) X. X' K8 R1 @8 P1 G, t        operation.set(key, value);$ X. n2 u" L8 a& D/ g& X5 i# o
            return operation;0 C9 k/ h9 }: N+ G8 M
        }
    . l) M" ~8 b* L4 ?2 l& y- T  I0 X# v
    ! Y4 d/ |' y- A  X+ i0 \    /**& L7 i' o8 r' O1 D% |
         * 设置对象及失效时间 (单位:秒)
    $ B& E! x( k$ u& d% r6 x     *7 s' j9 U% e& _! `2 z) i8 O9 |
         * @param key key; P  ~& s+ [3 y9 @9 i+ n
         * @param value value值
    ' [( i0 A! m3 `6 n     * @param <T> 返回值泛型
    ' y7 D4 ~3 c. t     * @param time 秒值6 v- ^) c; }3 s+ X
         * @return 正确的值:<T> 错误的值:null+ _$ c7 y$ }6 c! a4 ]% P
         */' ]0 ]3 D* e: A: ~' P) q+ {
        @SuppressWarnings("unchecked"); Z  X8 w! F7 _, ~1 P; G
        public <T> ValueOperations<String, T> setObject(final String key, final T value, final long time) {
    * G. D1 h( b5 [& F5 q% [4 m        final ValueOperations<String, T> operation = redisTemplate.opsForValue();" o' g8 h/ p+ A  o1 \; A* \7 q+ {
            operation.set(key, value, time, TimeUnit.SECONDS);
    3 e& C* Y  u4 S" h0 b  P4 [7 f        return operation;8 F% T& d7 [0 w
        }: [5 C$ j6 c! @1 `
    6 q5 ]! b/ `8 `0 y/ t3 B; y7 m% B

    1 H5 F0 u2 a9 _, `% {    /**
    ( k2 L. D* D6 P! b  A     * 设置对象及失效时间(单位:毫秒)
    $ e3 P% \+ L9 u0 n# y  W     *
    $ |+ a0 K7 ]4 n. U- G9 B# H     * @param key key9 J, v0 M1 `) X- v! O* Y% L
         * @param value value值" Y* m4 O! N6 M/ l* c
         * @param <T> 返回值泛型; h3 E( n( c7 s; ?! P- o
         * @param time 秒值8 a' R! x: K- c4 L. L) [' o) ]
         * @return 正确的值:<T> 错误的值:null
    * T# p1 @! x! o& m/ l8 u; V7 D0 i     */- W) y4 t: o  c2 Q3 N  W5 O2 V1 l
        @SuppressWarnings("unchecked"); G* V- J; `4 t2 n' }) L0 [
        public <T> ValueOperations<String, T> setObjectForMillSeconds(final String key, final T value, final long time) {
    * a% Q: X+ y( ~0 Y/ y# p0 a        final ValueOperations<String, T> operation = redisTemplate.opsForValue();
    ; n5 ?; ~0 i$ {/ g        operation.set(key, value, time, TimeUnit.MILLISECONDS);
    3 |. P9 D1 j, D. U, X3 H        return operation;& F' `, {) q) [: L2 ?) y1 y& r
        }
    3 z9 y- w6 j; U' z" Y# o. F, J( v# M: C+ L+ l' q& ]8 u
        /**
    0 n( E+ T$ E- S* Y  H" ]     * 获取对象/ K1 X- r! }' j) r# @7 A
         *
    9 F) d* ?& ]. u/ {( v3 h- y: M5 R     * @param key 键# N# P+ {7 k9 N1 H8 S$ y/ \
         * @return 正确的值:Object值对象<br>) X2 Y0 ?5 S- O2 l2 E* f
         * 错误的值:null
    0 ]# o+ e8 ^7 `3 U3 R+ Z$ w     */$ i9 r* Y/ K$ f+ Z& e: [; w
        @SuppressWarnings("unchecked")
    . h, F. |. F/ w0 ]5 L  E9 y& c$ ~1 f    public Object getObject(final String key) {  B0 a! }! F% S
            final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();5 J; |, S: p1 H
            if (valueOperations == null || !redisTemplate.hasKey(key)) {
    , ]& x! {  v  C' x            return null;
    4 i2 B- z' f3 {7 f; ^5 Q/ _        }0 t, U3 s  t; H* d" |% u0 a
            final Object object = valueOperations.get(key);
    7 q. }1 M' ]9 k; T2 S. [        return object;
    % t  q( ]7 s; H, C: h    }+ @/ D# a: `. T, R; ?

    8 b6 @: |; F4 G- Z    /**
    ' k( x" K+ Z3 }- i     * 从缓存中获取string值
    1 h' z3 P) x( h  S, O( G$ l. |' c     *
    6 Z* \' t" C) ]1 B% b     * @param key, O  O0 m# Q9 z  s* N' V4 [
         * @return*/
    2 X% g5 I  z6 R& h* `+ K5 G    @SuppressWarnings("unchecked")9 ~* b! w7 T$ J5 M3 ^$ v% \& A
        public String getString(final String key) {) S: ~+ A! X8 Y
            String value = "";
    , }; \  {9 O. d, f+ F        final ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
    * {7 N& y) Q, d# ?7 q- u4 K        if (valueOperations != null && redisTemplate.hasKey(key)) {. M1 \' j& Q8 e
                final Object object = valueOperations.get(key);
    , r" b  a9 [" f            if (null != object) {
    ; i1 q4 T, h' m: @                LOGGER.info("--getString--object not empty");  T9 y; S% o2 Y) F! f
                    value = object.toString();
    8 h( P. Z! {3 \            } else {; M* |+ H0 p( A2 R
                    LOGGER.info("--getString--object empty");1 g& N5 ]8 h2 M. K! n% l; ~
                }
    ' |5 k3 ?% F( `6 T        }
    : v7 P. G! [! H        return value;
    $ m5 x* z; L5 C& F! Q; X8 n! {    }
    ' n: P  p/ U/ Z+ i1 M2.2:在redis中实现时间控制; j9 F! [. D. Y/ K& K

    . V5 z% q/ V0 e& O' i9 w0 P0 L2.2.1:在流程中停留一段时间,通过无限循环来不断的从redis取数值,一旦取到的值为null(redis的键值为null)就退出,这样的写法有点类似于以前CAS的些许味道,通过无限循环比较值。
    ! l5 Y. n3 g/ \
    2 C" e0 p) J" r! }. Nimport com.youjia.orders.redis.RedisManager;
    . ?9 q6 ?) \5 p, Y) Yimport org.junit.Test;; L9 Y6 v  e7 f( L- u1 M/ m# Z
    import org.springframework.beans.factory.annotation.Autowired;
    " u4 W6 U: e0 p) p/ S
    % B  E4 E3 A5 Q1 a: j- |1 _import java.util.Objects;
    4 I9 p" I+ K) G3 n% Y4 m0 I" D  V1 j/ T& \9 x1 w
    /**
    " U3 Y, [$ a9 ]& L3 o) R+ J * @Auther: Yrion+ U* O& N9 {# q# w
    * @Date: 2019-01-11 23:36
    ' g( G- B: H5 H+ {3 n9 _ */
    3 S! M: f# O; ]( k
    6 y5 e5 R% c3 u/ y8 {+ c  W1 Z, zpublic class RedisTest extends OrderProviderApplicationTests {
    4 w# p$ P' b0 k) p1 ^  E$ O/ }3 w' ]. w; b
        @Autowired
    1 i1 a  f0 l) ]6 n7 S* m' A# B1 O. A    private RedisManager redisManager;
    ; g4 b0 u: U! y; o. n! L2 e* e/ h; N3 t0 M+ }7 R+ {
        @Test
    7 X/ f/ \: a" s" H) R. I    public void test() {9 f% P% h/ |) y$ H5 p9 z# |( Q/ R/ I
            controlTime("10000001", 10L);
    9 P7 l; L5 O. G( v    }4 |/ l: h4 ?7 y5 a. x9 F8 j

    0 R: w7 v& n" M! U2 p    public void controlTime(String requestId, Long timeOut) {1 @) ]# K; r6 s" y& _7 b, Q( o" x0 p

    + |$ I. S2 o( H0 G        if (Objects.isNull(requestId) || Objects.isNull(timeOut)) {
    9 N' E8 B7 K/ }$ M% v            return;. E# @& w' V  B  ]% q5 ]( }
            }
    & |. z* L9 e" {        //something code
    2 O7 [# G7 h% B9 x8 @5 k$ O        final String value = "value";
    5 w- _, g: _# l1 ^( B4 w1 s        redisManager.setObject(requestId, value, timeOut);
    : }; B$ v/ ^& W6 I* M* L        final long startTime = System.currentTimeMillis();2 {: ^3 M) X0 N' v, B
            System.out.println("开始控制时间");
    6 m4 B9 o! y( O        //start! j2 R, i! ~; C1 Y8 _. v
            for (; ; ) {
    0 {4 F* t6 R0 _2 d9 m/ k$ f9 v+ N! p% w            if (Objects.isNull(redisManager.getObject(requestId))) {1 f, K+ z. ?6 y
                    break;
    $ l* |/ d' u& x3 J            }6 s' E( l, A& w" L; Z; d% z, X
            }
    * |# Q. a' @" \  T        final long endTime = System.currentTimeMillis();
    7 r: y$ Y- G, e0 _+ z5 p4 F  K
    + r- \: Y  H6 R- u9 G9 }. f) M& }        final long useTime = endTime - startTime;
    & Y/ `8 }: g2 c7 b- r
    % w* @) R1 ]8 O/ s        System.out.println("一共耗费时间:" + useTime);( c7 t' X9 m$ M) z) T" J
        }
    9 E& D: U0 u, U" Y}
    $ M% u) t) A/ R! d1 J7 ?& joutPut:
    9 {: |1 C" {, ]( @8 n0 ?  @2 o& ?1 K7 N
    开始控制时间$ Q0 O! J8 j6 G$ f, b$ O
    一共耗费时间:100422 R: h" k6 G0 n6 p" V
    三:总结9 c3 d6 L. K, \7 U- \! h. Z! c

    ! m' D1 }0 z3 ]- m; Y/ W本篇博文讲述了在平时工作中,我们可能会遇到的一些关于时间控制的问题,在这个问题上我又进行了进一步的探讨,如何实现优雅的解决问题?我们解决问题不仅仅是要把这个问题解决了,而是要考虑如何更好更秒的解决,这就要善于利用一些中间件或者工具类提供的功能特性,善于发现、及时变通,把这种特性利用到我们的代码中,会对我们的开发起到推波助澜、如虎添翼的作用!
    & |; y4 v; c9 b6 {————————————————
    . n: \4 X( J  n% z版权声明:本文为CSDN博主「大数据架构师李旭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。! `0 F+ x4 ?7 i6 s& H
    原文链接:https://blog.csdn.net/dashujujiagoushi/article/details/105893325
    5 L; z/ C+ @7 Y* Q* U+ _
    " |4 @2 @1 p+ ^. o! g
    , U# F- {. A. ^  ], J
    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-13 22:32 , Processed in 0.496264 second(s), 57 queries .

    回顶部