多线程
线程实现和线程同步(重点)
程序
静态的概念,用逻辑处理数据
进程
是程序执行一次执行过程,是一个动态的概念.是系统资源分配的单位
线程
一个进程至少有一个线程,比如main,线程是并发执行.是CPU调度和执行的单位
注意:
- 很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器.如果是模拟出来的多线程,即在一个CPU的情况下,在同一个时间点,CPU只能执行一个代码.因为切换的很快,所以就有同时执行的错觉
线程核心概念
-
线程就是独立的执行路径
-
在程序运行时,即是没有自己创建线程,后台也会有多个线程,如主线程,gc线程
-
main()称之为主程序,为系统的入口,用于执行整个程序
-
在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的
-
对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制
-
线程会带来额外的开销,如cpu调度时间,并发控制开销
-
每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ayqsi37R-1637922287678)(C:\Users\74771\AppData\Roaming\Typora\typora-user-images\1636276800355.png)]
继承Thread类创建线程
- 创建线程,继承thread类
- 重写run()方法
- 调用start开启线程
总结:注意,线程开启不一定立即执行,由CPU调度执行
体会run()和start()的不同
public class ThreadDemo01 extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("冷月葬花魂"+i);
}
}
public static void main(String[] args) {
ThreadDemo01 threadDemo01 = new ThreadDemo01();
// threadDemo01.run();
threadDemo01.start();
for (int i = 0; i < 200; i++) {
System.out.println("寒塘渡鹤影"+i);
}
}
}
网络资源下载
需要导入 commons-io-2.11.0 包
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.net.URL;
public class WebDownMain extends Thread{
private String url;
private String name;
public WebDownMain(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public void run() {
Down down = new Down();
down.down(url,name);
System.out.println(name+"下载好了");
}
public static void main(String[] args) {
WebDownMain webDownMain = new WebDownMain("https://m701.music.126.net/20211110173708/3174f994aadd0570b384b0c4d21b6980/jdyyaac/535d/0e0e/030e/d79981c8139db5e873324d6c6839ecde.m4a","枉凝眉.m4a");
webDownMain.start();
}
}
class Down{
public void down(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
}
catch (Exception e){
}
}
}
线程创建的三种方法
public class ThreadNew {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 一种
new MyThread1().start();
//第二种
new Thread(new MyThread2()).start();
//第三种
FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());
new Thread(futureTask).start();
Integer integer = futureTask.get();
System.out.println(integer);
}
}
class MyThread1 extends Thread{
@Override
public void run() {
System.out.println("1111");
}
}
class MyThread2 implements Runnable{
@Override
public void run() {
System.out.println("2222");
}
}
class MyThread3 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("333");
return 100;
}
}
并发
多个线程同时操作同一个对象
买火车票
-
发现问题
- 多个线程操作同一个资源的情况下 ,线程不安全 ,数据紊乱
public class TsetThread implements Runnable{
private int ticketNums = 10;
@Override
public void run() {
while(ticketNums>0){
// if(ticketNums<=0){
System.out.println(Thread.currentThread().getName()+"--拿到了"+ticketNums+"票");
ticketNums--;
// }
}
}
public static void main(String[] args) {
TsetThread tsetThread = new TsetThread();
new Thread(tsetThread,"小明").start();
new Thread(tsetThread,"老师").start();
new Thread(tsetThread,"黄牛党").start();
}
}
龟兔赛跑
public class Race implements Runnable {
private static String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if (Thread.currentThread().getName().equals("兔子") && i % 10 == 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (gameOver(i)) {
break;
}
System.out.println(Thread.currentThread().getName() + "--跑了" + i + "步");
}
}
private boolean gameOver(int steps) {
if (winner != null) {
return true;
}
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();
}
}
实现Callable接口
Callable的好处
- 可以定义返回值
- 可以抛出异常
- 四步走
- 在线程池中
1. //创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(2);
2 //提交执行
Future<Boolean> submit1 = ser.submit(webDownMain1);
java.util.concurrent.Future<Boolean> submit2 = ser.submit(webDownMain2);
// Future<Boolean> r2 = ser.submit(webDownMain2);
3 //获取结果
boolean rs1 = submit1.get();
boolean rs2 = submit2.get();
4 //关闭服务
ser.shutdownNow();
package com.kuang.lesson01;
import com.sun.corba.se.impl.orbutil.closure.Future;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.net.URL;
import java.util.concurrent.*;
public class down implements Callable <Boolean> {
private String url;
private String name;
public down(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public Boolean call() {
Down down = new Down();
down.down(url,name);
System.out.println(name+"下载好了");
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
down webDownMain1 = new down("https://m701.music.126.net/20211110173708/3174f994aadd0570b384b0c4d21b6980/jdyyaac/535d/0e0e/030e/d79981c8139db5e873324d6c6839ecde.m4a","枉凝眉.m4a");
down webDownMain2 = new down("https://m701.music.126.net/20211110173708/3174f994aadd0570b384b0c4d21b6980/jdyyaac/535d/0e0e/030e/d79981c8139db5e873324d6c6839ecde.m4a","枉凝眉.m4a");
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(2);
//提交执行
Future<Boolean> submit1 = ser.submit(webDownMain1);
java.util.concurrent.Future<Boolean> submit2 = ser.submit(webDownMain2);
// Future<Boolean> r2 = ser.submit(webDownMain2);
//获取结果
boolean rs1 = submit1.get();
boolean rs2 = submit2.get();
//关闭服务
ser.shutdownNow();
}
}
class Down{
public void down(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
}
catch (Exception e){
}
}
}
静态代理
- 真实对象和代理对象都要实现同一个接口
- 代理对象要代理真实角色
- 好处:
- 代理对象可以做很多真实对象做不了的事情
- 真实对象专注于做自己的事情
new WeddingCompany(new You()).happyMarry();
new Thread(new Runnable() @Override public void run() { }}).start();
两行代码实际上差不多
//即线程和 静态代理之间的关系
public class StaticProxy {
public static void main(String[] args) {
new WeddingCompany(new You()).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();
target.happyMarry();
after();
}
public void before(){
System.out.println("结婚前,布置现场");
}
public void after(){
System.out.println("结婚后,收尾款");
}
}
lambda 表达式
总结:
-
lambda表达式只能有一行代码的情况下才能简化为一行,如果有多行,就要用代码块{}包裹
-
前提是接口为函数式函数(里面只有一个方法)
-
多个参数也可以去掉参数类型,要去都去,必须加括号
-
ILike like4 = (7,8)(参数数值)-> System.out.println(“i like lambda 7”+a);
package com.kuang.lesson01;
public class lambdaTest {
// 静态内部类,在类中
static class like2 implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda1");
}
}
public static void main(String[] args) {
// 局部内部类, 在方法中
class like implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda2");
}
}
like2 like2 = new like2();
like like = new like();
//匿名内部类,没有类的名字,必须借助接口或者父类
ILike like3 = new ILike() {
@Override
public void lambda() {
System.out.println("i kike lambda5");
}
};
//lambda 表达式
ILike like4 = ()->{ System.out.println("i like lambda6");};
//再精简,括号和花括号都可以去掉
like4 = ()-> System.out.println("i like lambda 7");
//如果有参数
// ILike like4 = (7,8)(参数数值)-> System.out.println("i like lambda 7"+a);
// 总结:lambda表达式只能有一行代码的情况下才能简化为一行,如果有多行,就要用代码块{}包裹
//前提是接口为函数式函数(里面只有一个方法)
//多个参数也可以去掉参数类型,要去都去,必须加括号
like.lambda();
like2.lambda();
like3.lambda();
}
}
//实现接口
interface ILike{
void lambda();
}
//实现类
class like implements ILike{
@Override
public void lambda() {
System.out.println("i like lambda");
}
}
线程停止 stop
public class stopTest implements Runnable{
private boolean flag = true;
@Override
public void run() {
int i=0;
while(flag){
System.out.println("run...thread"+i++);
}
}
public void stop(){
flag = false;
}
public static void main(String[] args) {
stopTest stopTest = new stopTest();
new Thread(stopTest).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main" + i);
if(i==998){
stopTest.stop();
}
}
}
}
线程休眠 sleep
-
模拟倒计时
-
,每个对象都有一个锁,sleep 不会释放锁
-
项目经理要求这里运行缓慢,好让客户给钱优化, 并得到明显的速度提升
Thread.sleep(1000);
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestSleep {
//还可以打印系统时间
public static void main(String[] args) throws InterruptedException {
Date data = new Date(System.currentTimeMillis());
while(true){
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(data));//获取时间
data = new Date(System.currentTimeMillis());//更新时间
}
// tenDown();
}
public static void tenDown() throws InterruptedException {
int num = 10;
while (true){
Thread.sleep(1000);
System.out.println(num--);
if(num<=0){
break;
}
}
}
}
线程礼让 yeild
public class yeildTest {
public static void main(String[] args) {
MyYeild myYeild = new MyYeild();
new Thread(myYeild,"A").start();
new Thread(myYeild,"B").start();
}
}
class MyYeild implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName()+"线程结束执行");
}
线程强制执行 join
- 可以看做是插队
public class JoinTest 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 {
JoinTest joinTest = new JoinTest();
Thread th = new Thread(joinTest);
th.start();
for (int i = 0; i < 500; i++) {
if(i==50){
th.join();
}
System.out.println("main"+i);
}
}
}
观测线程状态 state
- new 尚未启动的线程处于此状态
- runnable 在java虚拟机中执行的线程处于此状态
- blocked 被阻塞等待监控器锁定的线程处于此状态, 等待CPU调度
- waiting 正在等待另一个线程执行特定动作的线程处于此状态
- time_waitting 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
- terminated 已退出的的线程处于此状态
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);//NEW
//观察启动后
thread.start();//Run
state = thread.getState();
System.out.println(state);
//只要线程不终止,就一直输出
while(state != Thread.State.TERMINATED){
Thread.sleep(1000);
state = thread.getState();//更新线程状态
System.out.println(state);
}
}
}
线程的优先级 priority
public class PriorityTest {
public static void main(String[] args) {
//主线程默认优先级
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
MyPriority myPriority = new MyPriority();
Thread t1 = new Thread(myPriority);
Thread t2 = new Thread(myPriority);
Thread t3 = new Thread(myPriority);
Thread t4 = new Thread(myPriority);
//先设置优先级,再启动
//优先级只在 1- 10 之间,其他会报错
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY);
t4.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
}
}
守护线程,daemon
public class DaemonTest {
public static void main(String[] args) {
God god = new God();
You2 you2 = new You2();
Thread thread = new Thread(god);
thread.setDaemon(true);//默认是false表示是用户线程,正常的线程都是用户线程
thread.start();//上帝守护线程启动
new Thread(you2).start();// 你,用户线程启动
}
}
class God implements Runnable{
@Override
public void run() {
while(true){ //守护之后就会结束
System.out.println("上帝保佑着你");
}
}
}
class You2 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("开心的活着");
}
System.out.println("===== goodbye world =====");
}
}
线程同步
队列和锁机制(synchronized)
- 性能和安全
- 你上厕所,后面人要排队 = 队列
- 上的时候要关门 = 锁 = 安全
不安全的取钱
//不安全取钱
//两个人去银行取钱,账户
public class UnsafeBank {
public static void main(String[] args) {
Account account = new Account(100,"结婚基金");
Drawing you = new Drawing(account, 50, "你");
Drawing girlFriend = new Drawing(account, 100, "grilFriend");
you.start();
girlFriend.start();
}
}
class Account{
int money;
String name;
public Account(int money,String name){
this.money = money;
this.name = name;
}
}
class Drawing extends Thread{
Account account;//账户
int drawingMoney;
int nowMoney;
public Drawing(Account account, int drawingMoney, String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
//判断有没有钱
if(account.money-drawingMoney<0){
System.out.println(this.getName()+"not money");
return;
}
try {
Thread.sleep(1000);
//可以放大问题的发生性 , 很重要, 不然看不出来
// 等待两者同时取钱
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money = account.money-drawingMoney;
nowMoney = nowMoney +drawingMoney;
// this.name = Thread.currentThread().getName(),因为继承了Thread类
System.out.println(account.name+"余额为"+account.money);
System.out.println(this.getName()+"手里的钱为"+nowMoney);
}
}
安全形式
- synchronized (account){}
- 同步块
public void run() {
synchronized (account){
//判断有没有钱
if(account.money-drawingMoney<0){
System.out.println(this.getName()+"not money");
return;
}
try {
Thread.sleep(1000);
// 等待两者同时取钱
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money = account.money-drawingMoney;
nowMoney = nowMoney +drawingMoney;
// this.name = Thread.currentThread().getName(),因为继承了Thread类
System.out.println(account.name+"余额为"+account.money);
System.out.println(this.getName()+"手里的钱为"+nowMoney);
}
}
出现负数
当票只剩一张,大家都要买了,所以出现负数,
- 每个线程都有自己的工作内存
不安全的买票
//不安全的买票
//线程不安全,有负数
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket,"苦逼的我").start();
new Thread(buyTicket,"牛逼的你们").start();
new Thread(buyTicket,"可恶的黄牛党").start();
}
}
class BuyTicket implements Runnable{
private int ticketNums = 10;
boolean flag = true;//外部停止方式
@Override
public void run() {
while(flag){
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void buy() throws InterruptedException {
//判断是否有票
if (ticketNums<=0){
flag = false;
return;
}
//模拟延时
Thread.sleep(1000);
//买票
System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
}
}
- 在 buy 方法前 加 锁就行了
- private synchronized void buy()
- 同步方法
private synchronized void buy() throws InterruptedException {
//判断是否有票
if (ticketNums<=0){
flag = false;
return;
}
//模拟延时
//Thread.sleep(1000);
//买票
System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
}
线程的不安全
最后加不到1000
import java.util.ArrayList;
public class UnsafeList {
public static void main(String[] args) throws InterruptedException {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
//Thread.sleep(2000);
System.out.println(list.size());
}
}
//不同的包 juc
public class TestCallable {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
System.out.println(list.size());
}
}
synchronized
- 锁要变化的量
- 即增删改查的量
死锁
死锁线程各自占有一些共享资源,并且相互等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形,某一个同步块同时拥有两个以上对象的锁 时就可能会发生 死锁的问题
两个孩子都有玩具,但想抢另一个的,就都卡了,(人不会,但机器会)
- 我口红,你镜子,都想拿对方的,两个进程都想要镜子
产生死锁的四个必要条件
- 互斥条件 : 一个资源每次只能被一个进程使用
- 请求与保持条件 : 一个进程因请求资源而阻塞时,对方获得的资源保持不放
- 不剥夺条件: 进程已获得的资源,在未使用完之前,不能强行剥夺
- 循环等待条件 : 若干进程之间形成一种头尾相接的循环等待资源关系
public class DeadLock {
public static void main(String[] args) {
Makeup g1 = new Makeup(0,"灰姑娘");
Makeup g2 = new Makeup(0,"白雪公主");
g1.start();
g2.start();
}
}
class Lipsticl{
}
class Mirror{
}
class Makeup extends Thread{
//需要的资源只有一份,用static 来保证只有一份
static Lipsticl lipsticl = new Lipsticl();
static Mirror mirror = new Mirror();
int choice;//选择
String girlname;//用化妆品的人
public Makeup(int choice, String girlname) {
this.choice = choice;
this.girlname = girlname;
}
@Override
public void run() {
//化妆
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void makeup() throws InterruptedException {
if(choice ==0){
synchronized (lipsticl) {
System.out.println(this.girlname + "获得口红的锁");
Thread.sleep(1000);
}
//两个锁{}不在一起就不会死锁了
synchronized (mirror) {
System.out.println(this.girlname + "获得镜子的锁");
}
}else{
synchronized (mirror) {
System.out.println(this.girlname + "获得镜子的锁");
Thread.sleep(2000);
}
//两个锁{}不在一起就不会死锁了
synchronized (lipsticl) {
System.out.println(this.girlname + "获得口红的锁");
}
}
//放到else 外面就不会死锁了
}
}
Lock
ReentrantLock lock = new ReentrantLock();
lock.lock();//加锁
lock.unlock();
public class LockTest {
public static void main(String[] args) {
Testlock2 testlock2 = new Testlock2();
new Thread(testlock2).start();
new Thread(testlock2).start();
new Thread(testlock2).start();
}
}
class Testlock2 implements Runnable{
int ticketNums = 10;
//定义lock锁
ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock.lock();//加锁
if (ticketNums > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticketNums--);
} else {
break;
}
}finally {
//解锁
lock.unlock();
}
}
}
}
Lock 和 synchronized的对比
- Lock 是显式锁( 手动开启和关闭锁,别忘记关闭锁) synchronized 是隐式锁,出了作用域自动释放
- Lock只有代码块 synchronized有代码块锁和方法锁
- 使用Lock锁 ,JVM将花费较少的时间来调度线程,性能更好.并且具有更好的扩展性,( 提供更多的子类 )
- 优先使用顺序:
- Lock >同步代码块 ( 已经进入了方法体 ,分配了相应资源) > 同步方法 (在方法体之外)
生产者与消费者问题
利用缓冲区解决 : 管程法
注意 this.notify() 和 this.wait(); 的使用
//测试 :生产者消费者模型 --> 利用缓冲区解决 : 管程法
//生产者 , 消费者 , 产品 , 缓冲区
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Productor(container).start();
new Consumer(container).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++) {
System.out.println("生产了"+i+"只鸡");
try {
container.push(new Chicken(i));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer extends Thread{
SynContainer container;
public Consumer(SynContainer container) {
this.container = container;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
System.out.println("消费了-->"+container.pop().id+"只鸡");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Chicken{
int id;//产品编号
public Chicken(int id) {
this.id = id;
}
}
class SynContainer{
Chicken[] chickens = new Chicken[10];
int count = 0;
public synchronized void push(Chicken chicken) throws InterruptedException {
//如果容器满了,就等待消费者消费
if(count == chickens.length){
//通知消费者消费,生产者等待
this.wait();
}
//如果没有满,我们就需要丢入产品
chickens[count] = chicken;
count++;
//可以通知消费者消费了
this.notify();
}
public synchronized Chicken pop() throws InterruptedException {
if (count==0){
//等待生产者生产,消费者等待
this.wait();
}
//如果可以消费
count--;
Chicken chicken = chickens[count];
//吃完了,通知生产者生产
this.notify();
return chicken;
}
}
信号灯法 ,标志位解决
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new Watcher(tv).start();
}
}
class Watcher extends Thread{
TV tv;
public Watcher(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
tv.watch();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Player extends Thread{
TV tv;
public Player(TV tv) {
this.tv = tv;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if(i%2==0){
try {
this.tv.play("快乐大本营播放中");
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
try {
this.tv.play("红楼梦");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class TV{
// 演员表演 ,观众等待 T
// 观众观看 , 演员等待 F
String voice ; //表演的节目
boolean flag = true;
public synchronized void play(String voice) throws InterruptedException {
if(!flag){
this.wait();
}
System.out.println("演员表演了快乐大本营"+voice);
this.voice = voice;
this.flag = !this.flag;
//通知观众观看
this.notifyAll(); //通知唤醒
}
public synchronized void watch() throws InterruptedException {
if (flag){
this.wait();
}
System.out.println("观看了"+voice);
//通知演员表演
this.notifyAll();
this.flag = !this.flag;
}
}
线程池
public class TestPool {
public static void main(String[] args) {
// 1 创建服务
// n 参数为线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}