黑马全套Java教程(九):网络编程(一)+https://developer.aliyun.com/article/1556494
36.5 线程通信
线程通信就是线程间相互发送数据,线程通信通常通过共享一个数据的方式实现。
线程间会根据共享数据的情况决定自己该怎么做,以及通知其他线程怎么做
Account.java
package d7_thread_comunication; public class Account { private String cardId; private double money; //账户余额 public Account(){} public Account(String cardId, double money) { this.cardId = cardId; this.money = money; } public String getCardId() { return cardId; } public void setCardId(String cardId) { this.cardId = cardId; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; } public synchronized void drawMoney(double money){ try { String name = Thread.currentThread().getName(); if(this.money >= money){ this.money -= money; System.out.println(name + "来取钱" + money + "成功!余额是:" + this.money); //没钱了 this.notifyAll(); //唤醒所有线程 this.wait(); //锁对象,让当前线程进入等待 }else{ //钱不够 this.notifyAll(); //唤醒所有线程 this.wait(); //锁对象,让当前线程进入等待 } } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized void deposit(double money){ try { String name = Thread.currentThread().getName(); if(this.money == 0){ this.money += money; System.out.println(name + "存钱" + money + "成功!存钱后余额是:" + this.money); //有钱了,唤醒别人, this.notifyAll(); this.wait(); }else{ //有钱,不存钱 this.notifyAll(); this.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } } }
DrawThread.java
package d7_thread_comunication; /* * 取钱的线程类 * */ public class DrawThread extends Thread{ private Account acc; public DrawThread(Account acc, String name){ super(name); this.acc = acc; } @Override public void run(){ while(true){ acc.drawMoney(100000); try{ Thread.sleep(3000); }catch (Exception e){ e.printStackTrace(); } } } }
DepositThread.java
package d7_thread_comunication; public class DepositThread extends Thread{ private Account acc; public DepositThread(Account acc, String name){ super(name); this.acc = acc; } @Override public void run(){ while(true){ acc.deposit(100000); try{ Thread.sleep(3000); }catch (Exception e){ e.printStackTrace(); } } } }
ThreadDemo.java
package d7_thread_comunication; public class ThreadDemo { //目标:了解线程通信的流程 public static void main(String[] args) { // 1. 创建账户对象,代表5个人共同的操作的账户 Account acc = new Account("ICBC-112", 0); //2. 创建2个取钱线程 new DrawThread(acc, "小明").start(); new DrawThread(acc, "小红").start(); new DepositThread(acc, "亲爹").start(); new DepositThread(acc, "干爹").start(); new DepositThread(acc, "岳父").start(); } }
36.6 线程池
线程池就是一个可以复用线程的技术。如果用户每发起一个请求,后台就创建一个新线程来处理,下次新任务来了又要创建新线程,而创建新线程的开销是很大的,这样会严重影响系统的性能。
临时线程什么时候创建:新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。
什么时候会开始拒绝任务:核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始任务拒绝
ThreadPoolDemo1.java
package d8_threadpool; import java.util.concurrent.*; //目标:自定义一个线程池对象,并测试其特性 public class ThreadPoolDemo1 { public static void main(String[] args) { //1. 创建线程池对象 /* * public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { * */ ExecutorService pool = new ThreadPoolExecutor(3, 5, 6, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); // 2. 给任务线程池处理 Runnable target = new MyRunnable(); pool.execute(target); pool.execute(target); pool.execute(target); pool.execute(target); pool.execute(target); pool.execute(target); pool.execute(target); pool.execute(target); //创建临时线程 pool.execute(target); pool.execute(target); //不创建,拒绝策略被触发!!! //pool.execute(target); //pool.execute(target); //关闭线程池(开发中一般不会使用) //pool.shutdownNow(); //立即关闭,即使任务没有完成,丢失任务的 pool.shutdown(); //会等待全部任务执行完毕之后再关闭 } }
MyRunnable.java
package d8_threadpool; public class MyRunnable implements Runnable{ @Override public void run(){ for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName() + "输出了:HelloWorld ==> " + i); } try{ System.out.println(Thread.currentThread().getName() + "本任务与线程绑定了,线程进入休眠了"); Thread.sleep(10000000); }catch(Exception e){ e.printStackTrace(); } } }
例2:
ThreadPoolDemo2.java
package d8_threadpool; import java.util.concurrent.*; //目标:自定义一个线程池对象,并测试其特性 public class ThreadPoolDemo2 { public static void main(String[] args) throws Exception{ //1. 创建线程池对象 /* * public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { * */ ExecutorService pool = new ThreadPoolExecutor(3, 5, 6, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy()); // 2. 给任务线程池处理 Future<String> f1 = pool.submit(new MyCallable(100)); Future<String> f2 = pool.submit(new MyCallable(200)); Future<String> f3 = pool.submit(new MyCallable(300)); Future<String> f4 = pool.submit(new MyCallable(400)); System.out.println(f1.get()); System.out.println(f2.get()); System.out.println(f3.get()); System.out.println(f4.get()); } }
MyCallable.java
package d8_threadpool; import java.util.concurrent.Callable; //1. 定义一个任务类 实现Callable接口 应该声明线程任务执行完毕后的结果的数据类型 public class MyCallable implements Callable<String> { private int n; public MyCallable(int n){ this.n = n; } //2.重写call方法(任务方法) @Override public String call() throws Exception{ int sum = 0; for (int i = 0; i <= n ; i++) { sum += i; } return Thread.currentThread().getName() + "执行1-" + n + "的和,结果是:" + sum; } }
方法二:Executors创建线程池线程池
ThreadPoolDemo3.java
package d8_threadpool; import java.util.concurrent.*; //目标:使用Executors的工具方法直接得到一个线程池对象 public class ThreadPoolDemo3 { public static void main(String[] args) throws Exception{ //1. 创建固定线程数据的线程池 ExecutorService pool = Executors.newFixedThreadPool(3); pool.execute(new MyRunnable()); pool.execute(new MyRunnable()); pool.execute(new MyRunnable()); pool.execute(new MyRunnable()); //已经没有多余线程了 } }
36.7 定时器
定时器是一种控制任务延时调用,或者周期调用的技术
作用:闹钟、定时邮件发送
定时器的实现方式:
方式一:Timer
方式二:ScheduledExecutorService
方法一:Timer定时器是单线程执行,会有问题
package d9_timer; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TimerDemo1 { public static void main(String[] args) { //1. 创建Timer定时器 Timer timer = new Timer(); //定时器本身就是一个单线程 //2. 调用方法,处理定时任务 timer.schedule(new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "执行A " + new Date()); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } }, 3000, 2000); timer.schedule(new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "执行B " + new Date()); } }, 3000, 2000); } }
Timer-0执行A Sat Nov 05 20:47:33 CST 2022 Timer-0执行B Sat Nov 05 20:47:38 CST 2022 Timer-0执行A Sat Nov 05 20:47:38 CST 2022 Timer-0执行A Sat Nov 05 20:47:43 CST 2022 Timer-0执行B Sat Nov 05 20:47:48 CST 2022 Timer-0执行A Sat Nov 05 20:47:48 CST 2022
方法二:ScheduledExecutorService定时器
例:线程池做定时器
package d9_timer; import java.util.Date; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class TimerDemo2 { public static void main(String[] args) { //1. 创建ScheduledExecutorService线程池,做定时器 ScheduledExecutorService pool = Executors.newScheduledThreadPool(3); //2. 开启定时任务 pool.scheduleAtFixedRate(new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "执行输出:AAA " + new Date()); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } }, 0, 2, TimeUnit.SECONDS); //2秒 pool.scheduleAtFixedRate(new TimerTask() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "执行输出:BBB " + new Date()); } }, 0, 2, TimeUnit.SECONDS); //2秒 } }
36.8 并发并行、生命周期
并发剑法
黑马全套Java教程(九):网络编程(三)+https://developer.aliyun.com/article/1556507