概念
之前:main函数–method—(回到)main函数 始终在一条路径上
现在:main函数–method-- 主线程和子线程并行
- 程序:静态
- 进程:动态,程序的一次执行过程;系统分配资源的单位;包含至少一个线程,真正执行的是线程(Main:主线程)
- 线程:在程序中真正执行的部分,一个进程(Process)中可以有多个线程(Thread)(视频:图像+字幕+声音)
eg:main(用户线程);gc(垃圾回收线程,守护线程) - 同一个调度器(cpu)下只能执行一个线程,不是同时执行(切换很快的错觉)
01线程创建
方法1:Thread class
继承Thread类
//创建线程方式1:继承Thread,重写run(),start()调用
public class TestThreads1 extends Thread{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("我在看代码———"+i);
}
}
public static void main(String[] args) {
TestThreads1 testThreads1 = new TestThreads1();
testThreads1.start();
for (int i = 0; i < 1000; i++) {
System.out.println("我在学习多线程--"+i);
}
}
}
输出结果:
可以很明显地看出二者交替执行,但是线程不一定立即执行,而是由CPU进行调度安排
网图下载
这里需要下载 commons-io-2.8.0 包,并在com下新建folder:lib,把包加入进去;同时将右键lib:add as Library;
在"Project Structure" - "Libraries"中也可以看到该路径
package com.emelia.Threads;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//练习Thread,实现多线程同步下载图片
public class TestThread2 implements Runnable{
private String url;
private String name;
public TestThread2(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) {
TestThread2 t1 = new TestThread2("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603814651866&di=0cbde25ac3c0729e9b6c995911bc990e&imgtype=0&src=http%3A%2F%2Fimg3.duitang.com%2Fuploads%2Fitem%2F201604%2F28%2F20160428230707_4By2X.thumb.700_0.jpeg", "1.jpg");
TestThread2 t2 = new TestThread2("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603814718604&di=0bdfe7eef362b2e647326ea3a7f7997a&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201512%2F17%2F20151217095926_hdNwJ.thumb.700_0.jpeg","2.jpg");
TestThread2 t3 = new TestThread2("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1603814746929&di=77228a46bfffc68294958af6213ed455&imgtype=0&src=http%3A%2F%2Fc-ssl.duitang.com%2Fuploads%2Fitem%2F201609%2F06%2F20160906220143_Gkdvt.jpeg","3.jpg");
new Thread(t1).start();
new Thread(t2).start();
new Thread(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接口
//创建线程方式2: 实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法
public class TestThread3 implements Runnable {
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("我在看代码———"+i);
}
}
public static void main(String[] args) {
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("我在学习多线程--"+i);
}
}
}
Thread和Runnable本身很像,因为Thread继承了Runnable
public class Thread implements Runnable {
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives();
static {
registerNatives();
}
推荐继承Runnable,避免单继承局限性,方便一个对象被多个线程使用
方法3:Callable接口(了解即可)
02并发
实例
//多个线程同时操作一个对象
//买火车票的例子
public class TestThread4 implements Runnable {
//票数
private int tickectNum = 30;
@Override
public void run() {
while (true){
if(tickectNum<=0){
break;
}
//模拟延时,用于系统太快、、、
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->拿到了第" + tickectNum-- + "张票");
}
}
public static void main(String[] args) {
TestThread4 testThread4 = new TestThread4();
new Thread(testThread4,"小明").start();
new Thread(testThread4,"老师").start();
new Thread(testThread4,"黄牛党").start();
}
}
出现重复!
练习:模拟龟兔赛跑
package com.emelia.Threads;
//模拟龟兔赛跑
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("兔子")){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag = gameOver(i);
//若比赛结束,则停止程序
if (flag){
break;
}
System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");
}
}
//判断是否完成比赛
public boolean gameOver(int steps) {
//判断有无winner
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();
}
}
03静态代理模式
以结婚为例
package com.emelia.proxyStatic;
//静态代理模式总结:
//真实对象和代理对象都要实现同一个接口
//代理对象要代理真实角色
//好处:
//代理对象可以做很多真实对象做不了的事情
//真实对象专注于做自己的事情
public class StaticProxy {
public static void main(String[] args) {
You you = new You();
WeddingCompany weddingCompany = new WeddingCompany(you);
weddingCompany.HappyMarry();
}
}
--->改写成:
public class StaticProxy {
public static void main(String[] args) {
You you = new You();//你要结婚
new Thread( ()-> System.out.println("我爱你") ).start();
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();
this.target.HappyMarry();
after();
}
private void before() {
System.out.println("结婚之前,布置现场");
}
private void after(){
System.out.println("结婚之后,收尾款");
}
}
04λ表达式
Runnable()
可以使用
注意演化过程的逐步推导
例子1
public class TestLambda01 {
//3.静态内部类
static class Like2 implements ILike{
@Override
public void lambda() {
System.out.println("I like lambda2");
}
}
public static void main(String[] args) {
ILike like = new Like();
like.lambda();
like = new Like2();
like.lambda();
//4.局部内部类
class Like3 implements ILike{
@Override
public void lambda() {
System.out.println("I like lambda3");
}
}
like = new Like3();
like.lambda();
//5.匿名内部类,没有实现类的名称,(这里直接new接口)必须借助接口or父类
like = new ILike() {
@Override
public void lambda() {
System.out.println("I like lambda4");
}
};
like.lambda();
//6.用lambda简化
like = ()->{
System.out.println("I like lambda5");
};
like.lambda();
}
}
//1.定义一个函数式接口(只有一个方法的接口)
interface ILike{
void lambda();
}
//2.实现类
class Like implements ILike{
@Override
public void lambda() {
System.out.println("I like lambda");
}
}
例子2
public class TestLambda02 {
public static void main(String[] args) {
Ilove love = (int a)-> {
System.out.println("I love u-->" + a);
};
love.love(520);
}
}
interface Ilove{
void love(int a);
}
05线程状态
1.线程停止:最好用flag使其主动停止
//测试stop
//1.建议线程正常停止-->利用次数,不建议死循环
//2.建议使用标志位-->设置一个标志位
//3.不使用stop/destroy等过时或JDK不建议使用的方法
public class TestStop implements Runnable{
//1.标志位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag) {
System.out.println("run...Thread"+i++);
}
}
//2.public method用来停止thread,change flag
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) {
//call self-made stop method
//change the flag
testStop.stop();
System.out.println("线程该停止了");
}
}
}
}
2.线程休眠
一般用于:
- 减缓速度、放大问题
//多个线程同时操作一个对象
//买火车票的例子
public class TestThread4 implements Runnable {
//票数
private int tickectNum = 20;
@Override
public void run() {
while (true){
if(tickectNum<=0){
break;
}
//模拟延时,用于系统太快、、、
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->拿到了第" + tickectNum-- + "张票");
}
}
public static void main(String[] args) {
TestThread4 testThread4 = new TestThread4();
new Thread(testThread4,"小明").start();
new Thread(testThread4,"老师").start();
new Thread(testThread4,"黄牛党").start();
new Thread(testThread4,"小花").start();
new Thread(testThread4,"小红").start();
new Thread(testThread4,"黄杨党").start();
}
}
- 打印当前时间(各一秒打印一次)
import java.text.SimpleDateFormat;
import java.util.Date;
//模拟倒计时
public class TestSleep2 {
public void countDown() throws InterruptedException {
int time = 10;
while (time>0) {
Thread.sleep(1000);
System.out.println(time);
time--;
}
}
public static void main(String[] args) {
//打印系统当前时间
Date date = new Date(System.currentTimeMillis());
while (true) {
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
date = new Date(System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3.线程礼让
礼让未必成功,只是将正在执行中的线程退回到起跑线,具体选谁,还需要看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() + "线程停止执行");
}
}
4.Join
一般不建议使用,会造成程序的阻塞
//测试join方法,想象为插队
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("Thread VIP" + 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 < 500; i++) { //此时主线程和thread线程同时执行,看CPU选谁
if (i == 200) { //main执行到200时必须让位
thread.join();
}
System.out.println("main" + i);
}
}
}
5.线程状态
注意,线程只能启动一次,Terminated之后就不能再Start了
package com.emelia.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);//NEW
thread.start();
state = thread.getState();
System.out.println(state);//RUN
while(state != Thread.State.TERMINATED) {
Thread.sleep(100);
state = thread.getState();
System.out.println(state);
}
}
}
6. 线程优先级
- 优先级不决定调用的顺序,只是概率提高
- 默认为5,范围1~10,其他会报错
//测试线程的优先级
public class TestPriority{
public static void main(String[] args) {
//主线程默认优先级
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
MyPriority testPriority = new MyPriority();
Thread t0 = new Thread(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);
//先设置优先级,再启动
t0.start();
t1.setPriority(1);
t1.start();
t2.setPriority(4);
t2.start();
t3.setPriority(8);
t3.start();
t4.setPriority(9);
t4.start();
t5.setPriority(Thread.MAX_PRIORITY); // MAX_PRIORITY = 10
t5.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
}
}
7. 守护线程daemon
- 线程分为:用户线程&守护线程
- 虚拟机必须确保用户线程执行完毕(main)
- 虚拟机不必等待守护线程执行完毕(gc)
- 如后台记录操作日志,监控内存,垃圾回收…
//测试守护线程
//上帝守护你
public class TestDaemon {
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 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 < 36500; i++) {
System.out.println("你一生都开心的活着");
}
System.out.println("==========Goodbye, World!===========");
}
}
守护线程运行指导被守护的对象终止,一同终止
06线程同步 synchronized
- 并发:同一个对象被多个线程同时操作
- 线程同步:等待机制,多个需要访问该对象的线程同时进入对象的等待池
- 队列&锁:每个对象都有一把锁。作用是该线程启用之后锁上,其他线程无法访问该对象
- 问题:
- 一个线程持有锁会导致其他所有需要此锁的线程挂起
- 在多线程竞争下,加锁,释放锁会导致比较多的上下文切換和调度延时,引起性能问题
- 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性问题
例子1:买票
//不安全的买票
//拿到-1或者重复拿:每个程序都有自己独立的内存,当下看到只有1,都去执行,最终被减完就只剩下负数了
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket,"A").start();
new Thread(buyTicket,"B").start();
new Thread(buyTicket,"C").start();
}
}
class BuyTicket implements Runnable {
private int ticketNums = 10;
private boolean flag = true; //外部停止方式
@Override
public void run() {
//买票
while (flag) {
buy();
}
}
private void buy() {
//判断是否有
if (ticketNums <= 0) {
flag = false;
return; //此处需要return来使Thread停止
}
//模拟延时
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//买票,此处不需要循环,否则程序无法终止
System.out.println(Thread.currentThread().getName() + "买了第" + ticketNums-- + "张票");
}
}
例子2:取钱
//不安全的取钱
public class UnsafeBank {
public static void main(String[] args) {
Account account = new Account(100, "结婚基金");
Drawing boy = new Drawing(account, 50, "Boy");
Drawing girl = new Drawing(account, 100, "Girl");
boy.start();
girl.start();
}
}
//账户
class Account {
int money;
String name;
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
//银行:模拟取款
class Drawing extends Thread { //不存在多继承问题,这里用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 -= drawingMoney;
//你手里的钱
nowMoney += drawingMoney;
System.out.println(account.name+"余额为"+account.money);
//Thread.currentThread().getName() = this.getName()
System.out.println(this.getName()+"手里的钱:"+nowMoney);
}
}
例子3:集合
list不能被完全填满,size达不到10000,这是因为多个线程可能同时向一个位置填充name,导致被覆盖掉了
//线程不安全的集合
public class UnsafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<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());
}
}
锁机制 synchronized
对象:private
方法:synchronized
重点是在于要锁住会发生增删改查的对象
同步方法
public synchronized void method(int args)
//对于例子1:买票
private synchronized void buy()
同步块
//对于例子2:取钱
public synchronized void run()
/*没有用,synchronized默认是对this进行操作,
*在这里即为Drawing
*而实际上发生变化的是Account而不是Drawing
*因此应该利用同步块锁住Account
*/
@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 -= drawingMoney;
//你手里的钱
nowMoney += drawingMoney;
System.out.println(account.name+"余额为"+account.money);
//Thread.currentThread().getName() = this.getName()
System.out.println(this.getName()+"手里的钱:"+nowMoney);
}
}
//对于例子3:
for (int i = 0; i < 10000; i++) {
new Thread(()->{
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
补充:JUC
import java.util.concurrent.CopyOnWriteArrayList;
//测试JUC安全类型的集合
public class TestJUC {
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(); //线程安全的集合
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(3000);
System.out.println(list.size());
}
}
CopyOnWriteArrayList
中的源码:
final transient Object lock = new Object();
private transient volatile Object[] array;
死锁
某个同步块同时拥有2+对象的锁
多个线程互相抱着对方所需的资源,形成僵持
public class DeadLock {
public static void main(String[] args) {
Makeup makeup1 = new Makeup(0,"白雪公主1");
Makeup makeup2 = new Makeup(1,"灰姑凉2");
new Thread(makeup1).start();
new Thread(makeup2).start();
}
}
class Lipstick{}
class Mirror{}
class Makeup extends Thread{
static Lipstick lipstick = new Lipstick();
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();
}
}
public void makeup() throws InterruptedException {
if (choice == 0) {
synchronized (lipstick) {
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 (lipstick) {
System.out.println(this.girlName+"获得口红的锁");
}
}
}
}
}
解决方法:不要抱住对方的锁,从synchronized中把另一个取出来,拿完以后释放!
public void makeup() throws InterruptedException {
if (choice == 0) {
synchronized (lipstick) {
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 (lipstick) {
System.out.println(this.girlName+"获得口红的锁");
}
}
}
Lock(锁)
- JDK5.0之后,提供显式定义同步锁
java.util.concurrent.locks.Lock
接口reentrantLock
是Lock
的实现类,类似synchronized
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
lock lock = new lock();
new Thread(lock).start();
new Thread(lock).start();
new Thread(lock).start();
}
}
class lock extends Thread {
int tickNum = 10;
//定义lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock(); //加锁
if (tickNum > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(tickNum--);
}
} finally {
//解锁
lock.unlock();
}
}
}
}
Lock & synchronized:
07线程协作(生产者消费者问题)
问题解决方式分析
解决方法1
解决方法2
设置flag,判断何时等待、何时监听
1.管程法:设置缓冲区
//测试生产者消费者模型,利用缓冲区解决:管程法
import java.awt.*;
//生产者、消费者、产品、缓冲区
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++) {
container.push(new Chicken(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.pop().id+"只鸡");
}
}
}
//产品
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) {
//如果容器满了,就需要等待消费者消费
if (count == chickens.length) {
//通知消费者消费,生产等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有满,就需要丢入产品
chickens[count] = chicken;
count++;
//可以通知消费者消费了
this.notifyAll();
}
//消费者消费产品
public synchronized Chicken pop() {
//判断能否消费
if (count == 0) {
//等待生产者生产,消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以消费
count--;
Chicken chicken = chickens[count];
//吃完了,通知生产者生产
this.notifyAll();
return chicken;
}
}
2.设置标记位
//测试生产者消费者问题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;
}
}
线程池
- 创建多个线程放入线程池,使用时获取,使用完放入。避免重复创建、销毁,实现重复利用
- 好处:
** 相关API:**
ExecutorService
& Executors
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestPools {
public static void main(String[] args) {
//1.创建服务,创建线程池
//newFixedThreadPool 参数为:线程池大小
ExecutorService service = Executors.newFixedThreadPool(10);
//执行
//提交Callable用submit,提交Runnable用execute
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//2.关闭连接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
总结
//线程的3种创建方法package com.emelia.advanced;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//回顾
public class ThreadNew {
public static void main(String[] args) {
new MyThread1().start();
new Thread(new MyThread2()).start(); //
FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());
new Thread(futureTask).start();//futureTask继承了Runnable接口,理论上也可以这样启动
try {
Integer integer = futureTask.get();
System.out.println(integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
//1.继承Thread类
class MyThread1 extends Thread{
@Override
public void run() {
System.out.println("MyThread1");
}
}
//2.实现Runnable接口
class MyThread2 implements Runnable{
@Override
public void run() {
System.out.println("MyThread2");
}
}
//3.实现Callable接口
class MyThread3 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("MyThread3");
return 100;
}
}