一、线程简介
process进程:程序执行的过程,可以有多个线程。系统分配
thread线程:CPU调度和执行的单位。main函数:主线程,gc线程。独立的执行路径。
对同一份资源操作时,会存在资源抢夺问题,需要加入并发控制
线程会带来额外的开销。如cpu调度时间
二、线程的创建(重点)
1、继承Thread类(重点,实现Runnable接口),不建议使用:避免OOP单继承局限性
//继承Thread类,重写run() 方法,调用start() 方法开启线程
public class TestThread extends Thread{
//重写run方法
@Override
public void run() {
//线程体
for(int i=0; i<20; i++) {
System.out.println("我是子线程=====");
}
}
public static void main(String[] args) {
//调用子线程方法,由CPU调度
TestThread thread = new TestThread();
thread.start();
//主线程
for(int i=0; i<20; i++) {
System.out.println("我是主线程=====");
}
}
}
2、实现Runnable接口(重点),推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
package com.yans.java.basic;
//实现Runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,执行start方法
public class TestThread2 implements Runnable{
@Override
public void run() {
//线程体
for(int i=0; i<20; i++) {
System.out.println("我是子线程=====");
}
}
public static void main(String[] args) {
//创建runnable接口实现类对象
TestThread2 thread2 = new TestThread2();
//创建线程对象,通过线程对象开启线程
// Thread thread1 = new Thread(thread2);
// thread1.start();
//代理
new Thread(thread2).start();
//主线程
for(int i=0; i<20; i++) {
System.out.println("我是主线程=====");
}
}
}
3、实现Callable接口(了解)
- 实现Callable接口,需要返回值
- 重写call方法,需要抛出异常
- 创建目标对象
- 创建执行服务:ExecuteService ser = Executors.newFixedThreadPool(1);
- 提交执行:Future<Boolean> result1 = ser.submit(1);
- 获取结果:boolean r1 = result1.get()
- 关闭服务:ser.shutdownNow();
import java.util.concurrent.*;
//实现Callable接口
public class TestCallabe implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
//线程体
for(int i=0; i<20; i++) {
System.out.println("我是子线程=====");
}
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//声明对象
TestCallabe testCallabe =new TestCallabe();
//开启执行服务
ExecutorService ser = Executors.newFixedThreadPool(1);
//提交执行
Future<Boolean> result1 = ser.submit(testCallabe);
//获取结果
boolean r1 = result1.get();
//关闭服务
ser.shutdown();
//主线程
for(int i=0; i<20; i++) {
System.out.println("我是主线程=====");
}
}
}
三、静态代理
例子:
你:真实对象。实现结婚接口
婚庆公司:代理。实现结婚接口
结婚:结婚接口
//静态代理模式总结:
//真实对象和代理对象都需要实现同一个接口
//代理对象要代理真实角色
//好处:
//代理对象可以做很多真实对象做不了的事
//真实对象只需要专注做自己的事
public class StaticProxy {
public static void main(String[] args) {
// 对比线程实现
// new Thread(new Runnable() {
// @Override
// public void run() {
//
// }
// }).start();
WeddingCompany weddingCompany =new WeddingCompany(new You());
weddingCompany.marry();
}
}
//结婚
interface Marry {
void marry();
}
//真实对象--》你
//真实结婚
class You implements Marry{
@Override
public void marry() {
System.out.println("你要结婚了,超开心");
}
}
//代理对象--》婚庆公司
//帮助你结婚
class WeddingCompany implements Marry{
//获取代理真实对象
private Marry target;
//构造方法
public WeddingCompany(Marry target) {
this.target = target;
}
@Override
public void marry() {
before();
this.target.marry(); //真实对象
after();
}
private void after() {
System.out.println("结婚后,收尾款");
}
private void before() {
System.out.println("结婚前,布置场地");
}
}
三、Lamda表达式
函数式编程
(params) -> expression[表达式]
(params) -> statement[语句]
(params) -> { statement }
new Thread(() -> System.out.println("多线程")).start();
函数式接口:任何接口,如果只包含唯一一个抽象类方法,那么它就是一个函数式接口,如
public interface Runnable {
public abstract void run();
}
lamda表达式推导过程:
package com.yans.java.basic;
//lamda表达式推导
public class TestLamda {
//3、静态内部类
static class Like2 implements ILike{
@Override
public void like() {
System.out.println("I Like2");
}
}
public static void main(String[] args) {
//2、调用实现类
ILike like = new Like();
like.like();
//3、静态内部类
like = new Like2();
like.like();
//4、局部内部类
class Like3 implements ILike{
@Override
public void like() {
System.out.println("I Like3");
}
}
like = new Like3();
like.like();
//5、匿名内部类,没有名字,通过接口实现
like = new ILike() {
@Override
public void like() {
System.out.println("I Like4");
}
};
like.like();
//6、lamda简化
like = () -> {
System.out.println("I Like5");
};
like.like();
}
}
//1.定义一个接口
interface ILike {
void like();
}
//2、定义一个实现类
class Like implements ILike {
@Override
public void like() {
System.out.println("I Like");
}
}
lamda表达式简化过程:
package com.yans.java.basic;
//总结:
//lamda表达式只有一行代码的情况下可以省略{}
//前提是接口为函数式接口
//多个参数类型也可以去掉参数类型,去掉的话所有参数要都去掉,要加()
public class TestLamda2 {
public static void main(String[] args) {
ILove love;
love = (int a) -> {
System.out.println("I Love" + a);
};
love.love(1);
//简化1,省略参数类型
love = (a) -> {
System.out.println("I Love" + a);
};
love.love(2);
//简化2,省略括号
love = a -> {
System.out.println("I Love" + a);
};
love.love(3);
//简化3,省略{}括号
love = a -> System.out.println("I Love" + a);
love.love(4);
}
}
//定义一个接口
interface ILove {
void love(int a);
}
四、线程的状态
创建、就绪、运行、阻塞、死亡
五、线程方法
1、停止线程:
- 建议使用标志位
- 正常停止,利用次数,不建议死循环
- 不推荐使用JDK中提供的stop()、destroy()方法。
2、线程休眠:
- sleep(时间),指定当前线程阻塞的毫秒数。
- sleep存在异常InterruptedException
- sleep到达时间后进入就绪状态
- sleep可以模拟网络延时,倒计时
- 每个对象都有一个锁,sleep不会释放锁
3、线程礼让yield
让当前线程暂停但不阻塞
将线程从运行状态转为就绪状态
让CPU重写调度,但不一定成功,看CPU心情
package com.yans.java.basic;
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方法
合并线程,待此线程执行完后,其他线程才能执行
可以想象成插队
package com.yans.java.basic;
public class TestJoin 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 {
//启动线程
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
//主线程
for (int i =0; i<500; i++) {
if (i == 200){
//当主线程执行到200时,子线程插队
// 等子线程执行完后主线程才能继续执行
thread.join();
}
System.out.println("main" + i);
}
}
}
5、线程状态观测
Thread.State
package com.yans.java.basic;
public class TsetState {
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、线程优先级
setPriority(int newPriority) 更改线程优先级
getPriority() 获取线程优先级
优先级最小为1,最大为10,默认为5
package com.yans.java.basic;
public class TestPriority {
public static void main(String[] args) {
//主线程优先级
System.out.println(Thread.currentThread().getName() + "===>" + Thread.currentThread().getPriority());
MyPriorty myPriorty = new MyPriorty();
Thread t1 = new Thread(myPriorty);
Thread t2 = new Thread(myPriorty);
Thread t3 = new Thread(myPriorty);
Thread t4 = new Thread(myPriorty);
Thread t5 = new Thread(myPriorty);
Thread t6 = new Thread(myPriorty);
//设置优先级,不一定优先级高的就会先跑
t1.start();
t2.setPriority(1);
t2.start();
t3.setPriority(2);
t3.start();
t4.setPriority(10);
t4.start();
// //t5会报错
// t5.setPriority(-1);
// t5.start();
//
// //t6会报错
// t6.setPriority(11);
// t6.start();
t5.setPriority(7);
t5.start();
//t6会报错
t6.setPriority(8);
t6.start();
}
}
class MyPriorty implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "===>" + Thread.currentThread().getPriority());
}
}
输出结果:
main===>5
Thread-5===>8
Thread-1===>1
Thread-3===>10
Thread-4===>7
Thread-0===>5
Thread-2===>2
六、守护线程
线程分为用户线程和守护线程
虚拟机必须确保用户线程执行完毕
虚拟机不用等待守护线程执行完毕(gc线程),如:后台记录日志、监控日志等
package com.yans.java.basic;
//测试游湖线程
//上帝守护人类
public class TestDaemon {
public static void main(String[] args) {
God god = new God();
People people = new People();
Thread thread = new Thread(god);
thread.setDaemon(true); //默认是false,true表示是守护线程,正常的线程都是用户线程
thread.start();
new Thread(people).start(); //用户线程启动,用户线程执行完之后,守护线程还会运行一段时间
}
}
//守护线程 上帝
class God implements Runnable {
@Override
public void run() {
while (true) {
System.out.println("上帝守护着你");
}
}
}
//用户线程 人类
class People implements Runnable {
@Override
public void run() {
for (int i=0; i<36500; i++) {
System.out.println("人一生开心的活着");
}
System.out.println("goodbye world");
}
}
七、线程同步(重点)
并发:同一对象被多个线程同时操作
线程同步就是一种等待机制,让多个对象线程进入线程等待池队列
队列和锁:
锁机制(synchronized)(安全),当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可,存在以下问题:
- 一个线程持有锁会导致其他需要锁的线程挂起
- 在多个线程竞争下,加锁、释放锁会导致较多的上下文切换和调度延迟,引起性能问题
- 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级导致,引起性能问题
线程不安全例子:
package com.yans.java.basic;
//不安全买票
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(100);
System.out.println(Thread.currentThread().getName() +"抢到了" + ticketNums-- +"张票");
}
}
package com.yans.java.basic;
//不安全取钱
//两个人去银行取钱,账户
public class UnsafeDrawing {
public static void main(String[] args) {
Account account = new Account(100, "结婚基金");
Drawing you = new Drawing(account,"你", 50);
Drawing girlfried = new Drawing(account,"girlfriend", 100);
you.start();
girlfried.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, String name, int drawingMoney) {
super(name); //继承父类name
this.account = account;
this.drawingMoney = drawingMoney;
}
//取钱
@Override
public void run() {
//判断卡里是否有钱
if (account.money - drawingMoney < 0) {
System.out.println(this.getName() + "账户里的钱不够了");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//计算银行卡余额 = 卡里钱-取走钱
account.money = account.money - drawingMoney;
//手里的钱
nowMoney = nowMoney + drawingMoney;
//输出
System.out.println(account.name + "账户还剩" + account.money + "钱");
//Thread.currentThread().getName() ==> this.getName
System.out.println(this.getName() + "手里有" + nowMoney + "钱");
}
}
package com.yans.java.basic;
import java.util.ArrayList;
//不安全集合测试
public class TestArrayList {
public static void main(String[] args) throws InterruptedException {
ArrayList<String> list = new ArrayList<String>();
for (int i=0; i<1000; i++) {
new Thread(() -> {
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(1000);
System.out.println(list.size());
}
}
同步方法synchronized关键字修饰
例子,解决不安全的买票问题:
package com.yans.java.basic;
//不安全买票
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();
}
}
}
//加synchronized关键字修饰,加锁 ==》解决不安全问题
private synchronized void buy() throws InterruptedException {
//判断是否有票
if (ticketNums <= 0) {
flag = false;
return;
}
//模拟延时,方法问题的发现性
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() +"抢到了" + ticketNums-- +"张票");
}
}
同步块synchronized(Obj) { 代码块 }
例子,解决不安全的取钱问题:
package com.yans.java.basic;
//不安全取钱
//两个人去银行取钱,账户
public class UnsafeDrawing {
public static void main(String[] args) {
Account account = new Account(100, "结婚基金");
Drawing you = new Drawing(account,"你", 50);
Drawing girlfried = new Drawing(account,"girlfriend", 100);
you.start();
girlfried.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, String name, int drawingMoney) {
super(name); //继承父类name
this.account = account;
this.drawingMoney = drawingMoney;
}
//取钱
//synchronized默认锁的是this,加关键字锁的是Drawing类,但实际修改的是Account类
//使用synchronized块,将进行增删改的对象锁住
@Override
public synchronized void run() {
//锁的对象是进行增删改的对象
synchronized (account) {
//判断卡里是否有钱
if (account.money - drawingMoney < 0) {
System.out.println(this.getName() + "账户里的钱不够了");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//计算银行卡余额 = 卡里钱-取走钱
account.money = account.money - drawingMoney;
//手里的钱
nowMoney = nowMoney + drawingMoney;
//输出
System.out.println(account.name + "账户还剩" + account.money + "钱");
//Thread.currentThread().getName() ==> this.getName
System.out.println(this.getName() + "手里有" + nowMoney + "钱");
}
}
}
JUC并发线程安全:CopyOnWriteArrayList集合
package com.yans.java.basic;
import java.util.concurrent.CopyOnWriteArrayList;
public class TestJUC {
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
for (int i=0; i<1000; i++) {
new Thread(() -> {
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(1000);
System.out.println(list.size());
}
}
八、死锁
多线程在共享同一资源时,互相持有对方的锁,导致一致在等待锁。
四个必要条件:
1、互斥条件:一个资源每次只能被一个进程使用
2、请求与保持:一个进程因请求资源二阻塞时,对方已获得的资源保持不放
3、不剥夺:进程已获得的资源,在未使用完之前,不能被强行剥夺
4、循环等待:若干进程之间形成一种头尾相接的循环等待资源关系
只要破坏任一条件,死锁就不会产生。
列子,产生死锁:
package com.yans.java.basic;
public class TestDeathLock {
public static void main(String[] args) {
Makeup g1 = new Makeup(0, "灰姑娘");
Makeup g2 = new Makeup(1, "白雪公主");
new Thread(g1).start();
new Thread(g2).start();
}
}
//口红
class Lipstic {
}
//镜子
class Mirror {
}
class Makeup implements Runnable {
//static确保资源只有一份
static Lipstic lipstic = new Lipstic();
static Mirror mirror = new Mirror();
String name; //化妆的人
int choice; //选择 口红or镜子
//构造函数
public Makeup(int choice, String name) {
this.choice = choice;
this.name = name;
}
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//互相持有对方的锁,又需要对方的资源,形成僵持
private void makeup() throws InterruptedException {
if (choice == 0) {
//同时拥有两把锁
synchronized (lipstic) {
System.out.println(Thread.currentThread().getName() + "获得口红");
Thread.sleep(1000);
synchronized (mirror) {
System.out.println(Thread.currentThread().getName() + "获得镜子");
}
}
} else {
synchronized (mirror) {
System.out.println(Thread.currentThread().getName() + "获得镜子");
Thread.sleep(2000);
synchronized (lipstic) {
System.out.println(Thread.currentThread().getName() + "获得口红");
}
}
}
}
}
破坏死锁条件,不要同时拥有两把锁:
package com.yans.java.basic;
public class TestDeathLock {
public static void main(String[] args) {
Makeup g1 = new Makeup(0, "灰姑娘");
Makeup g2 = new Makeup(1, "白雪公主");
new Thread(g1).start();
new Thread(g2).start();
}
}
//口红
class Lipstic {
}
//镜子
class Mirror {
}
class Makeup implements Runnable {
//static确保资源只有一份
static Lipstic lipstic = new Lipstic();
static Mirror mirror = new Mirror();
String name; //化妆的人
int choice; //选择 口红or镜子
//构造函数
public Makeup(int choice, String name) {
this.choice = choice;
this.name = name;
}
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//互相持有对方的锁,又需要对方的资源,形成僵持
private void makeup() throws InterruptedException {
if (choice == 0) {
//只拥有一把锁
synchronized (lipstic) {
System.out.println(Thread.currentThread().getName() + "获得口红");
Thread.sleep(1000);
}
synchronized (mirror) {
System.out.println(Thread.currentThread().getName() + "获得镜子");
}
} else {
synchronized (mirror) {
System.out.println(Thread.currentThread().getName() + "获得镜子");
Thread.sleep(2000);
}
synchronized (lipstic) {
System.out.println(Thread.currentThread().getName() + "获得口红");
}
}
}
}
九、Lock锁
java.util.concurrent.Lock 接口
ReentrantLock 可重入锁,JVM花费调度时间更少,性能更快。
例子,解决不安全买票问题
package com.yans.java.basic;
import java.util.concurrent.locks.ReentrantLock;
//不安全买票
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; //停止标志
//定义锁
ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
//买票
while (flag) {
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//加synchronized关键字修饰,加锁 ==》解决不安全问题
private void buy() throws InterruptedException {
lock.lock(); //加锁
try {
//判断是否有票
if (ticketNums <= 0) {
flag = false;
return;
}
//模拟延时,方法问题的发现性
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() +"抢到了" + ticketNums-- +"张票");
} finally {
lock.unlock(); //解锁
}
}
}
十、线程协作(生产者消费者模式)
一个线程同步问题,生产者和消费者共享一个资源,互相依赖,互为条件
线程通信
wait() 等待
wait(long timeout) 指定等待毫秒数
notify() 唤醒等待的资源
notifyAll()
解决方法:
1、管程法
package com.yans.java.basic;
//测试生产者消费者模式--》管程法
//生产者、消费者、产品(鸡)、数据缓冲区
public class TestPC {
public static void main(String[] args) {
synContainer synContainer = new synContainer();
Producer producer = new Producer(synContainer);
Customer customer = new Customer(synContainer);
producer.start();
customer.start();
}
}
//生产者
class Producer extends Thread {
synContainer synContainer;
public Producer(synContainer synContainer) {
this.synContainer = synContainer;
}
@Override
public void run() {
for (int i=0; i<100; i++) {
synContainer.push(new Chicken(i));
}
}
}
//消费者
class Customer extends Thread {
synContainer synContainer;
public Customer(synContainer synContainer) {
this.synContainer = synContainer;
}
@Override
public void run() {
for (int i=0; i<100; i++) {
synContainer.pop();
}
}
}
//产品
class Chicken {
int id;
public Chicken(int id) {
this.id = id;
}
}
//数据缓冲区
class synContainer {
Chicken[] chickens = new Chicken[100];
int count = 0; //记录鸡的数量
//生产鸡
public synchronized Chicken push(Chicken chicken) {
//判断容器是否满了
if (count == chickens.length) {
try {
this.wait(); //等待消费者消费
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
chickens[count] = chicken; //把鸡放到容器里
count ++;
System.out.println("生产者生产了第" + count + "只鸡");
this.notifyAll(); //通知消费者消费
}
return chicken;
}
//消费者消费鸡
public synchronized Chicken pop() {
//判断是否有鸡
if (count == 0) {
try {
this.wait(); //没有鸡,需要等待生产者生产
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果还有,继续消费
System.out.println("消费者消费了第" + count + "只鸡");
count --;
Chicken chicken = chickens[count];
//消费完,通知生产者生产
this.notifyAll();
return chicken;
}
}
2、信号灯法
package com.yans.java.basic;
//测试生产者消费者模式==》信号灯法,标志位解决
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++) {
this.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;
}
}
十一、线程池
经常创建和销毁、使用量特别大的资源,如并发情况下的线程,对性能影响很大。
思路:提前创建好多个线程,放到线程池中,使用时直接取,用完放回池中。
JDK5.0后提供了线程池相关API:ExecutorServer和Executors。
ExecutorServer:真正的线程池接口,常见子类ThreadPoolExecutor:
- void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable。
- <T> Future<T> submit(Callable<T> task):执行任务,有返回值,一般用来执行Callable。
- void shutdown():关闭连接。
Executors工具类、线程池工厂类,用于创建并返回不同类型的线程池。
package com.yans.java.basic;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestPool {
public static void main(String[] args) {
//创建服务,创建线程池参数为线程池大小
ExecutorService ser = Executors.newFixedThreadPool(10);
//执行
ser.execute(new MyThread());
ser.execute(new MyThread());
ser.execute(new MyThread());
ser.execute(new MyThread());
ser.execute(new MyThread());
//关闭连接
ser.shutdown();
}
}
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
十二、总结
1、线程创建
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
2、同步
synchronized
Lock锁