进程在运行的过程中不断的改变其运行状态。通常一个运行的进程必须有三个状态,就绪态、运行态、阻塞态。
步骤:
- 自定义线程类继承Thread类
- 重写run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程(启动后不一定立即执行,抢到CPU资源才能执行)
// 自定义线程对象,继承Thread,重写run()方法
public class MyThread extends Thread {
public MyThread(String name){
super(name);
}
@Override
public void run() {
// 线程执行体
for (int i = 0; i < 10; i++) {
System.out.println("我是自定义" + Thread.currentThread().getName() + "--" + i);
}
}
public static void main(String[] args) {
// main线程,主线程
// 创建线程实现类对象
MyThread thread = new MyThread("线程1");
MyThread thread2 = new MyThread("线程2");
// 调用start()方法启动线程
thread.start();
thread2.start();
for (int i = 0; i < 10; i++) {
System.out.println("我是主线程--" + i);
}
}
}
运行:
步骤:
- 自定义线程类实现Runnable接口
- 实现run()方法,编写线程体
- 创建线程对象,调用start()方法启动线程(启动后不一定立即执行,抢到CPU资源才能执行)
// 自定义线程对象,实现Runnable接口,重写run()方法
public class MyRunnable implements Runnable {
@Override
public void run() {
// 线程执行体
for (int i = 0; i < 10; i++) {
System.out.println("我是自定义" + Thread.currentThread().getName() + "--" + i);
}
}
public static void main(String[] args) {
// main线程,主线程
// 创建实现类对象
MyRunnable myRunnable = new MyRunnable();
// 创建代理类对象
Thread thread = new Thread(myRunnable,"线程1");
Thread thread2 = new Thread(myRunnable,"线程2");
// 调用start()方法启动线程
thread.start();
thread2.start();
for (int i = 0; i < 10; i++) {
System.out.println("我是主线程--" + i);
}
}
}
输出
步骤:
- 实现Callable接口,先要返回值类型
- 重写call()方法,需要抛出异常
- 创建目标对象
- 创建执行服务:ExecutorService ser = Executor.newFixedThreadPool(1);
- 提交执行:Futureres = ser.submit(t1);
- 获取结果:boolean r1 = res.get();
- 关闭服务:ser.shutdownNow();
import java.util.concurrent.*;
// 自定义线程对象,实现Callable接口,重写call()方法
public class MyThread implements Callable {
@Override
public Boolean call() throws Exception {
// 线程执行体
for (int i = 0; i < 10; i++) {
System.out.println("我是自定义" + Thread.currentThread().getName() + "--" + i);
}
return true;
}
public static void main(String[] args) throws ExecutionException,
InterruptedException {
// main线程,主线程
// 创建线程实现类对象
MyThread thread = new MyThread();
MyThread thread2 = new MyThread();
// 创建执行服务,参数是线程池线程数量
ExecutorService ser = Executors.newFixedThreadPool(2);
// 提交执行
Future res = ser.submit(thread);
Future res2 = ser.submit(thread2);
// 获取结果
boolean r1 = res.get();
boolean r2 = res2.get();
// 关闭服务
ser.shutdownNow();
}
}
输出
1、 sleep(Long time)方法:
- 让线程阻塞的指定的毫秒数。
- 指定的时间到了后,线程进入就绪状态。
- sleep可研模拟网络延时,倒计时等。
- 每一个对象都有一个锁,sleep不会释放锁。
public class MyThread implements Runnable {
@Override
public void run() {
// 模拟倒计时
for (int i = 10; i >= 0; i--) {
try {
System.out.println(i);
//1000代表1秒,每过一秒输出一次
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
}
}
2、yield()方法:
- 提出申请释放CPU资源,至于能否成功释放取决于JVM决定。
- 调用yield()方法后,线程仍然处于RUNNABLE状态,线程不会进入阻塞状态。
- 调用yield()方法后,线程处于RUNNABLE状态,就保留了随时被调用的权利。
public class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName() + "线程结束执行");
}
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread,"a");
Thread thread2 = new Thread(myThread,"b");
thread.start();
thread2.start();
}
}
执行结果:从结果1看,a释放CPU成功后,b就抢到了CPU执行权,接着b也释放CPU成功,a抢到了CPU执行权;从结果2看,a并没有成功释放CPU。
结果1:
a线程开始执行
b线程开始执行
a线程结束执行
b线程结束执行
结果2:
a线程开始执行
a线程结束执行
b线程开始执行
b线程结束执行
3、join()方法:
- 将当前的线程挂起,当前线程阻塞,待其他的线程执行完毕,当前线程才能执行。
- 可以把join()方法理解为插队,谁插到前面,谁先执行。
public class MyThread implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "join()线程执行:" + i);
}
}
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread,"a");
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println("主线程执行:" + i);
if (i == 2) {
thread.join(); //主线程阻塞,等待thread一口气执行完,主线程才能继续执行
}
}
}
}
主线程执行:0
a线程join()执行:0
主线程执行:1
主线程执行:2
a线程join()执行:1
a线程join()执行:2
a线程join()执行:3
a线程join()执行:4
a线程join()执行:5
a线程join()执行:6
a线程join()执行:7
a线程join()执行:8
a线程join()执行:9
主线程执行:3
主线程执行:4
主线程执行:5
主线程执行:6
主线程执行:7
主线程执行:8
主线程执行:9
4、setPriority (int newPriority)、getPriority()
- 改变、获取线程的优先级。
- Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。
- 线程的优先级用数据表示,范围1~10。
- 线程的优先级高只是表示他的权重大,获取CPU执行权的几率大。
- 先设置线程的优先级,在执行start()方法。
执行结果:优先级高的线程不一定先执行
c线程优先级:10
b线程优先级:5
a线程优先级:1
d线程优先级:8
5、stop()、destroy()。【已废弃】
- JDK提供的上述两种方法已废弃,不推荐使用。
- 推荐线程自动停止下来,建议使用一个标识位变量进行终止,当flag=false时,则终止线程运行。
public class MyThread implements Runnable {
/**
* 标识位,为false时,线程结束
*/
private boolean flag = true;
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
int i = 0;
@Override
public void run() {
while (flag) {
System.out.println(Thread.currentThread().getName() + "线程:" + i ++);
}
}
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread,"a");
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println("主线程:" + i);
if (i == 5) {
// 当主线程 i== 5时,标识位变为false,控制子线程停止
myThread.setFlag(false);
}
}
}
}
执行结果:主线程 i== 5 之后,子线程就停止运行了
主线程:0
主线程:1
a线程:0
a线程:1
a线程:2
a线程:3
a线程:4
a线程:5
a线程:6
主线程:2
a线程:7
主线程:3
a线程:8
主线程:4
a线程:9
主线程:5
a线程:10
主线程:6
主线程:7
主线程:8
主线程:9
线程同步就是线程排队,就是操作共享资源要有先后顺序,一个线程操作完之后,另一个线程才能操作或者读取。
public class MyThread{
public static void main(String[] args) throws InterruptedException {
DeamonThread deamon = new DeamonThread();
UserThread user = new UserThread();
Thread deamonThread = new Thread(deamon);
deamonThread.setDaemon(true); // 设置为守护进程
deamonThread.start();
Thread userThread = new Thread(user);
userThread.start();
}
}
// 模拟守护线程
class DeamonThread implements Runnable{
@Override
public void run() {
// 验证虚拟机不用等待守护线程执行完毕,只要用户线程执行完毕,程序就结束。
// 如果成功,怎下面的打印不会一直输出;如果成功,则下面的打印会一直输出
while (true) {
System.out.println("我是守护线程");
}
}
}
// 用户线程
class UserThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("我是用户线程 :" + i);
}
}
}
执行结果:守护进程不会一直打印
我是守护线程
我是用户线程 :0
我是守护线程
...
我是用户线程 :1
我是守护线程
...
我是用户线程 :2
我是用户线程 :3
我是用户线程 :4
我是用户线程 :5
我是用户线程 :6
我是用户线程 :7
我是用户线程 :8
我是用户线程 :9
我是守护线程
...
我是守护线程
在多线程场景下,如果多个线程修改同一个资源,或者一个线程修改共享资源,另一个线程读取共享资源,可能会导致结果不对的问题,这就导致线程不安全,即并发。导致线程并发的原因:
下面以两个例子演示线程不安全问题。
示例1:买票问题
// 模拟线程不安全问示例1:买票
public class MyThread{
public static void main(String[] args) throws InterruptedException {
BuyTicker ticker = new BuyTicker();
Thread person1Thread = new Thread(ticker, "person1");
Thread person2Thread = new Thread(ticker, "person2");
Thread person3Thread = new Thread(ticker, "person3");
person1Thread.start();
person2Thread.start();
person3Thread.start();
}
}
class BuyTicker implements Runnable{
// 车票
private int tickerNum = 10;
// 停止线程标识
boolean flag = true;
@Override
public void run() {
while (flag) {
try {
buyTicker();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void buyTicker() throws InterruptedException {
// 判断是否还有票
if (tickerNum <= 0) {
flag = false;
return;
}
// 模拟延时
Thread.sleep(100);
// 买票
System.out.println(Thread.currentThread().getName() + "买到第" + tickerNum -- + "张票");
}
}
可以看到第4、3张票卖了两次,还有人买到了第0张票
person3买到第8张票
person2买到第10张票
person1买到第9张票
person1买到第7张票
person3买到第5张票
person2买到第6张票
person1买到第4张票
person2买到第4张票
person3买到第3张票
person1买到第2张票
person2买到第2张票
person3买到第1张票
person1买到第0张票
示例2:银行取钱
// 模拟线程不安全示例2:银行取钱
public class MyThread{
public static void main(String[] args) throws InterruptedException {
Account account = new Account(1000, "旅游基金");
new Bank(account, 500, "你").start();
new Bank(account, 600, "女朋友").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 drawingMoney;
public Bank(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(account.name+ "钱不够了,取不了了");
return;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 卡内余额 = 余额 - 取得钱
account.money = account.money - drawingMoney;
System.out.println(Thread.currentThread().getName() + "取了" + drawingMoney);
System.out.println(account.name + "余额为:" + account.money);
}
}
当你取500时,线程执行到account.money = account.money - drawingMoney之前,另一个线程抢到了CPU执行权,也执行到account.money = account.money - drawingMoney之前,现在余额还是1000,继续执行1000-500-900=-400.
你取了500
女朋友取了900
旅游基金余额为:-400
旅游基金余额为:-400
解决线程并发问题的方法是线程同步,线程同步就是让线程排队,就是操作共享资源要有先后顺序,一个线程操作完之后,另一个线程才能操作或者读取。
解决并发问题的两种方法:
① 同步方法:public synchronized void method(int args){执行体…}
② 同步代码块:synchronized (Obj){执行体…}
示例1:买票问题(使用同步方法改造成线程安全)
// 模拟线程不安全问示例1:买票
public class MyThread{
public static void main(String[] args) throws InterruptedException {
BuyTicker ticker = new BuyTicker();
Thread person1Thread = new Thread(ticker, "person1");
Thread person2Thread = new Thread(ticker, "person2");
Thread person3Thread = new Thread(ticker, "person3");
person1Thread.start();
person2Thread.start();
person3Thread.start();
}
}
class BuyTicker implements Runnable{
// 车票
private int tickerNum = 10;
// 停止线程标识
boolean flag = true;
@Override
public void run() {
while (flag) {
try {
buyTicker();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private synchronized void buyTicker() throws InterruptedException {
// 判断是否还有票
if (tickerNum <= 0) {
flag = false;
return;
}
// 模拟延时
Thread.sleep(100);
// 买票
System.out.println(Thread.currentThread().getName() + "买到第" + tickerNum -- + "张票");
}
}
person1买到第10张票
person1买到第9张票
person1买到第8张票
person1买到第7张票
person1买到第6张票
person1买到第5张票
person3买到第4张票
person3买到第3张票
person3买到第2张票
person2买到第1张票
示例2:银行取钱(使用同步代码块改造成线程安全)
// 模拟线程不安全示例2:银行取钱
public class MyThread{
public static void main(String[] args) throws InterruptedException {
Account account = new Account(1000, "旅游基金");
new Bank(account, 500, "你").start();
new Bank(account, 600, "女朋友").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 drawingMoney;
public Bank(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(account.name+ "钱不够了,取不了了");
return;
}
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 卡内余额 = 余额 - 取得钱
account.money = account.money - drawingMoney;
System.out.println(Thread.currentThread().getName() + "取了" + drawingMoney);
System.out.println(account.name + "余额为:" + account.money);
}
}
}
执行结果:你取了500后,你女朋友取600,就提示余额不足,不会出现余额为负数的情况了。这里的同步监视器是account,account才是操作的共享资源,而不是bank。
你取了500
旅游基金余额为:500
旅游基金钱不够了,取不了了
示例3:模拟集合ArrayList<>()是线程不安全的
import java.util.ArrayList;
import java.util.List;
// 模拟线程不安全示例2:银行取钱
public class MyThread{
public static void main(String[] args) throws InterruptedException {
List list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
// 加锁
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
Thread.sleep(5000);
System.out.println(list.size());
}
}
999
使用同步代码块改造成线程安全的
import java.util.ArrayList;
import java.util.List;
// 模拟线程不安全示例2:银行取钱
public class MyThread{
public static void main(String[] args) throws InterruptedException {
List list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
// 加锁
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
Thread.sleep(5000);
System.out.println(list.size());
}
}
1000
(1)死锁形成的原因:多个线程各自占有一个资源,并且相互等待其他线程占有的资源才能运行,从而导致另个或者多个线程都在等待对方释放资源,都停止了执行。某一个同步代码块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题。
// 死锁例子:鱼和熊不可兼得
public class MyThread{
public static void main(String[] args) throws InterruptedException {
Person personA = new Person(0, "猎人A");
Person personB = new Person(1, "猎人B");
personA.start();
personB.start();
}
}
// 熊掌
class Bear {
}
// 鱼
class Fish {
}
// 人
class Person extends Thread {
// 保证资源只有一份
public static Bear bear = new Bear();
public static Fish fish = new Fish();
int choose;
String personName;
public Person (int choose, String personName) {
this.choose = choose;
this.personName = personName;
}
@Override
public void run() {
// 捕猎
try {
this.hunting();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 捕猎方法
private void hunting() throws InterruptedException {
if (choose == 0) {
synchronized (bear) {
System.out.println(personName + "想捕捉熊");
Thread.sleep(1000);
synchronized (fish) {
System.out.println(personName + "想捕捉鱼");
}
}
} else {
synchronized (fish) {
System.out.println(personName + "想捕捉鱼");
Thread.sleep(1000);
synchronized (bear) {
System.out.println(personName + "想捕捉熊");
}
}
}
}
}
执行结果:两个线程一直阻塞,都在等在对方释放锁,结果导致死锁。
猎人A想捕捉熊
猎人B想捕捉鱼
(2)解决死锁的方法:同步代码块中不要相互嵌套,即,不要相互嵌套锁。
// 死锁例子:鱼和熊不可兼得
public class MyThread{
public static void main(String[] args) throws InterruptedException {
Person personA = new Person(0, "猎人A");
Person personB = new Person(1, "猎人B");
personA.start();
personB.start();
}
}
// 熊掌
class Bear {
}
// 鱼
class Fish {
}
// 人
class Person extends Thread {
// 保证资源只有一份
public static Bear bear = new Bear();
public static Fish fish = new Fish();
int choose;
String personName;
public Person (int choose, String personName) {
this.choose = choose;
this.personName = personName;
}
@Override
public void run() {
// 捕猎
try {
this.hunting();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 捕猎方法
private void hunting() throws InterruptedException {
if (choose == 0) {
synchronized (bear) {
System.out.println(personName + "想捕捉熊");
Thread.sleep(1000);
}
// 把嵌套的代码块拿到外面,两个代码块并列
synchronized (fish) {
System.out.println(personName + "想捕捉鱼");
}
} else {
synchronized (fish) {
System.out.println(personName + "想捕捉鱼");
Thread.sleep(1000);
}
// 把嵌套的代码块拿到外面,两个代码块并列
synchronized (bear) {
System.out.println(personName + "想捕捉熊");
}
}
}
}
执行结果:两个线程即捕到了熊,有捕到了鱼,解决了死锁问题。
猎人B想捕捉鱼
猎人A想捕捉熊
猎人A想捕捉鱼
猎人B想捕捉熊
Lock 锁也称同步锁,java.util.concurrent.locks.Lock 机制提供了⽐ synchronized 代码块和 synchronized ⽅法更⼴泛的锁定操作,同步代码块 / 同步⽅法具有的功能 Lock 都有,除此之外更强⼤,更体现⾯向对象。
创建对象 Lock lock = new ReentrantLock() ,加锁与释放锁⽅法如下:
public void lock() :加同步锁
public void unlock() :释放同步锁
synchronized和Lock的对比:
- Lock是显式锁(手动开启和关闭锁,别忘记关闭),synchronized是隐式锁,除了作用域就自动释放。
- Lock只是代码块锁(执行体放在开启锁和关闭锁中间),synchronized有代码块锁和方法锁。
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)。
- 优先使用顺序:Lock > 同步代码块(已经进入了方法体,分配了相应资源) > 同步方法(在方法体之外)。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
// 测试 Lock锁
public class MyThread{
public static void main(String[] args) throws InterruptedException {
TestLock testLock = new TestLock();
new Thread(testLock,"a").start();
new Thread(testLock,"b").start();
new Thread(testLock,"c").start();
}
}
class TestLock implements Runnable {
// 车票
private static int tickerNum = 10;
// 创建一个Lock锁
private final Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock(); // 加锁
try {
// 判断是否还有票
if (tickerNum > 0) {
// 模拟延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 买票
System.out.println(Thread.currentThread().getName() + "线程买到第" + tickerNum -- + "张票");
} else {
break;
}
} finally {
lock.unlock(); // 解锁
}
}
}
}
a线程买到第10张票
a线程买到第9张票
a线程买到第8张票
a线程买到第7张票
a线程买到第6张票
a线程买到第5张票
b线程买到第4张票
b线程买到第3张票
b线程买到第2张票
b线程买到第1张票
应用场景:生产者和消费者问题
假如仓库中只能存放一件商品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费。
如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止。
如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止。
场景分析:这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件。
在生产者消费者问题中,没生产出产品之前,消费者是不能消费的,反之,消费者没消费完之前,生产者是不能生产的。这就需要锁来实现线程之间的同步。仅有同步还不行,还要实现线程之间的消息传递,即通信。
Java提供了解决线程之间通信问题的方法:
方法名 | 作用 |
---|---|
wait () | 表示线程一直等待,直到其他线程通知,与 sleep 不同会释放锁 |
wait (long timeOut) | 指定等待的毫秒数 |
notify () | 唤醒一个处于等待状态的线程 |
notifyAll() | 唤醒同一个对象所有的调用 wait()方法的线程,优先级高的优先调度 |
注意:均是Object的方法,均只能在同步方法或者同步代码块中使用,否则会抛出异常IIIegalMonitorStageException。
解决线程之间通信的方式:管程法和信号灯法。
管程法:生产者把生产好的数据放入缓存区,消费者从缓存区中拿出数据。
// 线程通信:生产消费模式-管程法
public class MyThread{
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Productor(container).start();
new Consumer(container).start();
}
}
// 产品
class Chicken {
int id;
public Chicken (int id) {
this.id = id;
}
}
// 生产者
class Productor extends Thread {
SynContainer synContainer;
public Productor(SynContainer synContainer) {
this.synContainer = synContainer;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
synContainer.pushTo(new Chicken(i));
}
}
}
// 消费者
class Consumer extends Thread {
SynContainer synContainer;
public Consumer(SynContainer synContainer) {
this.synContainer = synContainer;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
synContainer.popTo();
}
}
}
// 容器
class SynContainer {
// 定义一个大小为10的容器
Chicken[] chickens = new Chicken[10];
// 容器计数器
int count;
// 生产者生产产品方法
public synchronized void pushTo(Chicken chicken) {
// 如果容器满了,就停止生产
if (chickens.length == count) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 如果容器没满,就往容器中放入产品
chickens[count] = chicken;
System.out.println("生产了" + chicken.id + "个鸡腿");
count ++;
// 通知消费者消费
this.notifyAll();
}
// 消费者消费产品方法
public synchronized Chicken popTo() {
// 如果容器中没有产品了,就停止消费
if (count == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 如果容器有产品,就可以消费
count --;
Chicken chicken = chickens[count];
System.out.println("消费了第" + chicken.id + "个鸡退");
//只要消费了,就通知生产者生产
this.notifyAll();
return chicken;
}
}
生产了0个鸡腿
生产了1个鸡腿
生产了2个鸡腿
生产了3个鸡腿
生产了4个鸡腿
生产了5个鸡腿
生产了6个鸡腿
生产了7个鸡腿
生产了8个鸡腿
生产了9个鸡腿
消费了第9个鸡退
生产了10个鸡腿
消费了第10个鸡退
生产了11个鸡腿
消费了第11个鸡退
生产了12个鸡腿
消费了第12个鸡退
生产了13个鸡腿
消费了第13个鸡退
生产了14个鸡腿
消费了第14个鸡退
生产了15个鸡腿
消费了第15个鸡退
生产了16个鸡腿
消费了第16个鸡退
生产了17个鸡腿
消费了第17个鸡退
消费了第8个鸡退
消费了第7个鸡退
消费了第6个鸡退
消费了第5个鸡退
消费了第4个鸡退
消费了第3个鸡退
消费了第2个鸡退
消费了第1个鸡退
消费了第0个鸡退
生产了18个鸡腿
生产了19个鸡腿
消费了第19个鸡退
消费了第18个鸡退
信号灯法:设置一个标识位,标识位来判断线程是等待还是执行。
// 线程通信:生产消费模式-信号灯法
public class MyThread{
public static void main(String[] args) {
CCTV cctv = new CCTV();
new Player(cctv).start();
new Watcher(cctv).start();
}
}
// 演员
class Player extends Thread {
CCTV cctv;
public Player(CCTV cctv) {
this.cctv = cctv;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (i%2 == 0) {
cctv.play("快乐大本营");
} else {
cctv.play("天天向上");
}
}
}
}
// 观众
class Watcher extends Thread {
CCTV cctv;
public Watcher(CCTV cctv) {
this.cctv = cctv;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
cctv.watch();
}
}
}
// 电视
class CCTV {
// 表演的节目
String voice;
// 标识
boolean flag = true;
// 表演节目
public synchronized void play(String voice) {
if (!flag) {
try {
this.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
this.voice = voice;
System.out.println("演员表演了:" + voice);
// 通知观众观看
this.notifyAll();
this.flag = !flag;
}
// 观看节目
public synchronized void watch () {
if (flag) {
try {
this.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
// 如果容器有产品,就可以消费
System.out.println("观众观看了:" + voice);
// 通知演员表演节目
this.notifyAll();
this.flag = !flag;
}
}
演员表演了:快乐大本营
观众观看了:快乐大本营
演员表演了:天天向上
观众观看了:天天向上
演员表演了:快乐大本营
观众观看了:快乐大本营
演员表演了:天天向上
观众观看了:天天向上
演员表演了:快乐大本营
观众观看了:快乐大本营
演员表演了:天天向上
观众观看了:天天向上
演员表演了:快乐大本营
观众观看了:快乐大本营
演员表演了:天天向上
观众观看了:天天向上
演员表演了:快乐大本营
观众观看了:快乐大本营
演员表演了:天天向上
观众观看了:天天向上
背景:经常创建和销毁线程,消耗特别大的资源,比如并发的情况下的线程,对性能影响很大。线程池就是问题为了解决这个问题,提前创建好多个线程,放在线程池中,使用时直接获取,使用完放回线程池中,可以避免频繁的创建、销毁,实现重复利用。
优点:
线程池相关的API:
void execute(Runnable command):执行任务命令,没有返回值,一般用来执行Runnable.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 线程池
public class ThreadPool {
public static void main(String[] args) {
// 1、创建服务,创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
MyThread myThread = new MyThread();
// 执行
service.execute(myThread);
service.execute(myThread);
service.execute(myThread);
service.execute(myThread);
// 关闭连接
service.shutdown();
}
}
// 演员
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
pool-1-thread-2
pool-1-thread-4
pool-1-thread-3
pool-1-thread-1