恕我直言,我怀疑你并不会生成随机数

有一次,我在逛 Stack Overflow 的时候,发现有这样一个问题:“Java 中如何产生一个指定范围内的随机数”,我心想,“就这破问题,竟然有 398 万的阅读量,统计确定没搞错?不就一个 Math.random() 的事儿嘛。”

于是我直接动用自己的权力投了一票反对。结果,没等到权力执行后的喜悦,却收到了一条提醒:“声望值低于 125 的人有投票权,但不会公开显示。”我呀,我去,扎心了。就冲我这急脾气,不用代码证明一下自己的实力,我还有脸说自己有十年的开发经验吗?于是我兴冲冲地就打开 IDEA,敲下了下面这段代码:

public class GenerateMathRandomInteger {
    public static void main(String[] args) {
        int leftLimit = 2;
        int rightLimit = 11;

        Runnable r = () -> {
            int generatedInteger = leftLimit + (int) (Math.random() * rightLimit);
            System.out.println(generatedInteger);
        };
       for (int i = 1; i < 10; i++) {
           new Thread(r).start();
       }
    }
}

这段代码我写得没毛病吧?乍看上去,参数和类的命名都很合理,就连 Lambda 表达式也用上了。但程序输出的结果却出乎我的意料:

8
10
10
4
3
4
6
12
3

12 是从哪里蹦出来的?当然是从程序的 bug 里蹦出来。leftLimit + (int) (Math.random() * rightLimit) 生成的随机数可能超出指定的范围。不行,Math.random() 信不过,必须要换一种方法。灵机一动,我想到了 Random 类,于是我写下了新的代码:

public class GenerateRandomInteger {
    public static void main(String[] args) {
        int leftLimit = 2;
        int rightLimit = 11;

        Random random = new Random();
        int range = rightLimit - leftLimit + 1;

        Runnable r = () -> {
            int generatedInteger = leftLimit + random.nextInt() % range;
            System.out.println(generatedInteger);
        };
       for (int i = 1; i < 10; i++) {
           new Thread(r).start();
       }
    }
}

这一次,我满怀信心,Math.random() 解决不了的问题,random.nextInt() 就一定能够解决。结果,输出结果再次啪啪啪打了我这张帅脸。

0
-3
10
2
2
-4
-4
-6
6

竟然还有负数,这真的是残酷的现实,我被教育了,似乎找回了刚入职那会被领导蹂躏的感觉。幸好,我的心态已经不像年轻时候那样易怒,稳得一匹:出问题不要紧,找解决方案就对了。

于是 5 分钟后我写出了下面这段代码:

public class GenerateRandomInteger {
    public static void main(String[] args) {
        int leftLimit = 2;
        int rightLimit = 11;

        Random random = new Random();
        int range = rightLimit - leftLimit;

        Runnable r = () -> {
            int generatedInteger = leftLimit + (int)(random.nextFloat() * range);
            System.out.println(generatedInteger);
        };
       for (int i = 1; i < 10; i++) {
           new Thread(r).start();
       }
    }
}

无论是调整线程的数量,还是多次重新运行,结果都符合预期,在 2 - 11 之间。

7
2
5
8
6
2
9
9
7

nextFloat() 方法返回一个均匀分布在 0 - 1 之间的随机浮点数(包含 0.0f,但不包含 1.0f),乘以最大值和最小值的差,再强转为 int 类型就可以保证这个随机数在 0 到(最大值-最小值)之间,最后再加上最小值,就恰好可以得到指定范围内的数字。

如果你肯读源码的话,会发现 Random 类有一个 nextInt(int bound) 的方法,该方法会返回一个随机整数,均匀分布在 0 - bound 之间(包含 0,但不包含指定的 bound)。那么利用该方法也可以得到一个有效的随机数,来看示例代码。

public class GenerateRandomNextInt {
    public static void main(String[] args) {
        int leftLimit = 2;
        int rightLimit = 11;

        Random random = new Random();
        Runnable r = () -> {
            int generatedInteger = leftLimit + random.nextInt(rightLimit - leftLimit + 1);
            System.out.println(generatedInteger);
        };
       for (int i = 1; i < 10; i++) {
           new Thread(r).start();
       }
    }
}

由于 nextInt() 不包含 bound,因此需要 + 1。程序运行的结果也符合预期:

8
2
9
8
4
6
4
5
7

你看,我之前两次尝试都以失败告终,但我仍然没有放弃希望,经过自己的深思熟虑,我又找到了两种可行的解决办法。这让我想起了普希金的一首诗歌:

假如生活欺骗了你,不要悲伤,不要心急,忧郁的日子里需要镇静,一切都会过去,一切都是瞬息,一切都会过去。希望之火需要再燃,需要呵护,不致让暴风雨将其熄灭,不致让自己在黑暗、阴冷、无助中绝望。

一首好诗吟完之后,我们再来想想还有没有其他的方案。反正我是想到了,Java 7 以后可以使用 ThreadLocalRandom 类,代码示例如下:

public class GenerateRandomThreadLocal {
    public static void main(String[] args) {
        int leftLimit = 2;
        int rightLimit = 11;

        Runnable r = () -> {
            int generatedInteger = ThreadLocalRandom.current().nextInt(leftLimit, rightLimit +1);
            System.out.println(generatedInteger);
        };
       for (int i = 1; i < 10; i++) {
           new Thread(r).start();
       }
    }
}

程序输出的结果如下:

11
9
6
10
6
6
10
7
3

ThreadLocalRandom 类继承自 Random 类,它使用了内部生成的种子来初始化(外部无法设置,所以不能再现测试场景),并且不需要显式地使用 new 关键字来创建对象(Random 可以通过构造方法设置种子),可以直接通过静态方法 current() 获取针对本地线程级别的对象:

static final ThreadLocalRandom instance = new ThreadLocalRandom();

static final void localInit() {
    int p = probeGenerator.addAndGet(PROBE_INCREMENT);
    int probe = (p == 0) ? 1 : p; // skip 0
    long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
    Thread t = Thread.currentThread();
    U.putLong(t, SEED, seed);
    U.putInt(t, PROBE, probe);
}

public static ThreadLocalRandom current() {
    if (U.getInt(Thread.currentThread(), PROBE) == 0)
        localInit();
    return instance;
}

这样做的好处就是,在多线程或者线程池的环境下,可以节省不必要的内存开销。

最后,我再提供一个解决方案,使用 Apache Commons Math 类库的 RandomDataGenerator 类。在使用该类库之前,需要在 pom.xml 文件中引入该类库的依赖。

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-math3</artifactId>
    <version>3.6.1</version>
</dependency>

在需要生成指定范围的随机数时,使用 new RandomDataGenerator() 获取随机生成器实例,然后使用 nextInt() 方法直接获取最大值与最小值之间的随机数。来看示例。

public class RandomDataGeneratorDemo {
    public static void main(String[] args) {
        int leftLimit = 2;
        int rightLimit = 11;

        Runnable r = () -> {
            int generatedInteger = new RandomDataGenerator().nextInt( leftLimit,rightLimit);
            System.out.println(generatedInteger);
        };
       for (int i = 1; i < 10; i++) {
           new Thread(r).start();
       }
    }
}

输出结果如下所示:

8
4
4
4
10
3
10
3
6

结果完全符合我们的预期——这也是我的最后一招,没想到就这么愉快地全交给你了。


好了,我亲爱的读者朋友,以上就是本文的全部内容了。如果觉得文章对你有点帮助,请微信搜索「 沉默王二 」第一时间阅读,回复「并发」更有一份阿里大牛重写的并发编程笔记。示例代码已经上传到 GitHub,传送门~

我是沉默王二,一枚有趣的程序员。原创不易,莫要白票,请你为本文点个赞吧,这将是我写作更多优质文章的最强动力。

-----我是沉默的分割线----

没想到这篇文章评论的人非常多,很多小伙伴都在纠正我应该好好看看 random() 方法,明明是我自己不会用。这种认真的 diss 我觉得非常棒,说明你们都是认真阅读文章并且乐于思考的读者。但我必须要再说一句,多往后看看其他的方案它不香吗?

  • 356
    点赞
  • 856
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 121
    评论
### 回答1: I'm sorry to hear that the customer is upset. Can you please provide more information about the issue so I can try to assist them? If they are requesting to speak with my supervisor, I would be happy to connect them with the appropriate person. However, if they are unwilling to share any information with me, it may be difficult for me to assist them effectively. ### 回答2: 当客户来投诉时,我首先向其表示歉意,表明我很重视他们的意见和感受。然后,我耐心地听取客户的投诉内容,并在听取客户的叙述时保持冷静和专注。我用礼貌的语言和态度与客户沟通,同时展示出对问题的重视和解决的决心。 当客户直言要求我的领导来处理时,我向客户解释我作为该业务单位的代表责任和权限范围,提供我可以主动解决问题的服务和解决方案,并说明上级领导可能需要更多时间来解决问题。 然后,我主动询问客户是否还有其他问题需要解决,并表示我愿意帮助他们寻找解决方案。如果客户仍然坚持要求我的领导来,我尽量理解和尊重客户的要求,并告知客户我尽快联系我的上级,并尽力将问题转达给他们。 在此之后,我尽快与我的上级进行沟通,将客户的投诉原因、细节和要求详细记录下来,并向上级报告客户的情况和要求。我请求上级给予合适的指导和解决方案,以满足客户的要求。 最后,我向客户诚挚地表示感谢,以及在得知上级的回复后,及时与客户沟通,并向客户传递上级的处理意见。若客户还有其他问题或疑虑,我耐心解答,并尽力恢复客户对我们的信任与满意度。无论最终结果如何,我保持专业和礼貌的态度来处理客户投诉,以确保客户感到被尊重和满意。 ### 回答3: 当一位生气的客户来投诉并要求见我的领导时,我做出以下的处理方式: 1. 耐心倾听:首先,我保持冷静和专注,认真倾听客户的投诉,并通过询问和回答问题来更好地理解他们的不满和需求。 2. 了解原因:我就客户投诉的具体原因进行详细了解,确保我完全理解他们的意见和问题。我主动提供一些可行的解决方案,并与客户共同探讨可能的解决途径。 3. 解释权责:我向客户解释我作为客户服务代表的职责和权限,将告知客户我所能为他们提供的帮助和解决问题的能力。同时,我保证他们的问题将得到妥善的处理和回应。 4. 寻求帮助:如果客户坚持要求与我的领导接触,我尊重客户的决定,并迅速联系我的领导,向他们简要介绍投诉情况,并要求他们提供支持和指导。 5. 快速响应:我尽快安排与我的领导和客户之间的面或电话交谈,以确保客户的问题能及时得到答复和解决。在此过程中,我紧密协调并持续跟进以确保客户的满意。 6. 反馈处理结果:一旦问题得到解决,我及时向客户反馈处理的结果,并诚挚地道歉,表达公司的关切和愿意改进的态度,同时邀请客户将对我们的服务进行评价,以期再次赢得他们的信任和满意。 总之,面对一位生气的客户投诉并要求见我的领导时,我以耐心、细心和解决问题的态度去处理,同时尽最大努力确保客户的满意。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 121
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沉默王二

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值