目录
一.案例:五个人抢红包
二.案例:两个抽奖池抽奖
三.案例:两个抽奖池抽奖:获取线程运行的结果
四.线程池:用来存放线程,避免多次重复创建线程
五.自定义线程池
六.最大并行数与线程池大小
一.案例:五个人抢红包
1.如何保证只留下两位小数,并且计算精确呢? 使用大浮点数类,使用setScale方法保留小数 2.如何保证随机的数字最小是一分钱呢? 设置最小中奖金额,如果随机到的数字小于这个数的话,就让这个数变成这个最小金额 同时也需要保证最大的金额不会大到把剩下人的一分钱(最小金额)也给吞掉了 3.如果说是最后一个抽到红包的,还应该去随机数字吗 直接把剩余的钱款给最后一个即可 在这里的随机金额中先抢的概率更大-这或许也是不合理的地方
public class Demo321 {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
MyThread t4 = new MyThread();
MyThread t0 = new MyThread();
t1.start();
t2.start();
t3.start();
t4.start();
t0.start();
}
}
public class MyThread extends Thread {
static BigDecimal money = BigDecimal.valueOf(100);
static int count = 3;
static final BigDecimal MIN = BigDecimal.valueOf(0.01);
@Override
public void run() {
synchronized (MyThread.class) {
if (count == 0) {
System.out.println(Thread.currentThread().getName() + "来晚一步!");
return;
}
BigDecimal price;
if (count == 1) price = money;
else
price = BigDecimal.valueOf(new Random().nextDouble(MIN.doubleValue(),
money.subtract(BigDecimal.valueOf(count-1).multiply(MIN)).doubleValue()));
count--;
price = price.setScale(2, RoundingMode.HALF_UP);
money = money.subtract(price);
System.out.println(Thread.currentThread().getName() + "抢到了" + price + "元");
}
}
}
二.案例:两个抽奖池抽奖
public class Demo322 {
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t0 = new MyThread();
t1.start();
t0.start();
System.out.println("到这一行就开启了三条线程");
System.out.println("main方法是运行在main线程中的栈,线程12开启的时候在内存中会多出两个栈:线程一的方法栈和线程2的方法栈");
}
}
public class MyThread extends Thread {
static ArrayList<Integer> arrayList;
static {
arrayList = new ArrayList<>();
Collections.addAll(arrayList, 10, 5, 20, 50, 100, 200, 500, 800, 2, 80, 300, 700);
}
@Override
public void run() {
ArrayList<Integer> myArraylist = new ArrayList<>();
while (true) {
synchronized (MyThread.class) {
if (arrayList.size() == 0) break;
int index = new Random().nextInt(arrayList.size());
Integer i = arrayList.get(index);
arrayList.remove(index);
myArraylist.add(i);
}
}
synchronized (Thread.class) {
if (myArraylist.size() == 0) {
System.out.println("在此抽奖过程中,"+getName() + "没有产生任何奖项");
return;
}
System.out.println("在此抽奖过程中," + getName() + "共产生了" + myArraylist.size() + "个奖项" + "分别为");
StringJoiner sj = new StringJoiner(",");
Integer count = 0;
Integer max = myArraylist.get(0);
for (Integer integer : myArraylist) {
sj.add(integer + "");
count += integer;
if (integer > max) max = integer;
}
System.out.println(sj + "," + "最高奖项为" + max + "元," + "总计额为" + count + "元");
}
}
}
三.案例:两个抽奖池抽奖:获取线程运行的结果
public class Demo323 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable();
System.out.println("需要注意的点:这里要创建两个多线程运行结果的管理对象");
FutureTask<Integer> futureTask0 = new FutureTask<>(myCallable);
FutureTask<Integer> futureTask1= new FutureTask<>(myCallable);
Thread t0 = new Thread(futureTask0);
Thread t1 = new Thread(futureTask1);
t0.start();
t1.start();
Integer i0 = futureTask0.get();
Integer i1 = futureTask1.get();
System.out.println("在这个抽奖过程中");
if (i1==null||i0>i1){
System.out.print(t0.getName()+"抽到了最大奖项,该奖金金额为:"+i0+"元");
}else {
System.out.print(t1.getName()+"抽到了最大奖项,该奖金金额为:"+i1+"元");
}
}
}
class MyCallable implements Callable<Integer> {
static ArrayList<Integer> arrayList;
static {
arrayList = new ArrayList<>();
Collections.addAll(arrayList, 10, 5, 20, 50, 100, 200, 500, 800, 2, 80, 300, 700);
}
@Override
public Integer call(){
ArrayList<Integer> myArraylist = new ArrayList<>();
while (true) {
synchronized (MyThread.class) {
if (arrayList.size() == 0) break;
int index = new Random().nextInt(arrayList.size());
Integer i = arrayList.get(index);
arrayList.remove(index);
myArraylist.add(i);
}
}
synchronized (Thread.class) {
if (myArraylist.size() == 0) {
System.out.println("在此抽奖过程中,"+Thread.currentThread().getName() + "没有产生任何奖项");
return null;
}
System.out.println("在此抽奖过程中," + Thread.currentThread().getName() + "共产生了" + myArraylist.size() + "个奖项" + "分别为");
StringJoiner sj = new StringJoiner(",");
Integer count = 0;
Integer max = myArraylist.get(0);
for (Integer integer : myArraylist) {
sj.add(integer + "");
count += integer;
if (integer > max) max = integer;
}
System.out.println(sj + "," + "最高奖项为" + max + "元," + "总计额为" + count + "元");
return max;
}
}
}
四.线程池:用来存放线程,避免多次重复创建线程
提交任务时,线程池会创建新的线程对象,任务执行完毕之后,线程归还给池子 1.下次再创建任务的时候还需要创建新的线程吗? 不需要再创建新的线程,复用已经有的线程即可 2.如果提交任务的时候池子里面没有空闲线程,就无法创建新的线程,任务会如何进行? 任务就会排队等待 3.线程池创建过程? 1.创建线程池 2.提交任务 3.所有任务执行完毕时,关闭线程池(实际开发中线程池一般不会关闭) 4.创建线程池的快速方法? 使用Executors(执行人)的静态方法创建线程池 5.如何创建无上限的线程池? 6.如何提交任务? 7.如何停止线程? 8.如何创建有上限的线程池?
public class Demo324 {
public static void main(String[] args) throws InterruptedException {
System.out.println("5.使用Executors的newCachedThread方法获取线程池对象(cached-缓存),无上限");
ExecutorService pool = Executors.newCachedThreadPool();
System.out.println("6.使用线程池的submit方法提交任务,这里的输出的线程池名字与线程名字编号都是从1开始的");
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
Thread.sleep(1000);
//在这里睡了一小会之后使用的还是原来的线程:不会创建新的线程(用线程一还是线程二是随机的)
pool.submit(new MyRunnable());
Thread.sleep(1000);
System.out.println("7.使用线程池的shutdown方法停止线程");
pool.shutdown();
Thread.sleep(1000);
System.out.println("8.使用Executors的newFixedThreadPool方法创建有上限的线程池");
pool = Executors.newFixedThreadPool(1);
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
pool.shutdown();
}
}
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
五.自定义线程池
自定义线程池 参数如下: 1.核心线程数量(核心员工) 2.线程中最大线程数量(包括临时员工) 3.4.临时线程空闲时间与单位(临时员工存活时间) 5.阻塞队列 6.创建线程的方式 7.要执行任务过多的解决方案 创建临时线程的时机是在什么时候? 阻塞队列满了之后才能创建,不是核心线程被占满了才创建,创建临时线程处理队伍之外的任务!
public class Demo325 {
public static void main(String[] args) {
ThreadPoolExecutor tpe = new ThreadPoolExecutor(
3,
6,
60,
TimeUnit.SECONDS,//时间单位
new ArrayBlockingQueue<>(3),//不想指定长度就写LinkedArrayBlockingQueue
Executors.defaultThreadFactory(),//创建线程工厂,底层是创建Thread线程
new ThreadPoolExecutor.AbortPolicy()//静态内部类(依赖于线程池,单独存在无意义),任务的默认拒绝策略:抛错
);
tpe.submit(new MyRunnable());
tpe.submit(new MyRunnable());
tpe.submit(new MyRunnable());
tpe.submit(new MyRunnable());
tpe.submit(new MyRunnable());
tpe.submit(new MyRunnable());
tpe.submit(new MyRunnable());
tpe.submit(new MyRunnable());
tpe.submit(new MyRunnable());
tpe.shutdown();
}
}
在上面代码基础上再加一个线程会报错:
六.最大并行数与线程池大小
最大并行数:和CPU有关 Intel利用-超线程技术-使得每个核有两个线程 在设备管理器可以查看 1.如何获取最大能够使用的线程? 2.线程池开多大合适呢? 项目一般分为:CPU密集型(计算比较多)和IO密集型(读写文件多) CPU密集型: PU运算比较多,最大并行数+1(1是备用池)即可 IO密集型(大多数项目): 最大并行数(8)*期望CPU利用率(占满100%)*[(计算时间+等待时间(等待IO流))/计算时间]这个需要用工具(thread dump)计算
public class Demo326 {
public static void main(String[] args) {
System.out.println("2.使用Runtime.getRuntime().availableProcessors()获取当前最多能够使用的线程");
System.out.println(Runtime.getRuntime().availableProcessors());
}
}