概述
进程
在计算机中,把一个任务称为一个进程
线程
在计算机中,把进程内部进行的子任务称为线程(线程就是独立的执行路径)
进程和线程的紧密联系
一个进程可以包含多个线程
备注
一个Java程序实际上是一个JVM进程,JVM进程通过主线程(main())执行,在main内部,可以用来启动多个线程
线程创建的三种方式
1.1创建Thread的方法,继承Thread类
public class Demo01 extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++){
System.out.println("我在看代码");
}
}
public static void main(String[] args) {
//主线程(main)
for (int i = 0; i < 20;i++){
System.out.println("我在看线程");
}
//线程对象的创建
Demo01 demo01 = new Demo01();
//调用start()方法开启线程
demo01.start();
}
}
1.2实现Runnable接口(推荐)
public class Demo02 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20; i++ ){
System.out.println(Demo01.currentThread().getName()+"实现Runnable接口");
}
}
public static void main(String[] args) {
//创建线程
Demo02 demo02 = new Demo02();
Thread thread = new Thread(demo02,"小明");
thread.start();
new Thread(demo02,"小黑").start();
}
}
1.3实现Callable接口
public class Demo03 implements Callable<Boolean> {
private String url;
private String name;
/**创建线程时,需要传入对象
*
* @param url
* @param name
*/
public Demo03(String url, String name) {
this.url = url;
this.name = name;
}
@Override
public Boolean call() throws Exception {
WebDownloader webDownloader = new WebDownloader();
//调用方法,传入下载的参数
webDownloader.Downloader(url,name);
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
Demo03 demo03 = new Demo03("https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2062307482,1942308797&fm=26&gp=0.jpg","图片1.jpg");
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(1);
//提交执行服务,并发包
Future<Boolean> r1 = ser.submit(demo03);
//获取结果
boolean rs1 = r1.get();
System.out.println(rs1);
//关闭服务
ser.shutdown();
}
}
/**下载其功能
**/
class WebDownloader{
/** 需要传入url和name
*
* @param url
* @param name
*/
public void Downloader(String url, String name){
try {
FileUtils.copyURLToFile( new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
}
}
}
静态代理
代理的概念
为某个对象提供一个代理,以控制对这个对象的访问。
静态代理的概念
由程序员创建或工具生成代理的源码,再编译代理类,所谓静态也就是子啊程序运行前就已经存在代理类的字节码文件。
注意点
真实对象和代理对象都要实现同一个接口
代理对象要代理真实角色
好处
代理对象可以做真实对象做不了的事情
真实对象可以专注做自己的事情
/**
静态代理例子:结婚 ,接口:结婚 实体;你、婚庆公司
*/
public class Demo {
public static void main(String[] args) {
/**
WeddingCompany weddingCompany = new WeddingCompany(new you());
//调用方法
weddingCompany.happyMarry();*/
//简化操作
new WeddingCompanys(new You()).happyMarry();
/**线程操作(跟本例子没有关系)
* new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我爱你");
}
}).start();*/
//简化线程操作,Thread代理Runnable接口
new Thread(()->{System.out.println("我爱你");}).start();
}
}
/**结婚
*
*/
interface Marrys{
void happyend();
}
/** 实体:你 作用:结婚
*
*/
class You implements Marry{
@Override
public void happyMarry() {
System.out.println("结婚快乐");
}
}
/** 实体:婚庆公司 作用:代理你(结婚前—>布置会场 结婚后—>收尾款 结婚时—>主持婚礼)
*
*/
class WeddingCompanys implements Marry{
//代理角色的名字
private Marry targets;
//传入参数
public WeddingCompanys(Marry targets){
this.targets = targets;
}
@Override
public void happyMarry() {
//结婚前
before();
//结婚时,公司代理的角色结婚
this.targets.happyMarry();
//结婚后
after();
}
//结婚前
private void before(){
System.out.println("布置会场");
}
//结婚后
private void after(){
System.out.println("收取尾款");
}
}
}
细节:lambda表达式
推导过程:lambda的出现进程历史(根据一步步的简化)
public class TestLambda1 {
/**3、优化,静态内部类
*
*/
static class Like2 implements ILike{
@Override
public void lambda(int a) {
System.out.println("i like lambda1");
}
}
public static void main(String[] args) {
ILike iLike = new Like();
ILike iLike1 = new Like2();
/** 4、局部内部类
*
*/
class Like3 implements ILike{
@Override
public void lambda(int a) {
System.out.println("i like lambda2");
}
}
ILike iLike2 = new Like3();
//5、匿名内部类
ILike iLike3 = new ILike() {
@Override
public void lambda(int a) {
System.out.println("i like lambda3");
}
};
//6、用lambda表达式
ILike iLike4 = (int a)->{System.out.println("i like lambda4");};
//1、简化(省略类型),注意:要不全省,要不不省
iLike4 = ( a)->{System.out.println("i like lambda4");};
//2、简化括号
iLike4 = a->{System.out.println("i like lambda4");};
//3、简化中括号,注意:只能由一条语句存在时
iLike4 = a-> System.out.println("i like lambda4");
}
}
/**1、定义函数(只有一个方法)式接口(lambda必须要函数式)
*
*/
interface ILike{
void lambda(int a);
}
/** 2、实现类
*
*/
class Like implements ILike{
@Override
public void lambda(int a) {
System.out.println("i like lambda");
}
}
线程状态
1.1线程状态五大状态
1.2线程方法
线程的停止(推荐标志位)
停止线程destroy()或是stop() [被JDK废弃了](不推荐)
===>推荐线程自己停止下来(使用标志位):
/**测试stop
* 1、建议线程正常停止—>利用次数,不建议死循环
* 2、建议使用标志位—>设置一个标志位
* 3、不要使用stop或者destroy等过时或者jdk不建议的方法
*/
public class TestStop implements Runnable{
/** 1、 设置标志位
* 、
*/
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (flag){
System.out.println("run....Thread"+ i++);
}
}
/**2、设置一个公开的方法停止线程,转换标志位
*
*/
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++) {
if (i == 900){
//调用stop方法切换标志位,让flag切换程false,让线程停止
testStop.stop();
System.out.println("该线程停止");
}
}
}
}
线程的休眠 sleep
指定当前线程阻塞的毫秒数;
存在异常InterruptedException;
时间达到后线程进入就绪状态;
可以模拟网络延时,倒计时等;
重点:每个对象都有一个锁,sleep不会释放锁;
//模拟倒计时
public class TestSleep2 {
public static void main(String[] args) {
try {
tenDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void tenDown() throws InterruptedException {
int num = 10;
while (true){
Thread.sleep(10);
System.out.println( num--);
if (num <= 0){
break;
}
}
}
}
线程礼让 Thread.yield()
礼让线程,让当前正在执行的线程暂停,但阻塞
将线程从运行状态转为就绪状态
注意:让CPU重新调度,礼让不一定成功!看CPU心情
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()+"线程停止执行");
}
}
线程的强制执行 join()
join合并线程,待此行程执行完成后,再执行其他线程
可以想象成插队
应用:重要的线程需要先执行
public class TestJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("线程vip来了"+i);
}
}
public static void main(String[] args) throws InterruptedException {
/**
* 总结:
* thread线程通过join方法插队main线程,一般来说都时main线程先执行*/
//启动我们的线程
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
//thread线程插队main线程(把启动的线程插队到main线程前)
thread.join();
for (int i = 0; i < 100; i++) {
System.out.println("我是main线程"+i);
}
}
}
1.3线程状态观测
public class TestState {
public static void main(String[] args) throws InterruptedException {
//创建线程
Thread thread = new Thread(()->{
for (int i = 0; i < 1000; i++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程死亡");
});
// 观察状态,获取状态
Thread.State state = thread.getState();
//new
System.out.println(state);
//启动线程
thread.start();
//获取启动线程的状态:run
state = thread.getState();
System.out.println(state);
//获取线程等待和死亡的状态,注意:线程死亡后是无法启动的
while (state != Thread.State.TERMINATED){//只要线程不终止,就一直输出状态
Thread.sleep(100);
state = thread.getState();
System.out.println(state);
}
}
}
1.4线程优先级
Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度哪个线程来执行
线程优先级用数字表示,范围从1~10(数字越高,优先级越高):
Thread.MIN_PRIORITY = 1;
Thread.MAX_PRIORITY = 10;
Thread.NORM_PRIORITY = 5;
使用以下方式改变或获取优先级:
getPriority().setPriority(int XXX)
注意:让CPU重新调度,不一定成功!看CPU心情 (可以看看线程礼让)
public class Demo {
public static void main(String[] args) {
//主线程
System.out.println("嘻嘻,永远我最先,优先级:" + Thread.currentThread().getPriority());
//创建对象
MyPriority myPriority = new MyPriority();
//创建线程->小明
Thread thread = new Thread(myPriority,"小明");
//设置优先级
thread.setPriority(1);
//创建线程->小黑
Thread thread1 = new Thread(myPriority,"小黑");
//设置优先级
thread1.setPriority(10);
//启动线程
thread.start();
thread1.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "来设置优先级来啦!");
}
}
守护线程( Daemo() )
我认为的守护线程:
当 JVM 中不存在任何一个正在运行的非守护线程(例如:死循环)时,则 JVM 进程即会退出。
简单来说守护线程会随者用户线程(例如:main线程)的结束而结束
应用:希望主体线程结束时,其他线程也可以自动关闭,即使时死循环
注意点:
线程分为:用户线程和守护线程
虚拟机必须确保用户线程执行完毕
虚拟机不用等待守护线程执行完毕
/**
*保护线程(例子:上帝的守护)
*/
public class Demo {
public static void main(String[] args) {
//创建god对象
God god = new God();
//创建线程
Thread thread = new Thread(god);
//设置为守护线程,默认false为用户线程
thread.setDaemon(true);
//启动线程
thread.start();
//创建hum对象
Hum hum = new Hum("小黑");
//启动线程
new Thread(hum).start();
}
}
/**上帝
*
*/
class God implements Runnable{
@Override
public void run() {
//死循环,突显虚拟机不用等待守护线程执行完毕
while (true){
System.out.println("上帝在守护你");
}
}
}
/** 人类个体
*
*/
class Hum implements Runnable{
private String name;
public Hum(String name){
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println( name+ "活了"+ i+ "年");
}
System.out.println("====================再见世界");
}
}
线程的同步机制
并发(多个线程操作同一个对象)
public class TestThread04 implements Runnable {
//初始票数
private int ticketNums = 10;
@Override
public void run() {
while (true){
if (ticketNums <= 0){
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() +"我买了第" + ticketNums-- + "张");
}
}
public static void main(String[] args){
TestSleep testSleep = new TestSleep();
new Thread(testSleep,"小明").start();
new Thread(testSleep,"老师").start();
new Thread(testSleep,"黄牛党").start();
}
}
产生问题
多个线程操作同一个资源的情况下,线程不安全,数据紊乱。
解决问题
可以通过同步的同步块和通过同步方法解决
细节:处理多线程问题
多个线程访问同一个对象,并且某些线程还想修改这个对象,这个时候我们就需要线程同步。
线程同步的理解
一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成对列,等待前面线程完成后,下一个线程再使用。
举例:
上厕所排队,一定要等上一个人结束后,才可以进行
线程同步的条件
队列+锁(每个对象都有一个锁)
锁的作用
同一进程的多个线程共享同一块存储空间,在带来便利的同时,也会带来访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized(当一个线程获得对象的排它锁,独占资源,其他线程必须等待)
锁的缺点
牺牲效率,但提高安全性
优先级倒置(高的等低的线程完成),引起性能问题
三大不安全案例:
1、不安全取钱
public class UnsafeBank {
public static void main(String[] args) {
//账户
Account account = new Account(100,"结婚基金");
//传入取的角色的线程名
Drawing drawing = new Drawing(account,50,"你");
Drawing girlFriend = new Drawing(account,100,"girlFriend");
drawing.start();
girlFriend.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,int drawingMoney, String name){
//设置父类的线程名
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
/** 取钱,通过继承Thread类重写run()
*
*/
@Override
public void run(){
//判断有没有钱
if (account.money - drawingMoney<0){
//没有钱
System.out.println(Thread.currentThread().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(),因为继承了Thread类
System.out.println(this.getName() + "手里的钱"+ nowMoney);
}
}
2、不安全的买票
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();
}
}
}
/** 买ticketNums票
*/
private void buy() throws InterruptedException {
if (ticketNums <= 0){
flag = false;
//结束程序
return;
}
//模拟延时,放大买票,更便于查出错误
Thread.sleep(1000);
//输出
System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
}
}
3、线程不安全的集合
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());
}
}
同步方法(锁方法)
使用指南:
比如买票的方法:一次只能进去一个人买,就只让一个线程进入方法
同步方法:
public synchronized void method( int args){}
控制对"对象"的访问,每个对象对应一把锁,每个同步方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,知道该方法返回才释放锁,后面被阻塞的线程才能这个锁,继续执行。
备注:同步方法的同步监控器是this
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();
}
}
}
/** 买ticketNums票
* 注意:synchronized锁(同步方法)的是默认this,因为只有一个对象,所以锁的是BuyTicket
*/
private synchronized void buy() throws InterruptedException {
if (ticketNums <= 0){
flag = false;
//结束程序
return;
}
//模拟延时,放大买票,更便于查出错误
Thread.sleep(1000);
//输出
System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
}
}
同步块(锁对象)
使用指南:多个线程操作同一个对象,比如:存钱和取钱时两个方法操作的都是同一个账户
同步块:
synchronized(Obj){}
Obj称为同步监控器:
Obj可以是任何对象,但是推荐使用共享资源作为同步监控器
同步监视器的执行过程:
1、第一个线程访问,锁定同步监视器,执行其中代码
2、第二个线程访问,发现同步监视器被锁定,无法访问
3、第一个线程访问完毕,解锁同步监视器
4、第二个线程访问,发现同步监视器没有锁,然后锁定并访问
public class UnsafeBank {
public static void main(String[] args) {
//账户
Account account = new Account(100,"结婚基金");
//传入取的角色的线程名
Drawing drawing = new Drawing(account,50,"你");
Drawing girlFriend = new Drawing(account,100,"girlFriend");
drawing.start();
girlFriend.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,int drawingMoney, String name){
//设置父类的线程名
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
/** 取钱,通过继承Thread类重写run()
*
*/
@Override
public void run(){
//同步块,因为有多个对象,锁的是对象为变化的量,即增删改查的对象
synchronized (account){
//判断有没有钱
if (account.money - drawingMoney<0){
//没有钱
System.out.println(Thread.currentThread().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(),因为继承了Thread类
System.out.println(this.getName() + "手里的钱"+ nowMoney);
}
}
}
CopyOnWriteArrayList(了解)
public class Demo {
public static void main(String[] args){
//已经写好,本身就是安全的
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 1000; 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 DeadThread {
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 choice;
String girlName;
/**
* 输入参数,初始化
*/
public Makeup(int choice, String girlName) {
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
//化妆
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 死锁,互相持有对方的锁,就是需要拿到对方的资源
* 本例子的产生的条件:一人在持有口红未结束的同时去持有镜子,另一个人相反
*/
public void makeup() throws InterruptedException{
//先选口红,后选镜子
if (choice == 0){
//获取口红的锁
synchronized (lipstick){
System.out.println( girlName + "获取口红");
synchronized (mirror){
System.out.println( girlName + "获取镜子");
}
}
}else {
synchronized (mirror){
System.out.println( girlName + "获取镜子");
synchronized (lipstick) {
System.out.println( girlName + "获取口红");
}
}
}
}
}
Lock锁
Lock锁
定义:显示定义同步锁
详细定义:
ReentrantLock(重复入锁)类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是 ReentrantLock,可以显式加锁,释放锁
注意:Lock只能锁块
public class TestLock {
public static void main(String[] args) {
TestLock02 testLock02 = new TestLock02();
new Thread(testLock02).start();
new Thread(testLock02).start();
new Thread(testLock02).start();
}
}
class TestLock02 implements Runnable{
int ticketNume = 10;
/** 定义Lock锁
*
*/
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
//加锁
lock.lock();
//加锁的代码
try {
if (ticketNume > 0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ticketNume--);
}else {
break;
}
}finally {
//解锁
lock.unlock();
}
}
}
}
synchronized与Lock的对比
- Lock是显性锁(手动开启和关闭锁,别忘记关闭锁)synchronized是隐形锁,出了作用域自动释放
- Lock只有代码块锁,synchronized有代码锁和方法锁
- 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且有更好的扩展性
- 优先使用顺序
- Lock > 同步代码块 > 同步方法
线程协程(生产者消费者模式=》问题)
描述:生产者和消费这个共享一个资源,并且生产者和消费者之间相互依赖,互为条件。
- 对于生产者,没有生产产品之前,要通知消费者等待,而生产了产品之后,又需要马上通知消费者消费
- 对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费
- 在生产者消费者问题中,仅有synchronized是不够的
- synchronized可阻止并发更新同一个共享资源,实现了同步
- synchronized不能用来实现不同线程之间的消息传递(通信)
解决方法
/**
* 测试:生产者消费者模型-->利用缓冲区解决:管程法
* 实体:生产者,消费者,产品,缓冲区
*/
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
//创建线程
new Productor(container).start();
new Consumer(container).start();
}
}
/**
* 产品(只管生产)
*/
class Productor extends Thread{
/**
* 创建缓冲区对象
*/
SynContainer container = new SynContainer();
/**
* 传入初始参数
* @param container
*/
public Productor(SynContainer container){
this.container = container;
}
/** 生产
*
*/
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//添加i值
container.push(new Chicken(i));
System.out.println("生产了" + i + "只鸡");
}
}
}
/**
* 消费者
*/
class Consumer extends Thread{
SynContainer container = new SynContainer();
public Consumer(SynContainer container){
this.container = container;
}
/**
* 消费
*/
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了--->"+ container.pop().id+ "只鸡");
}
}
}
/**产品
*
*/
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){
//判断容器(满)
if (count == chickens.length){
//通知消费者消费,生产等待
try {
//线程等待,直到其他线程通知
this.wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
//判断容器未满.丢入产品
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:信号灯,标志位解决
*/
public class TestPC02 {
public static void main(String[] args) {
Tv tv = new Tv();
new Play(tv).start();
new Watch(tv).start();
}
}
/**生产者--->演员
*
*/
class Play extends Thread{
Tv tv = new Tv();
public Play(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 Watch extends Thread{
Tv tv;
public Watch(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.notify();
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;
}
}
使用线程池
相关API:ExecutorService和Executors
ExecutorService:真正的线程池接口。常用类ThreadPoolExecutor
- void execute(Runnable command):执行任务/命令,没有返回值,一般用来执行Runnable
- Future submit(Callable task):执行任务,有返回值,一般又来执行Callable
- void shutdown():关闭连接池
Executors:工具类,线程池的工厂类,用于创建并返回不同类型的线程池
public class TestPool {
public static void main(String[] args) {
//1、创建服务,创建线程池
//构建线程池
ExecutorService service = Executors.newFixedThreadPool(10);
//执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
//结束连接
service.shutdown();
}
}
class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName());
}
}
}
扩展:
/** public ThreadPoolExecutor(
int corePoolSize, - 线程池核心池的大小。
int maximumPoolSize, - 线程池的最大线程数。
long keepAliveTime, - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
TimeUnit unit, - keepAliveTime 的时间单位。
BlockingQueue<Runnable> workQueue, - 用来储存等待执行任务的队列。
ThreadFactory threadFactory, - 线程工厂。
RejectedExecutionHandler handler) - 拒绝策略。
**/
public class TestPool {
public static void main(String[] args) {
//1、创建服务,创建线程池
//构建线程池
ThreadPoolExecutor service = new ThreadPoolExecutor(10,10,10, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(1),new ThreadPoolExecutor.DiscardOldestPolicy());
//执行
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 < 10; i++) {
System.out.println(Thread.currentThread().getName());
}
}
}