java多线程
1.线程的创建的三种方式
-
继承Thread类
- 自定义线程类继承Thread类
- 重写run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
public class TestThread extends Thread { @Override public void run() { //run方法为线程体 for (int i = 0; i < 20; i++) { System.out.println("我在写代码---"+i); } } public static void main(String[] args) { TestThread thread1=new TestThread(); thread1.start(); for (int i = 0; i < 20; i++) { System.out.println("主线程我在写代码--"+i); } } }
案例下载图片:
//练习Thread ,实现多线程同步下载图片 public class TestThread2 extends Thread{ private String name; private String url; public TestThread2(String url,String name){//注意构造方法没有返回值类型 this.name=name; this.url=url; } //2.下载图片的线程执行体 @Override public void run() { DownloaderWeb downloaderWeb=new DownloaderWeb(); downloaderWeb.downloader(url,name); System.out.println("下载图片----》"+name); } //3.创建线程 https://q4.qlogo.cn/g?b=qq&nk=1518574228@qq.com&s=3?d=retro public static void main(String[] args) { TestThread2 thread1=new TestThread2("https://q4.qlogo.cn/g?b=qq&nk=1518574228@qq.com&s=3?d=retro","1.jpg"); TestThread2 thread2=new TestThread2("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1594486623886&di=d2a2cb3cdcd65080e0ab784b4e2d49fd&imgtype=0&src=http%3A%2F%2Ft8.baidu.com%2Fit%2Fu%3D3571592872%2C3353494284%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D1200%26h%3D1290","2.jpg"); thread1.start(); thread2.start(); } } //1.下载器 class DownloaderWeb{ public void downloader(String url,String name){ try { FileUtils.copyURLToFile(new URL(url),new File(name));//文件工具类 } catch (IOException e) { e.printStackTrace(); System.out.println("下载器发生异常"); } } }
-
实现Runnable接口
- 自定义线程类实现Runnable接口
- 实现run()方法,编写线程执行体
- 创建自定义线程类对象,并在创建Thread时作为参数传入,然后调用start()方法启动
推荐使用第二种方法,因为可以用多个线程跑一个对象
public class TestThreadRun implements Runnable { @Override public void run() { for (int i = 0; i < 20; i++) { System.out.println("我是子线程----》"+i); } } public static void main(String[] args) { TestThreadRun thread1=new TestThreadRun(); new Thread(thread1).start(); for (int i = 0; i < 20; i++) { System.out.println("主线程输出-----》"+i); } } }
多个线程操作一个对象的例子:
买票:
public class TestThread implements Runnable { private int num=10; @Override public void run() { while(true)//只要票没取完,就一直取票 { if(num<=0){ break; } try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"线程取到第"+(num--)+"票"); } } public static void main(String[] args) { TestThread ticket =new TestThread(); new Thread(ticket,"小明").start(); new Thread(ticket,"老师").start(); new Thread(ticket,"黄牛党").start(); } } 黄牛党线程取到第10票 老师线程取到第9票 小明线程取到第8票 黄牛党线程取到第6票 小明线程取到第7票 老师线程取到第5票 小明线程取到第4票 老师线程取到第3票 黄牛党线程取到第3票 黄牛党线程取到第2票 小明线程取到第1票 老师线程取到第2票 进程完成,退出码 0
**发现问题:**老师和黄牛党同时取到第三张票。多个线程操作同一个资源的情况下,线程不安全,数据紊乱。
案例:龟兔赛跑
- 首先来个赛道距离
- 判断比赛是否结束,即是否有胜利者
- 打印胜利者
- 同时要模拟兔子睡觉
package com.javademo.day7_13; public class Race implements Runnable { //胜利者,用static保证胜利者唯一 private static String winner; @Override public void run() { for (int i = 0; i <=100; i++) {//赛道距离等于100 两个线程都执行各自的for循环,并发执行 上一个例子三个线程共享同一变量num //模拟兔子睡觉 if(Thread.currentThread().getName().equals("兔子-->") && i%10==0) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } boolean flag=gameOver(i); if(flag) break; System.out.println(Thread.currentThread().getName()+"跑了"+i+"步"); } } private boolean gameOver(int steps){//要判断是否已经有胜利者 if(winner==null){ if(steps>=100) { winner=Thread.currentThread().getName(); System.out.println(Thread.currentThread().getName()+"跑了"+steps+"步"); System.out.println("winner is "+winner); return true; } return false; } else{ return true; } } public static void main(String[] args) { Race race=new Race(); new Thread(race,"兔子-->").start(); new Thread(race,"乌龟").start(); } }
-
实现Callable接口(了解即可)
-
实现Callable接口,确定call方法的返回值类型
-
重写call方法
-
创建目标对象,也就是Callable的接口实现类对象
-
创建执行服务(线程池)
-
提交执行(submit 提交线程)
-
获取结果
-
关闭服务
package com.javademo.day7_14; import com.javademo.day7_11.TestThread2; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.concurrent.*; public class TestCallable implements Callable<Boolean> {//泛型确定call的返回值类型 private String name; private String url; public TestCallable(String url,String name){ this.name=name; this.url=url; } @Override public Boolean call() { DownloaderWeb downloaderWeb=new DownloaderWeb(); downloaderWeb.downloader(url,name); System.out.println("下载图片----》"+name); return true; } public static void main(String[] args) throws ExecutionException, InterruptedException { TestCallable thread1=new TestCallable("https://q4.qlogo.cn/g?b=qq&nk=1518574228@qq.com&s=3?d=retro","3.jpg"); TestCallable thread2=new TestCallable("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1594486623886&di=d2a2cb3cdcd65080e0ab784b4e2d49fd&imgtype=0&src=http%3A%2F%2Ft8.baidu.com%2Fit%2Fu%3D3571592872%2C3353494284%26fm%3D79%26app%3D86%26f%3DJPEG%3Fw%3D1200%26h%3D1290","4.jpg"); //创建执行服务 ExecutorService ser= Executors.newFixedThreadPool(2);//创建两个线程 //提交执行 线程运行 Future<Boolean> r1=ser.submit(thread1); Future<Boolean> r2=ser.submit(thread2); //获取结果,执行结果,此处始终返回true boolean rs1=r1.get(); boolean rs2=r2.get(); //关闭服务 ser.shutdown(); } } //1.下载器 class DownloaderWeb{ public void downloader(String url,String name){ try { FileUtils.copyURLToFile(new URL(url),new File(name));//文件工具类 } catch (IOException e) { e.printStackTrace(); System.out.println("下载器发生异常"); } } }
-
2.静态代理
真实对象和代理对象实现同一个接口,代理对象代理一个真实对象,可以通过代理对象的构造函数传入。
例子:chrome浏览器收藏夹
//创建线程 采用的是静态代理 因为Thread也实现了Runnable接口
new Thread(()->System.out.println("我爱你")).start();//lamda表达式
//对比如下
new Cinema(new RealMovie).play();
3.lamda表达式
- 函数式接口:
- 任何接口如果它只包含一个抽象方法,那么它就是一个函数式接口
- 对于函数式接口,我们可以通过Lamda表达式来创建该接口的对象
package com.javademo.day7_14;
public class TestLambda1 {
//1.匿名内部类
public static void main(String[] args) {
Animal a =new Animal() {
@Override
public void sound() {
System.out.println("我是匿名内部类的sound");
}
};
a.sound();
//2.通过lambda表达式继续简化
a=()->{
System.out.println("Lambda的sound");
};
a.sound();
}
}
interface Animal{
void sound();
}
带参数的lambda表达式
package com.javademo.day7_14;
public class TestLambda {
public static void main(String[] args) {
//匿名内部类
Like like1=null;
like1=new Like() {
@Override
public void like(int a, int b) {
System.out.println("匿名类---》"+a+"另一个参数--》"+b);
}
};
like1.like(1,2);
//Lambda表达式,可以简化去掉参数的类型,当语句块中只用一个语句的时候,大括号也可以省略
like1=(a,b)->{
System.out.println("Lambda参数1:"+a);
System.out.println("Lambda参数2:"+b);
};
like1.like(4,5);
}
}
interface Like{
void like(int a,int b);
}
4.线程停止
- 状态:创建状态,就绪状态,阻塞状态,运行状态,死亡状态
- 停止线程:一般使用一个标志位进行终止变量,当flag=false时终止线程运行
public class TestThreadStop implements Runnable {
//1.设置一个标志位
private boolean flag=true;
@Override
public void run() {
int i=0;
while(flag)
System.out.println("Run---------->"+(i++));
}
//2.设置一个公开的方法停止线程,转换标志位
public void stop(){
this.flag=false;
}
public static void main(String[] args) {
TestThreadStop testThreadStop=new TestThreadStop();
new Thread(testThreadStop).start();
for (int i = 0; i < 20; i++) {
System.out.println("main--->"+i);
if(i==18){
//调用stop方法切换标志位,让线程停止
testThreadStop.stop();
System.out.println("执行stop线程停止");
}
}
}
}
5.线程休眠(Thread的静态方法)
-
用sleep模拟倒计时
public class Down { public static void main(String[] args) { try { Down.timeDown(); } catch (InterruptedException e) { e.printStackTrace(); } ; } public static void timeDown() throws InterruptedException { int num=10; while(true){ Thread.sleep(1000);//1000ms等于1s System.out.println(num--); if(num<0){ break; } } } }
- sleep指定当前线程阻塞的毫秒数
- sleep存在异常InterruptedException
- sleep时间达到后就进入就绪状态
- sleep可以模拟网络延迟和倒计时
- 每个对象都有一个锁,sleep不会释放锁
import java.text.SimpleDateFormat;
import java.util.Date;
public class SystemTime {
public static void main(String[] args) throws InterruptedException {
Date date=new Date(System.currentTimeMillis());//获取当前系统时间
while (true){
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));//按格式打印当前系统时间
date=new Date(System.currentTimeMillis());//更新时间
}
}
}
6.线程礼让 yield(Thread的静态方法)
- 礼让线程,让当前执行的线程暂停,转入就绪状态。
- 让cpu重新调度,礼让不一定成功,看CPU心情,因为,礼让之后,线程又同时竞争cpu,如果还是原来的线程占有cpu则没有礼让成功。
public class YeildTest {
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()+"线程停止执行");
}
}
a线程开始执行
b线程开始执行
a线程停止执行
b线程停止执行
7.线程强制执行join
可想象为插队,注意插队之后必须待此线程执行完,才能执行其他线程。
public class TestJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("join插队,vip线程,一旦占用cup就执行完------->"+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 < 20; i++) {
System.out.println("main线程"+i);
if(i==10){
System.out.println("vip线程强制占用cpu");
thread.join();
}
}
}
}
8.线程状态观测
public class TestState {
/*
public static enum Thread.State
extends Enum<Thread.State>线程状态。 线程可以处于以下状态之一:
NEW
尚未启动的线程处于此状态。
RUNNABLE
在Java虚拟机中执行的线程处于此状态。
BLOCKED
被阻塞等待监视器锁定的线程处于此状态。
WAITING
正在等待另一个线程执行特定动作的线程处于此状态。
TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
TERMINATED
已退出的线程处于此状态。
一个线程可以在给定时间点处于一个状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。
*/
//通过Lambda表达式来创建线程,因为线程的自定义实现类和Thread都实现Runable接口,因此Thread相当于线程的自定义实现类的代理,
// 可以通过lambda表达式,创建类
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("for循环结束");
});
//观察状态
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){
Thread.sleep(1000);
state=thread.getState();
System.out.println(state);
}
}
}
NEW
RUNNABLE
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
for循环结束
TERMINATED
9. 线程优先级
public class TesPriority implements Runnable {
public static void main(String[] args) {
//主线程的默认优先级
System.out.println(Thread.currentThread().getName()+"线程优先级"+Thread.currentThread().getPriority());
TesPriority tesPriority=new TesPriority();
Thread t1=new Thread(tesPriority);
Thread t2=new Thread(tesPriority);
Thread t3=new Thread(tesPriority);
Thread t4=new Thread(tesPriority);
//先设置优先级,在启动
t1.setPriority(3);
t1.start();
t2.setPriority(8);
t2.start();
t3.setPriority(1);
t3.start();
t4.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY=10 可以看源码
t4.start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程优先级"+Thread.currentThread().getPriority());
}
}
main线程优先级5
Thread-3线程优先级10
Thread-1线程优先级8
Thread-0线程优先级3
Thread-2线程优先级1
10.守护(daemon)线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护进程执行完毕
- 如:后台记录操作日志,监控内存,垃圾回收
package com.javademo.day7_15;
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(); //上帝守护线程开始
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 < 5; i++) {
System.out.println("------------------你活了第"+(i+1)+"天-------------------");
}
System.out.println("======bye world========");
}
}
11.线程同步
-
并发:多个线程访问同一个资源
-
线程同步其实是一个排队机制,
-
队列加锁才能实现线程同步的安全性。
-
synchronized
12.三大不安全案例
- 买票
package com.javademo.day7_16;
//不同的人买到相同的票数,线程不安全
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 num=10;
private boolean flag=true;
@Override
public void run() {
while(flag){
buy();
}
}
private void buy(){
if(num<=0){
flag=false;
return;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"线程买到"+num--);
}
}
- 银行取钱
public class UnsafeBank1 {
public static void main(String[] args) {
Account1 account=new Account1(100,"结婚基金");
Thread getBankMoney1=new GetBankMoney1(50,account,"You");
Thread getBankMoney2=new GetBankMoney1(100,account,"Your GirlFriend");
getBankMoney1.start();
getBankMoney2.start();
}
}
class Account1{
public Account1(int nowMoney,String accountName){
this.accountName=accountName;
this.nowMoney=nowMoney;
}
int nowMoney;
String accountName;
}
class GetBankMoney1 extends Thread {
int getMoney;
Account1 account;
public GetBankMoney1(int getMoney, Account1 account,String name) {//不是操作同一对象因为取钱数不同,
// 只不过是操作同一账户,所以给出Account类操作同一个account对象,而不是同一个线程实现类对象
super(name);//传入线程名称
this.account = account;
this.getMoney = getMoney;
}
@Override
public void run() {
if (account.nowMoney - getMoney < 0) {
System.out.println(Thread.currentThread().getName() + "余额不足取不了");
return;
}
//sleep可以放大问题的发生性,以便模拟不安全的情况,即同时取钱
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.nowMoney = account.nowMoney - getMoney;
System.out.println(account.accountName + "余额:" + account.nowMoney);
System.out.println(Thread.currentThread().getName() + "手里的钱增加" + getMoney);
}
}
结婚基金余额:50
You手里的钱增加50
结婚基金余额:-50
Your GirlFriend手里的钱增加100
问题:余额50,依旧取出100
- ArrayList不安全
public class UnsafeArr {
public static void main(String[] args) {
ArrayList<String > arr=new ArrayList<>();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
arr.add(Thread.currentThread().getName());
}).start();
}
System.out.println(arr.size());
}
}
767
分析:两个线程同一时刻操作同意位置,那么后一个就会覆盖前一个,所以最后size大小不够1000
13.同步方法和同步块
-
同步方法:
**由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要对方法提出一套机制即可,这套机制就是synchronized关键字,**它包括两种用法:synchronzied方法和synchronized块。
synchronized控制对"对象"的访问,每个对象有一把锁,每个synchronized方法都必须获得调用该方法的对象的锁后,才能执行,否则线程就会阻塞,方法一旦执行就独占该锁,直到方法返回后才释放锁,后面被阻塞的线程才能获得这个锁,继续执行。缺陷:若一个大的方法申明为synchronized将会影响效率
修改不安全的案例使其安全:
-
买票:buy方法返回值类型前加synchronized
//synchronized锁的是this 对象也就是 BuyTicket对象 private synchronized void buy(){ if(num<=0){ flag=false; return; } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"线程买到"+num--); }
2.取钱,通过synchronized块使其变得线程安全
-
同步块:synchronized(Obj){}
Obj称之为同步监视器
- Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
- 同步方法中无需指定同步监视器,因为同步方法的监视器就是this,就是这个对象本身。
public class UnsafeBank1 {
public static void main(String[] args) {
Account1 account=new Account1(1000,"结婚基金");
Thread getBankMoney1=new GetBankMoney1(50,account,"You");
Thread getBankMoney2=new GetBankMoney1(100,account,"Your GirlFriend");
getBankMoney1.start();
getBankMoney2.start();
}
}
class Account1{
public Account1(int nowMoney,String accountName){
this.accountName=accountName;
this.nowMoney=nowMoney;
}
int nowMoney;
String accountName;
}
class GetBankMoney1 extends Thread {
int getMoney;
Account1 account;
public GetBankMoney1(int getMoney, Account1 account, String name) {
//不是操作同一GetBankMoney1对象因为取钱数不同,
// 只不过是操作同一账户,所以main方法操作同一个account对象,而不是同一个线程实现类对象
super(name);//传入线程名称
this.account = account;
this.getMoney = getMoney;
}
//注意我们要锁的一个是account对象,而不是run,因为创建的是两个GetBankMoney1对象,account才是临界资源
@Override
public void run() {
while (true) {
//sleep可以放大问题的发生性,睡眠以便有机会使其他进程进行取钱
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (account) {
if (account.nowMoney - getMoney < 0) {
System.out.println(Thread.currentThread().getName() + "余额不足取不了");
break;
}
account.nowMoney = account.nowMoney - getMoney;
System.out.println(account.accountName + "余额:" + account.nowMoney);
System.out.println(Thread.currentThread().getName() + "手里的钱增加--------->" + getMoney);
}
}
}
}
14.CopyOnWriteArrayList
线程安全的ArrayList
15.死锁
public class TestDeadLock {
public static void main(String[] args) {
Makeup g1=new Makeup(0,"灰姑凉");
Makeup g2=new Makeup(1,"白雪公主");
g1.start();
g2.start();
}
}
class Lipstick{
}
class Mirror{
}
class Makeup extends Thread{
//需要的资源只有一份,用static来保证只有一份
static Lipstick lipstick=new Lipstick();
static Mirror mirror=new Mirror();
int id;
String name;
public Makeup(int id,String name){
this.id=id;
this.name=name;
}
@Override
public void run() {
super.run();
makeup();
}
private void makeup(){
if(id==0){
synchronized (mirror) {//0号先取得镜子的资源
System.out.println(this.name + "获得了镜子");
synchronized (lipstick){
System.out.println(this.name+"获得口红资源");
}
}
}
else{
synchronized (lipstick) {//1号先取得口红的资源
System.out.println(this.name + "获得口红资源");
synchronized (mirror){
System.out.println(this.name+"获得了镜子");
}
}
}
}
如果按如下修改run,则不会发生死锁
private void makeup(){
if(id==0){
synchronized (mirror) {//0号先取得镜子的资源
System.out.println(this.name + "获得了镜子");
}
synchronized (lipstick){
System.out.println(this.name+"获得口红资源");
}
}
else{
synchronized (lipstick) {//1号先取得口红的资源
System.out.println(this.name + "获得口红资源");
}
synchronized (mirror){
System.out.println(this.name+"获得了镜子");
}
}
死锁的四个必要提交(逆否命题,缺一就不会发生死锁)
- 互斥条件
- 请求保持
- 不可剥夺
- 环路等待
16.Lock锁
Lock是接口,通过ReentrantLock可重入锁,来显式的加锁和释放锁。
格式:
class A{
private final ReenTrantLock lock=new ReenTrantLock();
public void m(){
lock.lock();
try{
//保证线程安全的代码
}
finally{
lock.unlock();
//如果同步代码有异常,需要把unlock()写在finally语句块中
}
}
}
synchronized和Lock对比
- lock是显式锁,synchronized是隐式锁,lock需要手动的开启和关闭锁,synchronized是隐式锁,出了作用域自动释放。
- Lock只有代码块锁,synchronized既有代码块锁又有方法锁。
17.生产者消费者问题
此前其实是线程互斥,此处是线程同步,即线程之间要相互协作,或者是直接制约。
-
wait():表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁。
-
notify():唤醒一个处于等待状态的线程
-
notifyAll():唤醒同一个对象上调用wait()方法的线程,优先级别高的线程优先调度
以上均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出IllegalMonitorStateException
18.管程法解决生产者消费者问题
package com.javademo.day7_18;
import java.util.concurrent.locks.ReentrantLock;
//管程法
public class TestPC {
public static void main(String[] args) throws InterruptedException {
//生产者和消费者要使用同一个缓冲区,因此建立一个缓冲区,然后将其传入消费者和生产者中
BufferPC buffer=new BufferPC();
Consumer consumer=new Consumer(buffer);
Productor productor=new Productor(buffer);
productor.start();
consumer.start();
}
}
//消费者
class Consumer extends Thread{
BufferPC bufferPC;
public Consumer(BufferPC bufferPC){
this.bufferPC=bufferPC;
}
@Override
public void run() {
for (int i = 0; i < 16; i++) {
bufferPC.pop();
}
}
}
//生产者
class Productor extends Thread{
BufferPC bufferPC;
public Productor(BufferPC bufferPC){
this.bufferPC=bufferPC;
}
@Override
public void run() {
for (int i = 0; i < 16; i++) {
bufferPC.push();
}
}
}
//生产和消费的物品
class Good{
int post;
public Good(int post) {
this.post = post;
}
}
//缓冲区 就是管程
class BufferPC{
private Good[] goods=new Good[10];
private int count=0;
private int copyCount;
//生产者生产
public synchronized void push(){
if(count==goods.length)//缓冲区满,则让生产者等待,否则生产者生产。
{
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
goods[count]=new Good(count);
System.out.println("在位置"+count+"生产");
//通知消费
this.notifyAll();
count++;
}
//消费者消费
public synchronized void pop(){
if(count==0){
//没有产品,消费者停止消费
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
System.out.println("消费位置"+count+"上的物品");
//通知生产
this.notifyAll();
}
}
19.线程池
使用线程池:
-
ExecutorService:真正的线程池接口,常见的子类,ThreadPoolExecutor
-
void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
-
Future subimit (Callable task):执行任务,有返回值,一般用来执行
Callable
-
void shutdown():关闭线程池
public class TestThreadPool { public static void main(String[] args) { //1.创建线程池,参数为线程池的大小 ExecutorService service=Executors.newFixedThreadPool(2); //2.执行 service.execute(new my()); service.execute(new my()); service.execute(new my()); service.execute(new my()); //3.关闭服务 service.shutdown(); } } class my implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()); } } pool-1-thread-1 pool-1-thread-2 pool-1-thread-1 pool-1-thread-2