一、创建线程的三种方法
1.1 继承Thread类
- 自定义线程类继承Thread类
- 重写**run() **方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
public class testThread1 extends Thread{
@Override
public void run(){
for(int i = 0; i < 20; i++){
System.out.println("我在看代码---" + i);
}
}
public static void main(String[] args) {
testThread1 testThread1 = new testThread1();
testThread1.start();
for(int i = 0; i < 200; i++){
System.out.println("我在学习多线程---" + i);
}
}
}
练习--下载图片
public class testThread2 extends Thread{
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://upload-images.jianshu.io/upload_images/25994682-8a5932b369e3cc36.png?imageMogr2/auto-orient/strip|imageView2/2/format/webp", "1.jpg");
testThread2 t2 = new testThread2("https://upload-images.jianshu.io/upload_images/25994682-a0c494eb408e4f66.png?imageMogr2/auto-orient/strip|imageView2/2/format/webp", "2.jpg");
testThread2 t3 = new testThread2("https://upload-images.jianshu.io/upload_images/25994682-4304e6bbc1c519fd.png?imageMogr2/auto-orient/strip|imageView2/2/format/webp", "3.jpg");
t1.start();
t2.start();
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方法出现问题");
}
}
}
1.2 实现Runnable接口
- 定义MyRunnable类实现Runnable接口
- 实现run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
//实现runnable接口、重写run方法、创建一个线程、执行线程需要丢入runnable接口实现类,调用start方法
public class TestTheard3 implements Runnable{
@Override
public void run() {
for(int i = 0; i < 20; i++){
System.out.println("我在run" + i);
}
}
public static void main(String[] args) {
TestTheard3 testTheard3 = new TestTheard3();
// Thread thread = new Thread(testTheard3);
// thread.start();
new Thread(testTheard3).start();
for(int i = 0; i < 100; i++){
System.out.println("我在主函数" + i);
}
}
}
1.3 小结
继承Thread类
- 子类继承Thread类具备多线程能力
- 启动线程: 子类.start()
- 不建议使用,避免OOP单继承局限性
实现Runnable接口
- 实现Runnable接口,重写run方法
- 启动线程: new Thread(子类).start()
- 推荐使用,避免OOP单继承局限性,灵活方便,一个对象(实现Runnable接口的)可以被多个线程使用
1.4 实现Callable接口
- 实现Callable接口,需要返回值类型
- 重写call方法,需要抛出异常
- 创建目标对象
- 创建执行服务: ExecutorService ser = Executors.newFixedThreadPool(1);
- 提交执行: Future<Boolean> result1 = ser.submit(t1);
- 获取结果: boolean r1 = result1.get();
- 关闭服务: ser.shutdownNow();
public class TestCallable implements Callable<Boolean> {
private String url;
private String name;
public TestCallable(String url, String name){
this.url = url;
this.name = name;
}
//下载图片线程的执行体
@Override
public Boolean call(){
webDownloader webDownloader = new webDownloader();
webDownloader.downloader(url, name);
System.out.println("下载了文件名为:" + name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable t1 = new TestCallable("https://upload-images.jianshu.io/upload_images/25994682-8a5932b369e3cc36.png?imageMogr2/auto-orient/strip|imageView2/2/format/webp", "1.jpg");
TestCallable t2 = new TestCallable("https://upload-images.jianshu.io/upload_images/25994682-a0c494eb408e4f66.png?imageMogr2/auto-orient/strip|imageView2/2/format/webp", "2.jpg");
TestCallable t3 = new TestCallable("https://upload-images.jianshu.io/upload_images/25994682-4304e6bbc1c519fd.png?imageMogr2/auto-orient/strip|imageView2/2/format/webp", "3.jpg");
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> result1 = ser.submit(t1);
Future<Boolean> result2 = ser.submit(t2);
Future<Boolean> result3 = ser.submit(t3);
//获取结果
boolean r1 = result1.get();
boolean r2 = result2.get();
boolean r3 = result3.get();
//关闭服务
ser.shutdownNow();
}
}
//下载器
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方法出现问题");
}
}
}
- Callable规定的方法是call(),而Runnable规定的方法是run().
- Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
- call()方法可抛出异常,而run()方法是不能抛出异常的。
- 运行Callable任务可拿到一个Future对象, Future表示异步计算的结果。
- 它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。
- 通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
- Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable的类都是可被其它线程执行的任务。
二、静态代理
静态代理模式:
真实对象和代理对象都实现同一个接口
代理对象要代理真实角色,所以代理对象中有一个接口的对象,用以接受真实对象
- 代理是为了不改变真实对象功能,给原对象以功能增强
- 真实对象可以专注做自己的事情
public class StacticProxy {
public static void main(String[] args) {
WeddingCompany weddingCompany = new WeddingCompany(new You());
weddingCompany.HappyMerry();
}
}
interface Merry{
void HappyMerry();
}
class You implements Merry{
@Override
public void HappyMerry() {
System.out.println("要结婚了");
}
}
class WeddingCompany implements Merry{
private Merry target;
public WeddingCompany(Merry target) {
this.target = target;
}
@Override
public void HappyMerry() {
before();
this.target.HappyMerry();
after();
}
private void after() {
System.out.println("结婚之后收尾款");
}
private void before() {
System.out.println("结婚之前布置现场");
}
}
可以用lambda表达式,通过线程来执行代理
public static void main(String[] args) {
new Thread( () -> System.out.println("我爱你")).start();
new WeddingCompany(new You()).HappyMerry();
}
三、Lambda表达式
- 是什么:实质属于函数式编程的概念
- 干什么:避免匿名内部类定义过多、简洁、留下核心逻辑
函数式接口
- 任何接口如果只包含一个抽象方法,那就是函数式接口
接口形式
- 实现一个函数式接口
- 实现类
- 静态内部类
- 把实现类放在方法里,局部内部类
- 匿名内部类,new 接口,要重写方法
- Lambda
- 类名 对象名 = (参数类型 参数名) ->{方法体};
- 类名 对象名 = (参数名) ->{方法体};
如果有参数 - 类名 对象名 = 参数名 ->{方法体};
如果方法体只有一行 - 类名 对象名 = 参数名 -> 方法体;
public class TestLambda1 {
//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();
//3.静态内部类
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 接口
//没有类的名称,必须借助接口或者父类
like = new ILike() {
@Override
public void lambda() {
System.out.println("I like Lambda4");
}
};
like.lambda();
//6.Lambda简化
like = ()->{
System.out.println("I like Lambda5");
};
}
}
//1. 定义一个函数式接口
interface ILike{
void lambda();
}
//2. 外部类实现接口
class Like implements ILike{
@Override
public void lambda() {
System.out.println("I like Lambda");
}
}
多线程的Runnable接口只有一个静态方法run(),用lambda表达式可以优化
四、线程状态
1. 线程停止 stop (自定义)
- 不推荐使用JDK自带的stop(), destroy()方法
- 推荐线程自己停下来
-
建议使用一个标志位进行终止变量
当flag = false,则终止线程运行
public class TestStop implements Runnable{
private boolean flag = true;
@Override
public void run() {
int i = 0;
while(flag){
System.out.println("run....Thread" + 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 < 1000; i++) {
System.out.println("main" + i);
if(i == 900){
testStop.stop();
System.out.println("线程该停止了");
}
}
}
}
2. 线程休眠 sleep
- sleep(时间)指定当前线程阻塞的毫秒数;
- sleep存在异常InterruptedException;
- sleep时间达到后线程进入就绪状态;
- sleep可以模拟网络延时,倒计时等;
- 每一个对象都有一个锁,sleep不会释放锁
public class TestSleep implements Runnable {
private int tickteNum = 10;
@Override
public void run() {
while (true){
if(tickteNum<=0){
break;
}else {
//模拟延时
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"得到了第 "+tickteNum--+" 票");
}
}
}
public static void main(String[] args) {
TestSleep testSleep = new TestSleep();
new Thread(testSleep,"小红").start();
new Thread(testSleep,"小明").start();
new Thread(testSleep,"小黄").start();
}
}
3. 礼让yield
- 礼让线程,让当前正在执行的线程暂停,但不阻塞
- 将线程从运行状态转为就绪状态
- 让CPU重新调度
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"小红").start();
new Thread(myYield,"小名").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("线程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){
//插队
thread.join();
}
System.out.println("main "+i);
}
}
}
- 其他线程进入阻塞状态,直到join的线程执行完毕
5.观测线程状态
public static void main(String[] args) throws InterruptedException {
//使线程睡眠5秒
Thread thread = new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
//观察状态
Thread.State state = thread.getState();
System.out.println(state);//NEW
//观察启动后
thread.start();
state = thread.getState();
System.out.println(state);//RUNNABLE
while (state!=Thread.State.TERMINATED){
state = thread.getState();
Thread.sleep(100);
System.out.println(state);//TERMINATED
}
//thread.start();
}
terminated后不能再start(),会返回IllegalThreadStateException
6.线程优先级
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(5);
t2.start();
t3.setPriority(1);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY);
t4.start();
t5.setPriority(8);
t5.start();
t6.setPriority(6);
t6.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
}
}
先设置优先级,再启动。
优先级低的只是意味着获得CPU调度的概率低,并不是优先级底就不会被调用了。
7.守护线程 (daemon)
- 线程分为用户现场和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
- 守护线程例子: 后台记录操作日志,监控内存,垃圾回收等等。
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();
Thread thread1 = new Thread(you);
thread1.start();
}
}
class God implements Runnable{
@Override
public void run() {
while (true) {
System.out.println("Still Alive");
}
}
}
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("活着");
}
System.out.println("=========死了==============");
}
}
五、线程同步
1、三大线程不安全案例
不安全的买票
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){
buy();
}
}
public 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,"理财基金");
TakeMoney you = new TakeMoney(account,50,"你");
TakeMoney girlFriend = new TakeMoney(account,100,"女朋友");
you.start();
girlFriend.start();
}
}
//账户
class Account{
int money;//余额
String name;//卡名
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
//模拟取钱
class TakeMoney extends Thread{
Account account;
int takeMoney;
int nowMoney;
public TakeMoney(Account account, int takeMoney, String name) {
super(name);
this.account = account;
this.takeMoney = takeMoney;
}
@Override
public void run() {
//判断有没有钱
if(account.money-this.takeMoney<0){
System.out.println("账户余额不足~,还剩 "+account.money);
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money = account.money - takeMoney;
nowMoney = nowMoney + takeMoney;
System.out.println(account.name+"-->"+"账户余额:"+account.money);
System.out.println(this.getName()+"-->"+"手里钱:"+nowMoney);
}
}
不安全的集合
public class UnSafeList {
public static void main(String[] args) throws InterruptedException {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
Thread.sleep(10);
System.out.println("list's size = "+list.size());
}
}
2、synchronize同步
- 一个线程持有锁会导致其他所有需要此锁的线程挂起;
- 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;
- 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题
由于我们可以通过 private 关键字来保证数据对象只能被方法访问,所以我们只需针对方法提出一套机制,这套机制就是 synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块。
1. synchronized 方法:通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。如:
public synchronized void accessVal(int newVal);
synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。
在 Java 中,不光是类实例,每一个类也对应一把锁,这样我们也可将类的静态成员函数声明为 synchronized ,以控制其对类的静态成员变量的访问。
synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率,典型地,若将线程类的方法 run() 声明为 synchronized ,由于在线程的整个生命期内它一直在运行,因此将导致它对本类任何 synchronized 方法的调用都永远不会成功。当然我们可以通过将访问类成员变量的代码放到专门的方法中,将其声明为 synchronized ,并在主方法中调用来解决这一问题,但是 Java 为我们提供了更好的解决办法,那就是 synchronized 块。
2. synchronized 块:通过 synchronized关键字来声明synchronized 块。语法如下:
synchronized(syncObject) {
//允许访问控制的代码
}
synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。
解决三个线程不安全问题
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 {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
buy();
}
}
//synchronized,同步方法,锁的是this
public synchronized void buy(){
if (ticketNums<=0){
flag = false;
return;
}
System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
}
}
public class SafeBank {
public static void main(String[] args) {
Account account = new Account(1000,"理财基金");
TakeMoney you = new TakeMoney(account,50,"你");
TakeMoney girlFriend = new TakeMoney(account,100,"女朋友");
you.start();
girlFriend.start();
}
}
//账户
class Account{
int money;//余额
String name;//卡名
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
//模拟取钱
class TakeMoney extends Thread{
Account account;
int takeMoney;
int nowMoney;
public TakeMoney(Account account, int takeMoney, String name) {
super(name);
this.account = account;
this.takeMoney = takeMoney;
}
@Override
public void run() {
//锁住account,因为account在增加减少【锁住变化的量】
synchronized(account){
//判断有没有钱
if(account.money-this.takeMoney<0){
System.out.println("账户余额不足~,还剩 "+account.money);
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money = account.money - takeMoney;
nowMoney = nowMoney + takeMoney;
System.out.println(account.name+"-->"+"账户余额:"+account.money);
System.out.println(this.getName()+"-->"+"手里钱:"+nowMoney);
}
}
}
public class UnSafeList {
public static void main(String[] args) throws InterruptedException {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
synchronized (list){
list.add(Thread.currentThread().getName());
}
}).start();
}
Thread.sleep(10);
System.out.println("list's size = "+list.size());
}
}
锁的对象是会产生变化的量
CopyOnWriteArrayList 同步List
public static void main(String[] args) {
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList();
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 DeadLock {
public static void main(String[] args) {
MakeUp girl1 = new MakeUp(0, "灰姑娘");
MakeUp girl2 = new MakeUp(1, "白姑娘");
girl1.start();
girl2.start();
}
}
class Lipstick{
}
class Mirror{
}
class MakeUp extends Thread{
static Mirror mirror = new Mirror();
static Lipstick lipstick = new Lipstick();
int choice;
String name;
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 (lipstick){
System.out.println(this.name + "获得口红");
Thread.sleep(1000);
// synchronized (mirror){
// System.out.println(this.name + "获得镜子");
// }
}
synchronized (mirror){
System.out.println(this.name + "获得镜子");
}
}else{
synchronized (mirror){
System.out.println(this.name + "获得镜子");
Thread.sleep(2000);
// synchronized (lipstick){
// System.out.println(this.name + "获得口红");
// }
}
synchronized (lipstick){
System.out.println(this.name + "获得口红");
}
}
}
}
产生死锁的必要条件
七、Lock 锁
- ReentrantLock 可重入锁
public class TestLock {
public static void main(String[] args) {
Lock2 lock2 = new Lock2();
new Thread(lock2).start();
new Thread(lock2).start();
}
}
class Lock2 implements Runnable{
private int ticketNums = 10;
private final ReentrantLock reentrantLock = new ReentrantLock();
@Override
public void run() {
while (true){
try{
//加锁
reentrantLock.lock();
if(ticketNums<=0){
break;
}else{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticketNums--);
}
}
finally {
//解锁
reentrantLock.unlock();
}
}
}
}
- Lock是显式锁,需要手动关闭锁,synchronized是隐式锁,出作用域自动释放
- Lock只能锁代码块,synchronized能锁代码块和方法
- Lock锁,JVM花费更少的时间来调度线程,性能更好,有更好的扩展性
- 优先使用顺序 Lock > 同步代码块 > 同步方法
八、线程协作
1. 生产者消费者模式
-
不是设计模式,是一种问题
2.管程法
public class TestPC {
public static void main(String[] args) {
KeepSpace keepSpace = new KeepSpace();
new Thread(new Producer(keepSpace)).start();
new Thread(new Customer(keepSpace)).start();
}
}
class Food{
int id;
public Food(int id) {
this.id = id;
}
}
class Producer implements Runnable{
KeepSpace keepSpace;
public Producer(KeepSpace keepSpace) {
this.keepSpace = keepSpace;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
keepSpace.push(new Food(i));
System.out.println("生产了第"+i+"个食物");
}
}
}
class Customer implements Runnable{
KeepSpace keepSpace;
public Customer(KeepSpace keepSpace) {
this.keepSpace = keepSpace;
}
@Override
public void run() {
//消费者消费
for (int i = 0; i < 100; i++) {
System.out.println("消费了第"+keepSpace.pop().id+"个食物");
}
}
}
class KeepSpace{
//空间大小
Food[] foods = new Food[10];
//空间计数器
int count = 0;
public synchronized void push(Food food){
//空间满了,等待消费者消费
if (count==foods.length){
//通知消费者消费
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//空间没有满,继续制作
foods[count] = food;
count++;
//有吃的,可以通知消费者消费了
this.notifyAll();
}
//消费者消费
public synchronized Food pop(){
//判断能否消费
if (count==0){
//等待生产者生产
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
Food food = foods[count];
//吃完了,通知生产者生产
this.notifyAll();
return food;
}
}
3.信号灯法
利用标志位来进行控制
//信号灯法,标志位解决 length为1的管程法
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
new Acter(tv).start();
new audience(tv).start();
}
}
//生产者 演员
class Acter extends Thread{
TV tv;
public Acter(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("DC");
}
}
}
}
//消费者 观众
class audience extends Thread{
TV tv;
public audience(TV tv) {
this.tv = tv;
}
@Override
public void run(){
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
//产品 节目
class TV{
String show;
boolean flag = true;
//表演
public synchronized void play(String show){
if(!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了:" + show);
//通知观众观看
this.notifyAll(); // 通知唤醒
this.show = show;
this.flag = !this.flag;
}
//观看
public synchronized void watch(){
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("观众观看:"+ show);
//通知演员表演
this.notifyAll();
this.flag = !this.flag;
}
}
八、线程池
- JDK5.0起 线程池相关API : ExecutorService 和 Executors
public class TestPoll {
public static void main(String[] args) {
//1.创建服务,创建线程池
//newFixedThreadPool(线程池大小)
ExecutorService service = Executors.newFixedThreadPool(10);
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() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
}