1.线程简介
-
程序是指令和数据的有序集合,本身没有任何运行的含义,是一个静态的概念
-
进程是执行程序的一次执行过程,是一个动态的概念。是系统资源分配的单位
-
一个进程中可以包含多个线程,一个进程也至少有一个线程。线程是CPU调度和执行的单位。
- 一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序不是人为可以干涉的
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
2.线程实现
2.1线程创建
- 继承Thread类
- 创建Thread类的子类
- 重写run()方法
- 创建子类对象,调用start()方法
/继承Thread
//创建Thread子类 , 重写run()方法 , 创建子类的对象 调用start方法
public class ThreadDemo1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("I'm start" + i);
}
}
public static void main(String[] args) {
ThreadDemo1 threadDemo1 = new ThreadDemo1();
threadDemo1.start();
for (int i = 0; i < 1000; i++) {
System.out.println("我在学习多线程" + i);
}
}
}
//run()方法与主方法交替出现 两个线程!
练习:利用多线程同步下载网络图片
package demo1;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
public class ThreadDemo2 extends Thread{
private String url; //网络图片地址
private String name; //保存的文件名
public ThreadDemo2(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) {
ThreadDemo2 t1 = new ThreadDemo2("url");
ThreadDemo2 t2 = new ThreadDemo2("url");
ThreadDemo2 t3 = new ThreadDemo2("url");
t1.start();
t2.start();
t3.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("IO异常,downLoader方法出现问题");
}
}
}
//线程创建好了 并不一定立即实现
2. 实现Runnable接口
- 实现runnable接口
- 重写run()方法
- 执行线程时需要丢入runnable接口实现类,调用start方法
package demo1;
//创建MyRunnable类实现Runnable接口
//重写run方法
//创建实现类丢入代理类中,调用start方法启动
public class ThreadDemo3 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("I'm start" + i);
}
}
public static void main(String[] args) {
ThreadDemo3 threadDemo3 = new ThreadDemo3();//实现类
new Thread(threadDemo3).start();//代理类
for (int i = 0; i < 1000; i++) {
System.out.println("我在学习多线程" + i);
}
}
}
两种方法对比
继承Thread类:
-
子类继承Thread类具备多线程能力
-
启动线程:子类.start();
-
**不建议使用:**避免单继承局限性
实现Runnable类:
-
实现接口Runnable具备多线程能力
-
启动线程:传入目标对象+Thread对象.start()
-
**建议使用:**避免单继承局限性,灵活方便,方便同一对象被多个线程使用
- 实现callable接口
-
好处:
- 可以定义返回值
- 可以抛出异常
-
package demo1; import java.util.concurrent.*; public class TestCallable implements Callable<Boolean> { private String url; //网络图片地址 private String name; //保存的文件名 public TestCallable(String url, String name) { this.url = url; this.name = name; } //线程体执行下载的任务 @Override public Boolean call() { WebDownLoader webDownLoader = new WebDownLoader(); webDownLoader.downLoader(url,name); System.out.println(name + "文件已经下载好"); return true; } //创建三个线程对象,”同时“实现下载任务 public static void main(String[] args) throws ExecutionException, InterruptedException { TestCallable t1 = new TestCallable("url"); TestCallable t2 = new TestCallable("url"); TestCallable t3 = new TestCallable("url"); //两种方式均可启动线程 //参数可以丢callable的实现类,也可以丢入Runnable接口的实现类 FutureTask<Boolean> futureTask = new FutureTask<Boolean>(t1); new Thread(futureTask).start(); boolean flag = futureTask.get();//获得返回值 /*//创建执行服务 ExecutorService ser = Executors.newFixedThreadPool(3); //提交执行 Future<Boolean> r1 = ser.submit(t1); Future<Boolean> r2 = ser.submit(t2); Future<Boolean> r3 = ser.submit(t3); //获取结果 boolean rs1 = r1.get(); boolean rs2 = r2.get(); boolean rs3 = r3.get(); //关闭服务 ser.shutdownNow();*/ } }
2.2模拟龟兔赛跑
package demo1;
public class Race implements Runnable{
private String winner;//胜利者
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//模拟兔子睡觉
if(Thread.currentThread().getName().equals("兔子") && (i%10 == 0)){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag = gameOver(i);
if(flag){
break;
}
System.out.println(Thread.currentThread().getName()+"-->走了" + i + "步");
}
}
public boolean gameOver(int steps){
if(winner!=null) {//winner 已经存在
return true;
} else{
//如果winner不存在 就判断步数
if(steps>=100){
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();
}
}
2.3静态代理模式
package demo1;
//婚庆公司 模拟静态代理
//真实对象 代理对象 实现同一个接口
/* 代理对象可以做很多真实对象不做的事情
真实对象可以专注做自己的事情
*/
public class StaticProxy {
public static void main(String[] args) {
You you = new You();
new WeddingCompany(you).happyMarry();
}
}
//代理对象
class WeddingCompany implements Marry{
private Marry target;
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void happyMarry() {
before();
target.happyMarry();
after();
}
private void after() {
System.out.println("收尾款");
}
private void before() {
System.out.println("布置婚房");
}
}
//真实对象
class You implements Marry{
@Override
public void happyMarry() {
System.out.println("要结婚了");
}
}
interface Marry{
void happyMarry();
}
2.4lamda表达式
-
函数式接口:
只有一个方法的接口称为函数式接口,Lambda表达式只能实现函数式接口
-
Lambda表达式的推导
package demo1;
//lambda表达式简化过程
public class TestLambda01 {
//3. 定义静态内部类
static class Like2 implements ILike{
@Override
public void like() {
System.out.println("I Like You2");
}
}
public static void main(String[] args) {
ILike like = new Like1();
like.like();
like = new Like2();
like.like();
//4. 定义局部内部类
class Like3 implements ILike{
@Override
public void like() {
System.out.println("I Like You3");
}
}
like = new Like3();
like.like();
//5. 定义匿名内部类 没有名字的类, 必须借助接口名或者父类名定义
like = new ILike() {
@Override
public void like() {
System.out.println("I Like You4");
}
};
like.like();
//6. lambda表达式
like = ()->{
System.out.println("I Like You5");
};
like.like();
}
}
//1. 定义函数式接口
interface ILike{
void like();
}
//2. 函数式接口实现类
class Like1 implements ILike{
@Override
public void like() {
System.out.println("I Like You1");
}
}
总结
只有函数式接口才能用lambda实现
- Lambda表达式的简化
- 参数类型可以省略
- 圆括号可以省略(没有参数或者多个参数时不能省略)
- 只有一句代码时可以简化成一行
like = ()->System.out.println("I Like You5");
3.线程状态
线程只能启动一次
3.1停止状态
/*线程停止的几种方法
* 1. 建议让线程自然停止--->利用次数
* 2. 建议使用标志位--->设置一个标志位
* 3. 不建议使用jdk不推荐的方法 stop() destroy()
* */
public class TestStop implements Runnable{
//定义一个boolean变量 控制线程的停止
private boolean flag = true;
private int a = 0;
@Override
public void run() {
while(flag){
System.out.println("Thread...Run"+ a);
a++;
}
}
//自定义一个停止方法 转换标志
public void stop(){
this.flag = false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
if(i==900){
testStop.stop();
System.out.println("Stop");
}
}
}
}
3.2休眠状态
模拟网络延时:放大问题的发生性!!!
package state;
//模拟网络延时:放大问题的发生性!!!
//做一个十秒钟的倒计时
public class TenDown implements Runnable{
private int i = 10;
@Override
public void run() {
while(true){
try {
System.out.println(i--);
Thread.sleep(1000);//线程休眠
if(i<=0){
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
TenDown tenDown = new TenDown();
new Thread(tenDown).start();
}
}
3.3礼让状态
礼让不一定成功
package state;
public class TestYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->start");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName()+"-->end");
}
public static void main(String[] args) {
TestYield testYield = new TestYield();
new Thread(testYield,"a").start();
new Thread(testYield,"b").start();
}
}
3.4强制执行(join)
不建议使用
package state;
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("vip来了"+i);
}
}
public static void main(String[] args) throws InterruptedException {
//启动线程
TestJoin join = new TestJoin();
Thread thread = new Thread(join);
thread.start();
//主线程
for (int i = 0; i < 500; i++) {
System.out.println("main"+i);
if(i==200) {
thread.join();//线程插队 强制执行 其他线程进入阻塞状态
}
}
}
}
3.5观测线程状态
package state;
public class TestState{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("休眠完成");
});
//线程新生状态
Thread.State state = thread.getState();
System.out.println(state);
//线程启动
thread.start();
state = thread.getState();
System.out.println(state);
while(state!=Thread.State.TERMINATED){
Thread.sleep(200);//防止打印太快(主线程休眠)
state = thread.getState();
System.out.println(state);
}
}
}
3.6线程优先级
先设置优先级再执行
优先级高的不一定先执行!!!
public class TestPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"--->"
+ Thread.currentThread().getPriority());
}
public static void main(String[] args) {
TestPriority testPriority = new TestPriority();
Thread t1 = new Thread(testPriority);
Thread t2 = new Thread(testPriority);
Thread t3 = new Thread(testPriority);
Thread t4 = new Thread(testPriority);
Thread t5 = new Thread(testPriority);
Thread t6 = new Thread(testPriority);
t1.start();
t2.setPriority(6);
t3.setPriority(8);
t4.setPriority(3);
t5.setPriority(9);
t6.setPriority(2);
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
3.7守护线程
线程分为用户线程和守护线程
虚拟机不用等待守护线程执行完毕
而虚拟机必须确保用户线程执行完毕
package state;
//测试守护线程
//虚拟机不用等待守护线程执行完毕再结束
public class TestDemon {
public static void main(String[] args) {
You you = new You();
God god = new God();
Thread thread = new Thread(god);
thread.setDaemon(true);//设置为守护线程 默认值为false
thread.start();//启动守护线程
new Thread(you).start();//启动用户线程 一般线程都为用户线程
}
}
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 30000; i++) {
System.out.println("开心的活着!");
}
System.out.println("=====you dead=====");
}
}
class God implements Runnable{
@Override
public void run() {
while(true){
System.out.println("上帝守护着你");//上帝永生,但是再你去世之后一段时间 虚拟机会停止
}
}
}
4.线程同步
**线程同步:**即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态。多个线程操作统一资源时需要同步
线程的不安全:
访问冲突问题
4.1三大不安全案例
package syn;
//不安全的买票问题
public class UnsafeBuyTicket {
public static void main(String[] args) {
Station station = new Station();
new Thread(station,"小明").start();
new Thread(station,"小红").start();
new Thread(station,"黄牛党").start();
}
}
class Station implements Runnable{
private int ticketNums = 10;//车票
private boolean flag = true;//外部标志 停止线程
@Override
public void run() {
while(flag){
buy();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}//车票为零则退出
if(ticketNums<=0){
break;
}
}
}
public void buy(){
//判断是否有车票
if(ticketNums<=0){
System.out.println("票卖完了,没有了");
return;
}
System.out.println(Thread.currentThread().getName() +
"--->拿到了第" + ticketNums-- +"张票");
}
}
package syn;
//不安全的取钱问题
public class UnsafeDrawMoney {
public static void main(String[] args) {
Account account = new Account("结婚基金",100);
Bank you = new Bank("you",0,50,account);
Bank girlFriend = new Bank("girlFriend",0,100,account);
you.start();
girlFriend.start();
}
}
//账户
class Account{
private String name;
private int accountMoney;
public Account(String name, int accountMoney) {
this.name = name;
this.accountMoney = accountMoney;
}
public int getAccountMoney() {
return accountMoney;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setAccountMoney(int accountMoney) {
this.accountMoney = accountMoney;
}
}
//银行
class Bank extends Thread{
private Account account;
private int nowMoney;
private int drawMoney;
public Bank(String name, int nowMoney, int drawMoney, Account account) {
super(name);
this.account = account;
this.nowMoney = nowMoney;
this.drawMoney = drawMoney;
}
@Override
public void run() {
try {
drawMoney();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void drawMoney() throws InterruptedException {
if(account.getAccountMoney()-drawMoney<=0){
System.out.println(Thread.currentThread().getName() + "--->取不到钱了,卡里的余额不足");
}
Thread.sleep(100);
account.setAccountMoney(account.getAccountMoney()-drawMoney);//更新账户余额
nowMoney = nowMoney + drawMoney;//更新现有余额
System.out.println(Thread.currentThread().getName()+"--->取了"+drawMoney +
",手中还有" + nowMoney);
System.out.println("卡里余额--->"+account.getAccountMoney());
}
}
package syn;
import java.util.ArrayList;
import java.util.List;
//线程不安全的集合
public class UnsafeList {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<String>();
for (int i = 0; i < 100000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(3000);
System.out.println(list.size());
}
}
4.2解决方法
为了保证数据在方法中被访问时的正确性,再访问时加入**锁机制synchronized**,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可。(队列+锁 —> 可以想象为多人上一个厕所)
- 也会存在以下问题**(安全和性能不可兼得)**:
- 一个线程持有锁会导致其他所有需要此锁的线程挂起(阻塞)
- 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
- 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能问题(性能导致)
解决方法
-
synchronized方法
//同步方法: public synchronized void function(){} //修改不安全的买票问题 package syn; //不安全的买票问题 public class UnsafeBuyTicket { public static void main(String[] args) { Station station = new Station(); new Thread(station,"小明").start(); new Thread(station,"小红").start(); new Thread(station,"黄牛党").start(); } } class Station implements Runnable{ private int ticketNums = 10;//车票 private boolean flag = true;//外部标志 停止线程 @Override public void run() { while(flag){ buy(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }//车票为零则退出 if(ticketNums<=0){ break; } } } //修改为同步方法,修改run()方法为同步方法也可。默认同步监视器均为这个对象即class station public synchronized void buy(){ //判断是否有车票 if(ticketNums<=0){ System.out.println("票卖完了,没有了"); return; } System.out.println(Thread.currentThread().getName() + "--->拿到了第" + ticketNums-- +"张票"); } }
若将一个大的方法申明为同步方法,则会大大影响效率
方法里面需要修改的内容才需要加锁,锁的太多浪费资源
-
synchronized块
synchronized(Obj){} /* Obj: 称为同步监视器;推荐使用共享资源作为同步监视器。 同步方法中不需要指定同步监视器,默认为this这个对象 */ //修改不安全的集合 public class UnsafeList { public static void main(String[] args) throws InterruptedException { List<String> list = new ArrayList<String>(); for (int i = 0; i < 100000; i++) { new Thread(()->{ //加入同步块 将贡献资源list作为同步监视器 synchronized (list){ list.add(Thread.currentThread().getName()); } }).start(); } Thread.sleep(3000); System.out.println(list.size()); } } //修改不安全的取钱问题 package syn; //不安全的取钱问题 public class UnsafeDrawMoney { public static void main(String[] args) { Account account = new Account("结婚基金", 100); Bank you = new Bank("you", 0, 50, account); Bank girlFriend = new Bank("girlFriend", 0, 100, account); you.start(); girlFriend.start(); } } //账户 class Account{ private String name; private int accountMoney; public Account(String name, int accountMoney) { this.name = name; this.accountMoney = accountMoney; } public int getAccountMoney() { return accountMoney; } public void setAccountMoney(int accountMoney) { this.accountMoney = accountMoney; } } //银行 class Bank extends Thread{ private Account account; private int nowMoney; private int drawMoney; public Bank(String name, int nowMoney, int drawMoney, Account account) { super(name); this.account = account; this.nowMoney = nowMoney; this.drawMoney = drawMoney; } @Override public void run() { try { drawMoney(); } catch (InterruptedException e) { e.printStackTrace(); } } public void drawMoney() throws InterruptedException { //加入同步块,因为账户为共享资源 则同步监视器为account synchronized (account){ if(account.getAccountMoney()-drawMoney<=0){ System.out.println(Thread.currentThread().getName() + "--->想取" + drawMoney + "卡里的余额不足"); return; } Thread.sleep(100); account.setAccountMoney(account.getAccountMoney()-drawMoney);//更新账户余额 nowMoney = nowMoney + drawMoney;//更新现有余额 System.out.println(Thread.currentThread().getName()+"--->取了"+drawMoney + ",手中有" + nowMoney); System.out.println("卡里余额--->"+account.getAccountMoney()); } } }
4.3死锁
package syn;
//测试死锁
//模拟化妆
public class DeadLock {
public static void main(String[] args) {
Makeup g1 = new Makeup(0, "小红");
Makeup g2 = new Makeup(1, "小芳");
g1.start();
g2.start();
}
}
//镜子
class Mirror{ }
//口红
class Lipstick{ }
//化妆
class Makeup extends Thread{
//需要的资源
//且需要用static 保证资源的唯一性
private static Mirror mirror = new Mirror();
private static Lipstick lipstick = new Lipstick();
private int choice;//做出选择 先拿什么
private String name;
public Makeup(int choice,String name) {
super(name);
this.choice = choice;
}
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void makeup() throws InterruptedException {
if(choice == 0){
synchronized (mirror){
System.out.println(Thread.currentThread().getName()+
"拿到了镜子");
Thread.sleep(1000);//一秒钟之后想要拿到口红
synchronized (lipstick) {
System.out.println(Thread.currentThread().getName() +
"拿到了口红");
}
}
}else{
synchronized (lipstick){
System.out.println(Thread.currentThread().getName()+
"拿到了口红");
Thread.sleep(2000);//一秒钟之后想要拿到镜子
synchronized (mirror) {
System.out.println(Thread.currentThread().getName() +
"拿到了镜子");
}
}
}
}
}
4.4lock锁
package gaoji;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
Station station = new Station();
new Thread(station).start();
new Thread(station).start();
new Thread(station).start();
}
}
class Station implements Runnable{
ReentrantLock lock = new ReentrantLock();
int ticketNums = 10;
@Override
public void run() {
try {
lock.lock();//开锁
while(true){
if(ticketNums<=0){
System.out.println("卖完了");
return;
}
System.out.println("拿到了第" + ticketNums-- + "张票");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();//解锁
}
}
}
4.5lock 与synchronized 对比
- Lock是显式锁(手动开启和关闭锁) synchronized是隐式锁,出了代码块之后自动释放
- Lock只有代码块锁,而synchronized可以锁方法和代码块
- Lock锁,JVM将花费较少的时间来调度,性能更好。并且具有更好的拓展性(提供更多的子类)
- 使用顺序一般为:Lock—>同步代码块—>同步方法
5.线程通信问题
并发协作模型 “生产者、消费者 ”
5.1管程法
package gaoji;
/* 线程之间的通信
并发协作模型 ”生产者 消费者模式“-->解决方法1 -->缓冲区:管程法
生产者:负责生产数据的模块
消费者:负责处理数据的模块
缓冲区:生产者将数据放入缓冲区 消费者从缓冲区中拿走数据
*/
//生产者 消费者 产品 缓冲区
public class TestPC {
public static void main(String[] args) {
Container container = new Container();
Productor productor = new Productor(container);
Consumer consumer = new Consumer(container);
productor.start();
consumer.start();
}
}
//生产者
class Productor extends Thread{
Container container;
public Productor(Container container){
this.container = container;
}
//生产者专注生产鸡
@Override
public void run() {
for (int i = 0; i < 100; i++) {
container.push(new Chicken(i));
System.out.println("生产了-->" + i + "鸡");
}
}
}
//消费者
class Consumer extends Thread{
Container container;
public Consumer(Container container){
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了-->" + container.pop().id + "鸡");
}
}
}
//产品
class Chicken{
int id;
public Chicken(int id) {
this.id = id;
}
}
//缓冲区
class Container{
//缓冲区大小
Chicken[] chickens = new Chicken[10];
//缓冲区计数器
static int count = 0;
//生产者生产产品
public synchronized void push(Chicken chicken){
//判断如果缓冲区中的产品是满的就停止生产,通知消费者消费
if(count == chickens.length){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有将缓冲区填满 就继续生产
chickens[count++] = chicken;
//只要缓冲区中有产品就可以通知消费者消费
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken pop(){
//判断 缓冲区中是否有产品
if(count == 0){
//没有产品 等待生产者生产
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果有产品直接消费,同时通知生产者生产
Chicken chicken = chickens[--count];
this.notifyAll();
return chicken;
}
}
5.2信号灯法(标志位)
package gaoji;
//解决生产者 消费者2--->信号灯法(标志位)
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
new Performer(tv).start();
new Audience(tv).start();
}
}
//生产者--->演员
class Performer extends Thread{
TV tv = new TV();
public Performer(TV tv){
this.tv = tv;
}
//演员表演节目
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if(i%2==0){
tv.show("快乐大本营");
}else{
tv.show("抖音:记录美好生活");
}
}
}
}
//消费者--->观众
class Audience extends Thread{
TV tv = new TV();
public Audience(TV tv){
this.tv = tv;
}
@Override
public void run() {
//观众专注观看
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
//产品--->TV
//演员表演,观众等待 T
//观众观看,演员等待 F
class TV {
//表演的节目
String program;//节目传递在演员和观众之间
//标志位
boolean flag = true;
//表演
public synchronized void show(String program) {
// 演员等待 flag = false
if (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//演员表演节目
System.out.println("演员表演了--->" + program);
this.program = program;//传给观众
//表演结束 通知观众观看
this.flag = !this.flag;//更新标志位
this.notifyAll();
}
//观看
public synchronized void watch() {
// 观众等待 flag = true
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//否则观众观看
System.out.println("观众观看了--->" + this.program);
this.flag = !this.flag;//更新标志位
//观看结束 通知演员表演
this.notifyAll();
}
}
6. 线程池
package gaoji;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 两个类: ExecutorService--->创建服务 (callable也使用了)
// Executors---> 工具类 用于创造并返回不同类型的线程池
public class Pool {
public static void main(String[] args) {
//newFixedThreadPool 参数为 :线程池大小
//1. 创建服务
ExecutorService service = Executors.newFixedThreadPool(10);
//2. 执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//3. 关闭服务
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}