一、线程简介(任务、进程、线程、多线程)
1.多任务
2.多线程
注:生活中的多线程例子(生活、游戏、编程)
3.普通方法调用和多线程
4.程序、进程(Process)、线程(Thread)
4.1 说起进程,不得不说一下程序。程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。
4.2 而进程是执行程序的一次执行过程,是一个动态的概念,是系统资源分配的单位。
4.3 通常在一个进程中可以包含若干个线程,当然一个进程至少有一个线程,不然没有存在的意义,线程是CPU调度和执行的单位。
5. 本章核心概念
二、线程的创建(三种)
Thread class --> 继承Thread类 Runnable接口 --> 实现Runnable接口 Callable接口 --> 实现Callable接口
1.线程创建方法一
继承Thread类,实现run方法,调用start开启线程
代码实现
2.线程创建方法二(实现Runnable接口)
实现Runnable接口,重写run方法,执行线程时需要丢入Runnable接口实现类并调用start方法
代码实现
3.两种线程实现方式对比
4. 创建线程方法三(实现Callable接口)
好处:
- 可以抛出异常
- 可以定义返回值
缺点:
- 步骤繁琐
- 不常使用,一般使用方法二
三、初识并发问题
1. 使用购买火车票的例子说明问题(代码实现)
package TestThread;
//使用多线程操作同一对象(购买火车票的例子)
public class TestThread1 implements Runnable{
//表示票数
private int tickedNums = 15;
@Override
public void run() {
while(true) {
if(tickedNums <= 0) {
break;
}
//模拟延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->购买了第"+tickedNums--+"张票");
}
}
public static void main(String[] args) {
TestThread1 ticket = new TestThread1();
new Thread(ticket,"小明").start();
new Thread(ticket,"小刚").start();
new Thread(ticket,"黄牛党").start();
}
}
结果分析:
小刚-->购买了第15张票
小明-->购买了第14张票
黄牛党-->购买了第13张票
小明-->购买了第11张票
小刚-->购买了第12张票
黄牛党-->购买了第10张票
小明-->购买了第9张票
小刚-->购买了第9张票
黄牛党-->购买了第8张票
小刚-->购买了第7张票
小明-->购买了第7张票
黄牛党-->购买了第6张票
小明-->购买了第5张票
小刚-->购买了第4张票
黄牛党-->购买了第3张票
小刚-->购买了第1张票
小明-->购买了第2张票
分析结果可知小明和小刚同时购买了第九张票,造成冲突,即多个线程操作同一个对象的时候会造成线程不安全的状况。
四、静态代理
通过一段代码进行理解
package TestStaticProxy;
public class StaticProxy {
public static void main(String[] args) {
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("结婚前布置婚礼现场");
}
}
"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" "-javaagent:D:\IntelliJ IDEA\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=61530:D:\IntelliJ IDEA\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;C:\Users\1\IdeaProjects\Test\out\production\Test" TestStaticProxy.StaticProxy
结婚前布置婚礼现场
我要结婚了,超开心
结婚后,收尾款
Process finished with exit code 0
静态代理模式总结:
- 真实对象和代理对象都要实现同一个接口
- 代理对象要代替真实对象做事情
静态代理的好处:
- 代理对象可以实现很多真实对象实现不了的事情
- 而真实对象可以专注于做自己的事情
注意:多线程的实现方式二相当于是使用了静态代理模式
五、线程的五大状态
2. 停止线程
注意:
- 建议线程正常停止–》建议使用次数,不建议使用死循环
- 建议使用标志位停止线程 --》 设置一个标志位即可
- 不用使用stop,destroy等过时的JDK不建议使用的方法
3. 线程休眠
注意:
线程休眠主要是用来模拟延时或者制作计时用的。
4. 线程礼让(yield)
注意:一定要注意礼让不一定成功
左图是礼让成功,b线程先跑,然后a线程再跑;右图是没有礼让成功,依旧是a线程先跑,b线程再跑。
代码实现:
package TestYeild;
class TestYeild {
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()+"线程开始执行");
}
}
5. 线程强制执行(join)
6.线程状态观测
7. 线程的优先级
package TestPriority;
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);
//先设置优先级再启动
t1.start();//默认优先级是5
t2.setPriority(1);
t2.start();
t3.setPriority(4);
t3.start();
//设置为最大优先级,最大优先级是10
t4.setPriority(Thread.MAX_PRIORITY);
t4.start();
}
}
class MyPriority implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());
}
}
运行结果
"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" "-javaagent:D:\IntelliJ IDEA\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=53717:D:\IntelliJ IDEA\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;C:\Users\1\IdeaProjects\Test\out\production\Test" TestPriority.TestPriority
main-->5
Thread-3-->10
Thread-0-->5
Thread-2-->4
Thread-1-->1
Process finished with exit code 0
注意:
- 优先级低只是意味着被CPU调度的可能性低,并不是优先级低就不会被调用了,这取决于CPU是否要调度
- 建议先设置优先级,再进行start调度
8. 守护线程(daemon)
注:常见的用户线程是main线程
代码实现
package TestDaemon;
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
You you = new You();
Thread thread = new Thread(god);
//将该线程设置为守护线程,默认状况下该参数是false,为false时是用户线程
thread.setDaemon(true);
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("=====good bye=====");
}
}
注意:
- 虚拟机不用等待守护线程执行完毕,只要用户线程执行完毕后,守护线程也就会停止。
六、线程同步(多个线程操作同一个资源时需要线程同步)
- 三大不安全案例
案例一:买票案例(代码实现)
package syn;
import javax.swing.*;
//线程不安全,会获得负数的票或者两人同时买了同一张票
public class UnsafeBuyTickets {
public static void main(String[] args) {
BuyTicket ticket = new BuyTicket();
new Thread(ticket,"小明").start();
new Thread(ticket,"小刚").start();
new Thread(ticket,"黄牛党").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();
}
}
}
//编写购票方法
public void buy() throws InterruptedException {
if(ticketNums <= 0) {
flag = false;
return;
}
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "购买了第" + ticketNums-- + "张票");
}
}
- 线程不安全问题案例二(银行取款)
package syn;
import java.util.ArrayList;
//两个人同时操作一个账户,会发生线程不安全
public class UnSafeBank {
public static void main(String[] args) {
Account account = new Account(100,"结婚基金");
Bank you = new Bank(account,50,"You");
Bank girlFirend = new Bank(account,100,"girlFirend");
you.start();
girlFirend.start();
}
}
class Account {
//余额
int money;
//持卡人名字
String name;
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
//银行模拟取款
class Bank extends Thread {
//账户
Account account;
//要从银行中取走的金额数
int drawMoney;
//目前用户手中的钱
int nowMoney;
public Bank(Account account,int drawMoney,String name) {
super(name);
this.account = account;
this.drawMoney = drawMoney;
}
public void run() {
//判断账户中有没有钱
if(account.money - drawMoney < 0) {
System.out.println(Thread.currentThread().getName() + "钱不够,取款失败");
return;
}
//sleep方法可以放大问题的发生性
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卡里的余额
account.money = account.money - drawMoney;
//目前手中已经有的钱
nowMoney = nowMoney + drawMoney;
System.out.println(account.name + "余额为:" + account.money);
//这里的this.money 等价于 Thread.currentThread().getName()
System.out.println(this.getName() + "手里的钱为:" + nowMoney );
}
}
"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" "-javaagent:D:\IntelliJ IDEA\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=58902:D:\IntelliJ IDEA\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;C:\Users\1\IdeaProjects\Test\out\production\Test" syn.UnSafeBank
结婚基金余额为:-50
girlFirend手里的钱为:100
结婚基金余额为:-50
You手里的钱为:50
Process finished with exit code 0
- 线程不安全案例三(集合)
多个线程同时操作一个集合,可能会发生对同一个位置的重复赋值。
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<>();
for (int i = 0; i < 10000; i++) {
new Thread(() -> {
list.add(Thread.currentThread().getName());
}).start();
}
//Thread.sleep(1000);
System.out.println(list.size());
}
}
"C:\Program Files\Java\jdk1.8.0_181\bin\java.exe" "-javaagent:D:\IntelliJ IDEA\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar=59054:D:\IntelliJ IDEA\IntelliJ IDEA 2019.3.3\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_181\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_181\jre\lib\rt.jar;C:\Users\1\IdeaProjects\Test\out\production\Test" syn.UnsafeList
9676
Process finished with exit code 0
-
同步方法
-
同步块
一定要注意synchronized锁的对象是增删改的对象
七、死锁问题
1. 死锁的概念
2. 产生死锁的必要条件
要想解除死锁,就破坏产生死锁的必要条件中的一个或者两个即可。
- 死锁问题的代码实现(下面代码会产生死锁,程序会卡住不会往下进行)
package TestDeadLock;
public class TestDeadLock {
public static void main(String[] args) {
Makeup t1 = new Makeup(0,"灰姑娘");
Makeup t2 = new Makeup(1,"白雪公主");
t1.start();
t2.start();
}
}
//创建一个口红的类
class LipStick {
}
//创建一个镜子的类
class Mirro {
}
//创建一个化妆的类
class Makeup extends Thread {
//使用static关键字表示只有一个共享资源
static LipStick lipStick = new LipStick();
static Mirro mirro = new Mirro();
int choice;
String girtName;
public Makeup(int choice,String girtName) {
this.choice = choice;
this.girtName = girtName;
}
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 编写一个化妆函数,互相持有对方需要的资源,而自身又想拥有对方的资源
private void makeup() throws InterruptedException {
if(choice == 0) {
synchronized (lipStick) {
//获得口红的锁
System.out.println(this.girtName + "获得口红的锁");
Thread.sleep(1000);
//一秒钟之后又想要获得镜子
synchronized (mirro) {
System.out.println(this.girtName + "获得镜子的锁");
}
}
}else {
synchronized (mirro) {
//获得口红的锁
System.out.println(this.girtName + "获得镜子的锁");
Thread.sleep(2000);
//两秒钟之后又想要获得镜子
synchronized (lipStick) {
System.out.println(this.girtName + "获得口红的锁");
}
}
}
}
}
- 解除死锁的代码实现
package TestDeadLock;
public class TestDeadLock {
public static void main(String[] args) {
Makeup t1 = new Makeup(0,"灰姑娘");
Makeup t2 = new Makeup(1,"白雪公主");
t1.start();
t2.start();
}
}
//创建一个口红的类
class LipStick {
}
//创建一个镜子的类
class Mirro {
}
//创建一个化妆的类
class Makeup extends Thread {
//使用static关键字表示只有一个共享资源
static LipStick lipStick = new LipStick();
static Mirro mirro = new Mirro();
int choice;
String girtName;
public Makeup(int choice,String girtName) {
this.choice = choice;
this.girtName = girtName;
}
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 编写一个化妆函数,互相持有对方需要的资源,而自身又想拥有对方的资源
private void makeup() throws InterruptedException {
if(choice == 0) {
synchronized (lipStick) {
//获得口红的锁
System.out.println(this.girtName + "获得口红的锁");
Thread.sleep(1000);
}
//解除死锁
synchronized (mirro) {
System.out.println(this.girtName + "获得镜子的锁");
}
}else {
synchronized (mirro) {
//获得口红的锁
System.out.println(this.girtName + "获得镜子的锁");
Thread.sleep(2000);
}
//解除死锁
synchronized (lipStick) {
System.out.println(this.girtName + "获得口红的锁");
}
}
}
}
- Lock锁
代码实现
package TestLock;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
BuyTicket ticket = new BuyTicket();
new Thread(ticket).start();
new Thread(ticket).start();
new Thread(ticket).start();
}
}
class BuyTicket implements Runnable {
int ticketNum = 10;
//定义一个可重入锁
private final ReentrantLock reentrantLock = new ReentrantLock();
@Override
public void run() {
//显式的加锁,使用try-finally结构
try {
reentrantLock.lock();
while(true) {
if(ticketNum > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("剩余票数为:" + ticketNum--);
}else {
break;
}
}
} finally {
reentrantLock.unlock();
}
}
}
synchronized和lock锁的区别
八、线程协作(生产者消费者模式)
- 线程通信
注意:这些方法均是Object类的方法,只能在同步方法或者同步代码块中使用,否则会抛出lllegalMonitorStateException
- 生产者消费者模式的解决方法
2.1 解决方式1(管程法)
代码实现
package TestProductAndConsumer;
import javax.print.DocFlavor;
//测试生产者消费者模式--管程法
//需要四个类,分别为生产者、消费者、产品和缓冲区
public class TestProductAndConsumer {
public static void main(String[] args) {
Syncontainer syncontainer = new Syncontainer();
Productor productor = new Productor(syncontainer);
Consumer consumer = new Consumer(syncontainer);
productor.start();
consumer.start();
}
}
//生产者
class Productor extends Thread {
Syncontainer syncontainer;
public Productor(Syncontainer syncontainer) {
this.syncontainer = syncontainer;
}
//编写生产者生产产品的方法
@Override
public void run() {
for (int i = 1; i < 100; i++) {
System.out.println("生产了第" + i + "只鸡");
try {
syncontainer.push(new Chicken(i));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//由于生产者和消费者都需要一个消费缓冲区,所以在生产者和消费者的类中都需要定义这个容器
//消费者
class Consumer extends Thread {
Syncontainer syncontainer;
public Consumer(Syncontainer syncontainer) {
this.syncontainer = syncontainer;
}
//编写消费者消费产品的方法
@Override
public void run() {
for (int i = 1; i < 100; i++) {
System.out.println("消费了第" + syncontainer.pop().id + "只鸡");
syncontainer.pop();
}
}
}
//产品(用烧鸡代替)
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.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 解决方法二(信号灯法)
九、线程池
- 使用线程池
代码实现
package TestPoll;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestPoll {
public static void main(String[] args) {
//创建服务,创建线程池,容量为10
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());
}
}