多线程代码笔记
1、创建线程
三种创建方式:继承Thread类、实现Runnable接口、实现Callable接口。
1.1继承Thread类
- 自定义线程类继承Thread类
- 重写run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
//创建线程方式一:继承Thread类,重写run()方法,调用start()开启线程
//线程开启后不一定立即执行,由CPU调度执行
public class ThreadDemo01 extends Thread{
@Override
public void run() {
//run方法线程体
for(int i=0;i<1000;i++){
System.out.println("run方法执行"+i);
}
}
public static void main(String[] args) {
//main线程,主线程
//创建一个线程对象
ThreadDemo01 threadDemo01 = new ThreadDemo01();
//调用start()方法开启线程
threadDemo01.start();
for (int i = 0; i < 1000; i++) {
System.out.println("main方法执行"+i);
}
}
}
//练习Thread,实现多线程同步下载图片
public class ThreadDemo02 extends Thread{
private String url; //网络图片地址
private String name; //保存的文件名
public ThreadDemo02(String url, String name) {
this.url = url;
this.name = name;
}
//下载图片线程的执行体
@Override
public void run() {
WebDownLoader webDownLoader = new WebDownLoader();
webDownLoader.downLoader(url,name);
System.out.println("下载的文件"+name);
}
public static void main(String[] args) {
ThreadDemo02 t1 = new ThreadDemo02("https://fanyi-cdn.cdn.bcebos.com/static/translation/widget/header/si/img/new_ba8f6bc.png","1.jpg");
ThreadDemo02 t2 = new ThreadDemo02("https://fanyi-cdn.cdn.bcebos.com/static/translation/widget/header/si/img/new_ba8f6bc.png","2.jpg");
ThreadDemo02 t3 = new ThreadDemo02("https://fanyi-cdn.cdn.bcebos.com/static/translation/widget/header/si/img/new_ba8f6bc.png","3.jpg");
ThreadDemo02 t4 = new ThreadDemo02("https://fanyi-cdn.cdn.bcebos.com/static/translation/widget/header/si/img/new_ba8f6bc.png","4.jpg");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
//下载器
class WebDownLoader{
//下载方法
public void downLoader(String url, String name){
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("downloader方法异常");
}
}
}
1.2实现Runnable接口
- 自定义MyRunnable类实现Runnable接口
- 实现run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
//创建线程方式2:实现Runnable接口,重写run方法,
// 执行线程需要丢入runnable接口实现类,调用start方法
public class RunnableDemo01 implements Runnable{
@Override
public void run() {
//run方法线程体
for(int i=0;i<1000;i++){
System.out.println("run方法执行"+i);
}
}
public static void main(String[] args) {
//创建Runnable接口实现类对象
RunnableDemo01 runnableDemo01 = new RunnableDemo01();
//new一个线程,将Runnable接口实现类对象丢入线程
Thread thread = new Thread(runnableDemo01);
//通过线程对象调用start()方法开启线程
thread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("main方法执行"+i);
}
}
}
- 小结
(1)继承Thread类,启动线程:子类对象.start()
不建议使用:为了避免OOP单继承局限性
(2)实现Runnable接口,启动线程:传如目标对象+Thread对象.start()
推荐使用:避免了单继承局限性,方便同一个对象被多个线程使用。
1.3初识并发问题
//并发买火车票出现问题
//多个线程操作同一个资源时,线程不安全,数据紊乱
public class RunnableDemo03 implements Runnable{
private int ticketnum=50;
@Override
public void run() {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (true){
if (ticketnum <= 0) {
break;
}
System.out.println(Thread.currentThread().getName()+"-->买到第 "+ticketnum--+" 张票");
}
}
public static void main(String[] args) {
//创建Runnable接口实现类对象
RunnableDemo03 runnableDemo03 = new RunnableDemo03();
//new一个线程,将Runnable接口实现类对象丢入线程,通过线程对象调用start()方法开启线程
new Thread(runnableDemo03,"xiaoming").start();
new Thread(runnableDemo03,"honghong").start();
new Thread(runnableDemo03,"jingjing").start();
new Thread(runnableDemo03,"beibei").start();
new Thread(runnableDemo03,"blue").start();
}
}
1.4龟兔赛跑
//模拟龟兔赛跑
public class Race implements Runnable {
//胜利者
private String winner;
@Override
public void run() {
for (int i = 1; i <= 1000; i++) {
//模拟乌龟速度
if (Thread.currentThread().getName().equals("乌龟")) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//模拟兔子睡觉
if (Thread.currentThread().getName().equals("兔子") && i==700) {
try {
System.out.println("兔子休息了");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
//判断比赛是否结束了
boolean flag = gameOver(i);
//如果结束就停止程序
if (flag) {
//break;
System.exit(0);
}
}
}
//判断比赛是否结束
private boolean gameOver(int steps){
//判断是否有胜利者
if (winner != null) {
return true;
}else if (steps == 1000){
winner = Thread.currentThread().getName();
System.out.println("winner is "+winner);
return true;
}
return false;
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
1.5实现Callable接口
1.实现Callable接口,需要返回值类型
2.重写call方法,需要抛出异常
3.创建目标对象
4.创建执行服务ExecutorService ser = Executors.newFixedThreadPool(1);
5.提交执行:Future result1 = ser.submit(t1);
6.获取结果:boolean r1 = result1.get()
7.关闭服务器:ser.shutdownNow();
2、静态代理模式
//静态代理模式总结:
//真实对象和代理对象都要实现同一个接口
//代理对象要代理真实角色
//---好处:
//------代理对象可以做很多真实对象做不了的事
//------真实对象可以专注做自己的事
public class StaticProxy {
public static void main(String[] args) {
You you = new You();
//you.HappyMarry();//直接结婚
//将you给婚庆公司,然后代理结婚
WeddingCompany weddingCompany = new WeddingCompany(new You());
weddingCompany.HappyMarry();
}
}
interface Marry{
void HappyMarry();
}
//真实角色,你去结婚
class You implements Marry{
@Override
public void HappyMarry() {
System.out.println("结婚了,新娘最美");
}
}
//代理角色,帮助你结婚
class WeddingCompany implements Marry{
private Marry target;//代理谁,真实目标角色
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void HappyMarry() {
before();
this.target.HappyMarry();//真实对象
after();
}
private void after() {
System.out.println("收钱了");
}
private void before() {
System.out.println("布置和主持");
}
}
3、Lamda表达式
3.1为什么使用Lamda表达式
- 避免匿名内部类定义过多。
- 使代码更简洁。
- 去掉了一堆没有意义的代码,只留下核心逻辑。
3.2函数式接口
- 只包含唯一一个抽象方法的接口。
- 对于函数式接口,可以通过lambda表达式来创建改接口的对象。
- 例如,Runnable接口就是函数式接口。
//Lambda表达式推导
public class LambdaDeduce {
//2静态内部类
static class Like2 implements ILike{
@Override
public void lambda() {
System.out.println("I like Lambda2");
}
}
public static void main(String[] args) {
ILike like = new Like1();
like.lambda();
new Like2().lambda();
//3局部内部类
class Like3 implements ILike{
@Override
public void lambda() {
System.out.println("I like Lambda3");
}
}
new Like3().lambda();
//4匿名内部类
like = new ILike() {
@Override
public void lambda() {
System.out.println("I like Lambda4");
}
};
like.lambda();
//5用Lambda表达式简化
like = ()->{
System.out.println("I like Lambda5");
};
like.lambda();
}
}
//定义一个函数式接口
interface ILike{
void lambda();
}
//1实现类
class Like1 implements ILike{
@Override
public void lambda() {
System.out.println("I like Lambda1");
}
}
public class LambdaTest {
public static void main(String[] args) {
/*//用Lambda表达式的前提下是接口是函数式接口,
lambda表达式只能在只有一行时,才能简化成一行,多行时要用代码块包裹,
可以去掉参数类型。*/
ILove love = (a,b,c)->{
System.out.println("I like");
System.out.println("I like Lambda5-->"+a+b+c);
};
love.lambda(13,14,"520");
}
}
//定义一个函数式接口
interface ILove{
void lambda(int a, int b,String c);
}
4、线程的状态
4.1线程停止
- 不推荐使用JDK提供的stop()和destroy()等方法,推荐线程自己停下来,建议使用一个标志位进行终止变量,当flag= false,则终止线程运行。
//测试停止线程
//1、建议线程正常停止---利用次数,不建议死循环
//建议使用标志位---设置一个标志位
//不使用自带的stop或者destroy等JDK不建议使用的方法
public class ThreadStopDemo implements Runnable{
//1.设置一个个标志位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag){
System.out.println("Thread is running----" + i++);
}
}
//2.设置一个公开的方法停止线程,转换标志位
public void myStop(){
this.flag = false;
}
public static void main(String[] args) throws InterruptedException {
ThreadStopDemo threadStopDemo = new ThreadStopDemo();
new Thread(threadStopDemo).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main线程-----" + i);
if (i == 998) {
//调用myStop方法切换标志位,让线程停止
threadStopDemo.myStop();
System.out.println("线程停止");
}
}
}
}
4.2线程休眠
- 每一个对象都有一个锁,sleep不会释放锁。
- sleep可以模拟网络延迟、倒计时等。
- sleep指定当前线程阻塞的毫秒数,时间到后,线程进入就绪态。
- sleep存在异常InterruptedException 。
//模拟网络延时:放大问题的发生性
public class ThreadSleepDemo implements Runnable{
private int ticketnum=50;
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (true){
if (ticketnum <= 0) {
break;
}
System.out.println(Thread.currentThread().getName()+"-->买到第 "+ticketnum--+" 张票");
}
}
public static void main(String[] args) {
//创建Runnable接口实现类对象
ThreadSleepDemo tsd = new ThreadSleepDemo();
//new一个线程,将Runnable接口实现类对象丢入线程,通过线程对象调用start()方法开启线程
new Thread(tsd,"xiaoming").start();
new Thread(tsd,"honghong").start();
new Thread(tsd,"jingjing").start();
new Thread(tsd,"beibei").start();
new Thread(tsd,"blue").start();
}
}
//模拟倒计时
public class ThreadSleepDemo2{
public static void main(String[] args) {
tenDown();
}
public static void tenDown(){
int num =10;
while(true){
//获得系统当前时间
Date time = new Date(System.currentTimeMillis());
//输出系统当前时间
System.out.println(new SimpleDateFormat("HH:mm:ss").format(time));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(num--);
//刷新系统当前时间
time = new Date(System.currentTimeMillis());
if (num <= 0) {
break;
}
}
}
}
4.3线程礼让
- 礼让线程,让当前正在执行的线程暂停,但不阻塞。即让线程从运行态转为就绪态。
- 让CPU重新调度,礼让不一定成功。
//测试礼让线程
public class ThreadYieldDemo implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();//线程礼让
System.out.println(Thread.currentThread().getName()+"线程结束");
}
public static void main(String[] args) {
ThreadYieldDemo yieldDemo = new ThreadYieldDemo();
new Thread(yieldDemo,"a").start();
new Thread(yieldDemo,"b").start();
new Thread(yieldDemo,"c").start();
}
}
4.4线程强制执行
- join合并线程,待此线程执行完后,再执行其他线程,其他线程阻塞,即插队。
//join,强制执行
public class ThreadJoinDemo implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("执行"+i+Thread.currentThread().getName());
}
}
public static void main(String[] args) throws InterruptedException {
ThreadJoinDemo joinDemo = new ThreadJoinDemo();
Thread t1 = new Thread(joinDemo,"aaaa");
Thread t2 = new Thread(joinDemo,"bbbb");
Thread t3 = new Thread(joinDemo,"cccc");
t1.start();
t2.start();
t3.start();
for (int i = 0; i < 100; i++) {
//当i=30时,t1线程强制执行
//t1线程结束前,t2/t3线程可以执行,主线程不能执行
if(i == 30){
t1.join();//插队
}
System.out.println(i);
}
}
}
4.5观测线程状态
//观测线程的状态
public class TestThreadState{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sleep_over");
});
//观测状态
Thread.State state = thread.getState();
System.out.println(state);//new
thread.start();
System.out.println(thread.getState());//Runnable
//只要线程不停,就一直输出线程状态
while(thread.getState() != Thread.State.TERMINATED){
Thread.sleep(10);
System.out.println(thread.getState());
}
}
}
4.6线程的优先级
- 线程优先级越大,先运行的概率越大
//测试线程优先级
public class TestPriority {
public static void main(String[] args) {
MyPriority priority = new MyPriority();
Thread t0 = new Thread(priority);
Thread t1 = new Thread(priority);
Thread t2 = new Thread(priority);
Thread t3 = new Thread(priority);
Thread t4 = new Thread(priority);
Thread t5 = new Thread(priority);
//先设置优先级再启动
t0.setPriority(8);
t1.setPriority(7);
t2.setPriority(2);
t3.setPriority(Thread.MAX_PRIORITY);
t4.setPriority(Thread.MIN_PRIORITY);
t5.start();//默认优先级
t2.start();
t3.start();
t4.start();
t1.start();
t0.start();
//主线程默认的优先级为5
System.out.println(Thread.currentThread().getName()+
"--->"+Thread.currentThread().getPriority());
}
}
class MyPriority implements Runnable{
@Override
public void run() {
//输出线程名优先级
System.out.println(Thread.currentThread().getName()+
"--->"+Thread.currentThread().getPriority());
}
}
4.7守护线程daemon
- 线程分为用户线程和守护线程。
- 虚拟机必须确保用户线程执行完毕,不用等待守护线程执行完毕。
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
People people = new People();
Thread tg = new Thread(god);
//默认是false,表示用户线程,正常的线程都是用户线程
tg.setDaemon(true);
tg.start();//守护线程启动
new Thread(people).start();//用户线程启动
}
}
//神
class God implements Runnable{
@Override
public void run() {
int i = 0;
while(true){
System.out.println("神守护人"+ i++);
}
}
}
//人
class People implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("人活着"+ i);
}
System.out.println("人死了");
}
}
5、线程同步
(1)处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象,这时候就需要线程同步。
(2)线程同步是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池,形成队列,等待前面的线程使用完毕,下一个线程再使用。
(3)由于同一进程的多个线程共享同一块存储空间,存在访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制(synchronized),当一个线程获得对象的排它锁,独占资源,其他线程必须等待,然后释放锁。这个过程存在以下问题:
- 一个线程持有锁会导致其他所有需要此锁的线程挂起。
- 在多线程竞争下,加锁、释放锁,会导致比较多的上下文切换和调度延时,引起性能问题。
- 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题。
5.1三大不安全案例
//线程不安全的集合
public class UnsafeList {
public static void main(String[] args) throws InterruptedException {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(500);
System.out.println(list.size());
}
}
//不安全的银行取钱,两个人,同时取
public class UnsafeBank {
public static void main(String[] args) {
Account account = new Account(1000, "共同的钱");
Drawing d1 = new Drawing(account,600);
Drawing d2 = new Drawing(account,700);
new Thread(d1,"wo").start();
new Thread(d2,"ni").start();
}
}
//账户
class Account{
int money;//账户的钱
String accname;
public Account(int money, String accname) {
this.money = money;
this.accname = accname;
}
}
//模拟取钱
class Drawing implements Runnable{
Account account;//账户
int drawingMoney;//被取的钱数
int nowMoney;//手里的钱
public Drawing(Account account, int drawingMoney) {
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
System.out.println("账户余额"+account.money);
//判断有没有钱
if (account.money-drawingMoney <=0) {
System.out.println(Thread.currentThread().getName()+"钱不够了");
return;
}
try {
Thread.sleep(1000);//模拟延时
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money = account.money-drawingMoney;
nowMoney = nowMoney+drawingMoney;
System.out.println(Thread.currentThread().getName()+nowMoney);
System.out.println("账户余额"+account.money);
}
}
//不安全的买票
//线程不安全,有负数和0
public class UnsafeTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket,"ming").start();
new Thread(buyTicket,"aaaaa").start();
new Thread(buyTicket,"bbb").start();
new Thread(buyTicket,"xxxx").start();
}
}
//买票
class BuyTicket implements Runnable{
//票
private int ticketNums = 20;
boolean flag = true;//外部停止标志
@Override
public void run() {
while(flag){
buy();
}
}
private void buy(){
//判断是否有票
if(ticketNums<=0){
flag = false;
return;
}
//模拟延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//买票
System.out.println(Thread.currentThread().getName()+"买到"+ticketNums--);
}
}
5.2 synchronized
-
同步方法
- 加 synchronized 修饰方法,synchronized锁的是 this.
-
同步代码块
-
synchronized(对象){代码块}
-
锁的对象是需要增删改的量
-
//线程不安全的集合
public class UnsafeList {
public static void main(String[] args) throws InterruptedException {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
synchronized (list){
list.add(Thread.currentThread().getName());}
}).start();
}
Thread.sleep(500);
System.out.println(list.size());
}
}
5.3 CopyOnWriteArrayList
//测试JUC安全类型的集合CopyOnWriteArrayList<String>
public class TestJUC {
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10000; i++) {
list.add(Thread.currentThread().getName());
}
Thread.sleep(500);
System.out.println(list.size());
}
}
5.4死锁
- 多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或多个线程都在等待对方释放资源,都停止执行的情形,某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生死锁的问题。
//死锁:多个线程相互拿着对方需要的资源,然后形成僵局。
public class DeadLock {
public static void main(String[] args) {
Makebeauty m1 = new Makebeauty(0);
Makebeauty m2 = new Makebeauty(1);
new Thread(m1,"aaaaa").start();
new Thread(m2,"bbbbb").start();
}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
//化妆
class Makebeauty implements Runnable{
//需要的资源只有一份
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choise;//选择
public Makebeauty(int choise) {
this.choise = choise;
}
@Override
public void run() {
//化妆,互相持有对方的锁,就是需要拿到对方的资源
if (choise == 0){
synchronized(lipstick){//获得口红的锁
System.out.println(Thread.currentThread().getName()+"获得口红的锁");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized(mirror){//一秒后获得镜子的锁
System.out.println(Thread.currentThread().getName()+"获得镜子的锁");
}
}else{
synchronized(lipstick){//获得镜子的锁
System.out.println(Thread.currentThread().getName()+"获得镜子的锁");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized(mirror){//一秒后获得口红的锁
System.out.println(Thread.currentThread().getName()+"获得口红的锁");
}
}
}
}
5.5 Lock锁
- ReentrantLock(),可重入锁
public class TestLock implements Runnable{
int ticketNum = 20;
//ReentrantLock(),可重入锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run(){
while (true){
try{
lock.lock();//加锁
if(ticketNum>0){
System.out.println(ticketNum--);
}else{
return;
}
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();//解锁
}
}
}
public static void main(String[] args) {
TestLock testLock = new TestLock();
new Thread(testLock).start();
new Thread(testLock).start();
new Thread(testLock).start();
}
}
6、线程协作(生产者-消费者问题)
6.1管程法
//测试生产者、消费者模型---利用缓冲区解决:管程法
//生产者、消费者、产品、缓冲区
public class TestProCon {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
new Productor(synContainer).start();
new Consumer(synContainer).start();
}
}
//生产者
class Productor extends Thread{
SynContainer container;
public Productor(SynContainer container) {
this.container = container;
}
//生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.putIn(new Food(i));
System.out.println("生产第"+i+"个产品");
}
}
}
//消费者
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
//生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消耗第--"+container.takeOut().id+"--个产品");
}
}
}
//产品
class Food{
int id;//产品编号
public Food(int id) {
this.id = id;
}
}
//缓冲区
class SynContainer{
//设定容器大小
Food[] foods = new Food[10];
//容器计数器
int count = 0;
//生产者放入产品
public synchronized void putIn(Food food){
//如果容器满了,就需要等待消费者消耗
if(count == foods.length){
//生产者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有满,就需要放入产品
foods[count] = food;
count++;
//可以通知消费者消费
this.notifyAll();
}
//消费者消耗产品
public synchronized Food takeOut(){
//判断能否消费
if(count == 0){
//消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以消费
count--;
//消费后,通知生产者生产
this.notifyAll();
//取出当前这个产品,并返回
return foods[count];
}
}
6.2 信号灯法
//测试生产者消费者问题2,信号灯法,标志位
public class TestProCon2 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new Watcher(tv).start();
}
}
//生产者---演员
class Player extends Thread{
TV tv;
public Player(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
this.tv.play("三国演义");
}
}
}
//消费者---观众
class Watcher extends Thread{
TV tv;
public Watcher(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
this.tv.watch();
}
}
}
//产品---节目
class TV{
//演员表演,观众等待;观众观看,演员等待
String view;//节目
boolean flag = true;//有节目
//表演
public synchronized void play(String view){
if (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了---"+view);
//通知观众观看
this.notifyAll();//通知唤醒
this.view = view;
this.flag = !this.flag;
}
//观看
public synchronized void watch(){
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观众看---"+view);
//通知演员表演
this.notifyAll();//通知唤醒
this.flag = !this.flag;
}
}
7、线程池
-
背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能响很大。
-
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
-
好处:
- 提高响应速度(减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
- 便于线程管理(.….)
-
corePoolSize:核心池的大小
-
maximumPoolSize:最大线程数
-
keepAliveTime:线程没有任务时最多保持多长时间后会终止
-
JDK 5.0起提供了线程池相关API: ExecutorService和Executors
-
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
- void execute(Runnable command)∶执行任务/命令,没有返回值,一般用来执行Runnable
- Future submit(Callable task):执行任务,有返回值,一般又来执行Callable
- void shutdown():关闭连接池
-
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
//测试线程池
public class TestPool {
public static void main(String[] args) {
//创建服务,创建线程池
ExecutorService service = Executors.newFixedThreadPool(4);
//执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
}
private static class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
}