注:学习记录
一、基本概念
任务:同时做多件事情,例如上厕所+打电话;本质上大脑只在做一件事情
程序:是为完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码,静态对象。
进程(Process):是程序的一次执行过程,或是正在运行的一个程序,是一个动态的过程,有它自身的产生,存在和消亡的过程。-------生命周期
线程:进程可进一步细化为线程,是一个程序内部的一条执行路径
多线程:是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理” 。
二、多线程创建
1.继承Thread类
-
线程是程序中执行的线程。Java虚拟机允许应用程序同时执行多个执行线程。
每个线程都有优先权。 具有较高优先级的线程优先于优先级较低的线程执行。 每个线程可能也可能不会被标记为守护程序。 当在某个线程中运行的代码创建一个新的
Thread
对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护线程。当Java虚拟机启动时,通常有一个非守护进程线程(通常调用某些指定类的名为
main
的方法)。 Java虚拟机将继续执行线程,直到发生以下任一情况:- 已经调用了
Runtime
类的exit
方法,并且安全管理器已经允许进行退出操作。 - 所有不是守护进程线程的线程都已经死亡,无论是从调用返回到
run
方法还是抛出超出run
方法的run
。
创建一个新的执行线程有两种方法。 一个是将一个类声明为
Thread
的子类。 这个子类应该重写run
类的方法Thread
。 然后可以分配并启动子类的实例。 例如,计算大于规定值的素数的线程可以写成如下: - 已经调用了
-
class PrimeThread extends Thread { long minPrime; PrimeThread(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } }
然后,以下代码将创建一个线程并启动它运行:
PrimeThread p = new PrimeThread(143); p.start();
另一种方法来创建一个线程是声明实现类
Runnable
接口。 那个类然后实现了run
方法。 然后可以分配类的实例,在创建Thread
时作为参数传递,并启动。 这种其他风格的同一个例子如下所示:
class PrimeRun implements Runnable { long minPrime; PrimeRun(long minPrime) { this.minPrime = minPrime; } public void run() { // compute primes larger than minPrime . . . } }
然后,以下代码将创建一个线程并启动它运行:
PrimeRun p = new PrimeRun(143); new Thread(p).start();
每个线程都有一个用于识别目的的名称。 多个线程可能具有相同的名称。 如果在创建线程时未指定名称,则会为其生成一个新名称。
除非另有说明,否则将
null
参数传递给null
中的构造函数或方法将导致抛出NullPointerException
。
2.Runnable接口
//线程实现方法二:实现runnable接口,重写run方法,执行方法需要先丢入runnable接口实现类,调用start()方法
public class TestThread3 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("重写"+i);
}
}
public static void main(String[] args) {
//创建一个runnable接口实现类对象
TestThread3 testThread3=new TestThread3();
//创建线程对象,通过线程对象来开启我们的线程,代理
//Thread thread = new Thread(testThread3);
//thread.start();
new Thread(testThread3).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
}
}
}
避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
初识并发问题
//多个线程同时操作一个对象
//买火车票的例子
//发现问题:多个线程在操作同一个资源情况下会数据紊乱,线程不安全
public class TestThread4 implements Runnable{
//票数
private int ticketNums=10;
@Override
public void run() {
while (true){
if (ticketNums<=0){
break;
}
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"票");
}
}
public static void main(String[] args) {
TestThread4 testThread4 = new TestThread4();
//开启三个线程
new Thread(testThread4,"小明").start();
new Thread(testThread4,"小红").start();
new Thread(testThread4,"黄牛").start();
}
}
龟兔赛跑
package com.base.thread;
//模拟龟兔赛跑
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();
}
}
//判断比赛是否结束
boolean flag=gamaOver(i);
if (flag){
break;
}
System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
}
}
//判断是否完成比赛
private boolean gamaOver(int steps) {
//判断是否有胜利者
if (winner != null) {//已经存在胜利者
return true;
}{
if (steps>=100){
winner=Thread.currentThread().getName();
System.out.println("胜利者是"+winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
Race race=new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
三、静态代理模式
package com.base.thread;
//人间四大喜事
//久旱逢甘露
//他乡遇故知
//洞房花烛夜
//金榜题名时
public class StaticProxy {
public static void main(String[] args) {
WeddingCompany weddingCompany = new WeddingCompany(new you());
weddingCompany.HappyMarry();
}
}
//接口
interface Marry{
void HappyMarry();
}
//you是一个真实角色
class you implements Marry{
@Override
public void HappyMarry() {
System.out.println("超级开心");
}
}
//weddingCompany是一个代理角色
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("结婚之前,布置现场");
}
}
/*
静态代理模式总结:
1.真实对象和代理对象都要实现同一个接口;
2.代理对象要代理真实角色;
好处:
代理对象能做很多真实对象做不了的事情;
真实对象专注自己的事情
*/
四、lamda表达式
函数式接口:一个接口函数,只有一个抽象的方法;
/*
推导lamda表达式
*/
public class TestLamda {
//3.静态内部类(将实现类放进来)
static class Like2 implements ILike{
@Override
public void Lamda() {
System.out.println("lamda2");
}
}
public static void main(String[] args) {
ILike like = new Like();
like.Lamda();
Like2 like2 = new Like2();
like2.Lamda();
//4.局部内部类
class Like3 implements ILike{
@Override
public void Lamda() {
System.out.println("lamda3");
}
}
like=new Like3();
like.Lamda();
//匿名内部类,没有类的名称,必须借助接口或者父类
like=new ILike() {
@Override
public void Lamda() {
System.out.println("lamda4");
}
};
like.Lamda();
//6.lamda方法简化
like = ()->{
System.out.println("lamda5");
};
like.Lamda();
}
}
//1.定义一个函数式接口
interface ILike{
void Lamda();
}
//2.实现类
class Like implements ILike{
@Override
public void Lamda() {
System.out.println("lamda");
}
}
public class TestLamda2 {
public static void main(String[] args) {
Qq love = (int a)-> {
System.out.println("i like you" + a);
};
love.love(5);
//简化1 去掉参数类型
love = (a)-> {
System.out.println("i like you" + a);
};
love.love(6);
//简化2 去掉括号
love = a-> {
System.out.println("i like you" + a);
};
love.love(8);
//简化3 去掉花括号
love = a->
System.out.println("i like you" + a);
love.love(10);
/*
总结:
1、lambda表达式只能有一行代码情况下才能简洁成一行
2、前提必须是函数式接口
3、多个参数也可以去掉参数类型,要去掉就都去掉,必须加括号
*/
}
}
interface Qq{
void love(int a);
}
五、线程停止
线程五个状态
/*
1.建议线程正常停止--->利用次数,不死循环
2.建议使用标志位,即设置一个标志为
3.不要使用stop,destory,过时,jdk不建议使用
*/
public class TestStop implements Runnable{
//设置一个标识符
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag){
System.out.println("run...."+i++);
}
}
//设置一个公开的方法停止线程,转换标志
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 < 200; i++) {
System.out.println("main"+i);
if (i==90){
testStop.stop();
System.out.println("该线程停止");
}
}
}
}
六、线程休眠
sleep指定当前线程阻塞的毫秒数;
sleep存在异常,需要抛出;
sleep时间达到后线程就进入就绪状态;
sleep可以模拟网络延迟;(放大问题的发生性)
每个对象都有一把锁,sleep不会释放锁;
//模拟网络延迟 放大问题的发生性
public class TestSleep implements Runnable{
private int ticketNums=10;
@Override
public void run() {
while (true){
if (ticketNums<=0){
break;
}
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"票");
}
}
public static void main(String[] args) {
TestSleep testThread4 = new TestSleep();
//开启三个线程
new Thread(testThread4,"小明").start();
new Thread(testThread4,"小红").start();
new Thread(testThread4,"黄牛").start();
}
}
//模拟倒计时
public class TestSeelp2 {
public static void main(String[] args) {
// try {
// down();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//打印当前系统时间
Date time = new Date(System.currentTimeMillis());
while (true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(time));
time = new Date(System.currentTimeMillis());//更新时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void down() throws InterruptedException {
int num = 10;
while (true){
Thread.sleep(1000);
System.out.println(num--);
if (num<=0){
break;
}
}
}
}
七、线程礼让_yield
礼让线程,让当前正在执行的线程暂停,但不阻塞;
将线程从运行状态转为就绪状态;
让cpu重新调度,礼让不一定成功,看cpu
//测试礼让线程
//礼让不一定成功
public class TestYield {
public static void main(String[] args) {
MyYield myYield =new MyYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
八、线程强制执行——join
join合并线程,待此线程执行完后,再执行其他线程,其他线程阻塞;
可以想成插队;
不建议使用!!!
//测试join方法 相当于插队
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("线程join来了"+i);
}
}
public static void main(String[] args) throws InterruptedException {
//启动线程
TestJoin testJoin = new TestJoin();
Thread thread=new Thread(testJoin);
thread.start();
//主线程
for (int i = 0; i < 200; i++) {
if(i==150){
thread.join();//插队
}
System.out.println("main线程"+i);
}
}
}
九、观测线程状态
-
线程状态。线程可以处于以下状态之一:
NEW
尚未启动的线程处于此状态。RUNNABLE
在Java虚拟机中执行的线程处于此状态。BLOCKED
被阻塞等待监视器锁定的线程处于此状态。WAITING
正在等待另一个线程执行特定动作的线程处于此状态。TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。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("i");
}
});
//观察状态
Thread.State state = thread.getState();
System.out.println(state); //NEW
//观察启动后
thread.start();//启动线程
state=thread.getState();
System.out.println(state);
while (state!=Thread.State.TERMINATED){//只要线程不终止,就会一直输出
Thread.sleep(100);
state=thread.getState();//更新线程状态
System.out.println(state);//Run
}
}
}
十、线程优先级-priority
优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用,看cpu调度;
//测试线程优先级
public class TestPriority {
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);
Thread t5 = new Thread(myPriority);
Thread t6 = new Thread(myPriority);
//先设置优先级 在启动
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY);//=10
t4.start();
/*t5.setPriority(-1);
t5.start();
错误
t6.setPriority(11);
t6.start();
*/
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
}
}
十一、守护线程daemon
- 线程分为用户线程和守护线程;
- 虚拟机必须确保用户线程执行完毕(main)
- 虚拟机不用等待守护线程执行完毕;gc()
//测试守护线程
//上帝守护你
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread thread = new Thread(god);
thread.setDaemon(true);//默认false是用户线程,正常线程都是用户线程
thread.start();//守护线程启动
new Thread(you).start();//用户线程启动
}
}
//上帝
class God implements Runnable{
@Override
public void run() {
while (true){
System.out.println("上帝守护着你");
}
}
}
//你
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 500; i++) {
System.out.println("开心活着");
}
System.out.println("==goodbye");
}
}
十二、线程同步机制
**并发:**同一个对象被多个线程同时操作
线程同步形成条件:队列+锁
锁机制:synchronized
三大线程不安全案例
//不安全买票
public class UnsafeByTicked {
public static void main(String[] args) {
BuyTicked buyTicked = new BuyTicked();
new Thread(buyTicked,"1").start();
new Thread(buyTicked,"2").start();
new Thread(buyTicked,"3").start();
}
}
class BuyTicked implements Runnable{
//票
private int ticketNums=10;
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--);
}
}
//不安全取钱
public class UnsafeBank {
public static void main(String[] args) {
Account account = new Account(100,"存款");
Drawing you = new Drawing(account,50,"你");
Drawing he = new Drawing(account,100,"他");
you.start();
he.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(Thread.currentThread().getName()+"钱不够");
return;
}
//sleep放大问题发生性
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡内余额=余额-取的钱
account.money=account.money-drawingMoney;
//手里的钱
nowMoney=nowMoney+drawingMoney;
System.out.println(account.name+"余额为:"+account.money);
//this.getName()=Thread.currentThread().getName()
System.out.println(this.getName()+"手里的钱"+nowMoney);
}
}
//线程不安全集合
public class UnsafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
改过后的代码:
//不安全买票
public class UnsafeByTicked {
public static void main(String[] args) {
BuyTicked buyTicked = new BuyTicked();
new Thread(buyTicked,"1").start();
new Thread(buyTicked,"2").start();
new Thread(buyTicked,"3").start();
}
}
class BuyTicked implements Runnable{
//票
private int ticketNums=10;
boolean flag=true;
@Override
public void run() {
//买票
while (flag){
buy();
}
}
//同步方法 synchronized 锁的是this
private synchronized void buy(){
//判断是否有票
if (ticketNums<=0){
flag=false;
return;
}
//模拟延迟
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//买票
System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
}
}
//不安全取钱
public class UnsafeBank {
public static void main(String[] args) {
Account account = new Account(200,"存款");
Drawing you = new Drawing(account,50,"你");
Drawing he = new Drawing(account,100,"他");
you.start();
he.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() {
//锁的是变化的量,需要增删改的对象
synchronized (account){
//判断有没有钱
if (account.money-drawingMoney<0){
System.out.println(Thread.currentThread().getName()+"钱不够");
return;
}
//sleep放大问题发生性
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡内余额=余额-取的钱
account.money=account.money-drawingMoney;
//手里的钱
nowMoney=nowMoney+drawingMoney;
System.out.println(account.name+"余额为:"+account.money);
//this.getName()=Thread.currentThread().getName()
System.out.println(this.getName()+"手里的钱"+nowMoney);
}
}
}
//线程不安全集合
public class UnsafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
synchronized (list){
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
CopyOnWriteArrayList:
import java.util.concurrent.CopyOnWriteArrayList;
//测试JUC安全类型的集合
public class TestJUC {
public static void main(String[] args) {
CopyOnWriteArrayList<String> list=new CopyOnWriteArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
死锁:
多个线程各自占有一些共享资源﹐并且互相等待其他线程占有的资源才能运行﹐而导致两个或者多个线程都在等待对方释放资源﹐都停止执行的情形﹒某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题.
相互等待或抱着对方资源,于是都停下来了
产生死锁的四个必要条件:
1.互斥条件:一个资源每次只能被一个进程使用。
2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3.不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
上面列出了死锁的四个必要条件,我们只要想办法破其中的任意一个或多个条件就可以避免死锁发生
十三、lock锁
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MyWOX9J5-1620141510630)(F:\截图\线程\3.png)]
public class TestLock {
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 ticketNum = 10;
//定义lock锁
private final ReentrantLock reentrantLock=new ReentrantLock();
@Override
public void run() {
while (true){
try {
reentrantLock.lock();//加锁
if (ticketNum>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticketNum--);
}else{
break;
}
}finally {
reentrantLock.unlock();//解锁
}
}
}
}
synchronized 与Lock 的对比:
- Lock是显式锁(手动开启和关闭锁,别忘记关闭锁) synchronized是隐式锁,出了作用域自动释放
- Lock只有代码块锁,synchronized有代码块锁和方法锁
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
- 优先使用顺序:
Lock >同步代码块(已经进入了方法体,分配了相应资源)>同步方法(在方法体之外)
十四、线程协作
//测试:生产者 消费者模型-->利用缓冲区解决:管程法
public class TestPC {
public static void main(String[] args) {
SynContainer container=new SynContainer();
new Productor(container).start();
new Cousumer(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++) {
container.push(new Chicken(i));
System.out.println("生产了"+i+"只鸡");
}
}
}
//消费者
class Cousumer extends Thread{
SynContainer container;
public Cousumer(SynContainer 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 SynContainer{
//需要一个容器大小
Chicken[] chickens=new Chicken[10];
//容器计数器
int cout=0;
//生产者放入产品
public synchronized void push(Chicken chicken){
//如果容器满了,就要等待消费者消费
if (cout==chickens.length){
//让消费者消费,生产者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没满,我们需要继续生产丢入
chickens[cout]=chicken;
cout++;
//可以通知消费者消费产品
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken pop(){
//判断能否消费
if (cout==0){
//等待生产者生产,消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以消费
cout--;
Chicken chicken=chickens[cout];
//吃完了,通知生产者
this.notifyAll();
return chicken;
}
}
信号灯法:
//测试生产者消费者问题2:信号灯法,通过标志位解决
public class TestPC2 {
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 < 20; i++) {
if (i%2==0){
this.tv.play("快乐星球");
}else {
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 < 20; i++) {
tv.watch();
}
}
}
//产品-->节目
class TV{
//演员表演,观众等待
//观众观看,演员等待
String voice;//表演节目
boolean flag=true;
//表演
public synchronized void play(String voice){
if (!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了"+voice);
//通知观众观看
this.notifyAll();
this.voice=voice;
this.flag= !this.flag;
}
//观看
public synchronized void watch(){
if (flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观看了:"+voice);
//通知演员表演
this.notifyAll();
this.flag= !this.flag;
}
}
演员表演了快乐星球
观看了:快乐星球
演员表演了英雄联盟
观看了:英雄联盟
演员表演了快乐星球
观看了:快乐星球
演员表演了英雄联盟
观看了:英雄联盟
演员表演了快乐星球
观看了:快乐星球
十五、线程池
每一个线程的启动和结束都是比较消耗时间和占用资源的。
如果在系统中用到了很多的线程,大量的启动和结束动作会导致系统的性能变卡,响应变慢。
为了解决这个问题,引入线程池这种设计思想。
线程池的模式很像生产者消费者模式,消费的对象是一个一个的能够运行的任务
package multiplethread;
public class TestThread {
public static void main(String[] args) {
ThreadPool pool= new ThreadPool();
int sleep=1000;
while(true){
pool.add(new Runnable(){
@Override
public void run() {
//System.out.println("执行任务");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
try {
Thread.sleep(sleep);
sleep = sleep>100?sleep-100:sleep;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}