今天的内容特别重要!!!
1.进程
2.线程【重点】
1.什么是进程
进程是独立的运行程序
比如咱们电脑软件,你启动起来以后,他就是一个进程。
qq idea,vscode等等
进程需要windows系统的分配。可以获取当前的系统的网卡,内存,显卡等
- 独立性
各个进程之间是相互的独立的互不影响 的。录屏软件和idea没有关系的
- 互斥性
每个软件系统都会分配一个独立端口号,如果启动一个软件以后他的端口号97。
如果再启动另外一个软件,另外一个软件如果端口也是97,这个程序就启动不了,端口被占用了
脑海里面知道开启的软件就是一个进程 即可!!!
2.什么是线程
进程是由多个或者一个线程组成的。每个进程至少得有一个线程的支撑。
脑海里面这样来想,一个进程(qq),qq里面很多个线程在执行。线程的执行
支撑起来了进程的执行。
把一个人比作一个进程,那么你身体里面的细胞就是线程。如果没有细胞。这个人还存在吗?不存在的!!!
进程包含了线程,线程是组成进程的最小基本单位
特性:
- 抢占式运行的【重要】
CPU在执行的时候,按照时间片来执行的,单位的时间片是抢占是执行
比如 idea qq 抢占CPU的,比如qq的线程抢到cpu,idea线程等待
我是一个cpu。你们问我问题。75个线程。同时来问我问题吗?不是
抢着问。一个问。然后其他人等待。这个人甚至还没有问完,其他的某一个人
抢到我了,他问我。大概就是效果
- 资源共享性
一个线程可以共享当前CPU, 网卡等
Java程序:
一个Java程序就是一个进程 Demo1 就是一个应用程序 就是一个进程
一个Java程序Demo1里面至少几个线程?
两个:
main主函数线程
JVM垃圾回收器线程
3.线程和进程的区别【面试题】
进程是一个应用程序,是独立的
线程是进程中最小的基本单位。
把进程比作生产车间,每个流水线就是一个线程
进程有独立性和互斥性
线程有抢占式资源共享特性
4.并发和并行
并发:同时发生,轮流交替执行(执行很快所以有了 "并行的错觉" )
并行:真正意义的同时执行
比如:
你去饭店点了两个菜,生活中拿个筷子轮流夹菜哦这就是并发场景
端起盘子,同时倒到嘴里面,这就是并行场景
5.创建线程的两种方式【重点】
方式一、继承Thread类
创建线程的两种方式
一个是将一个类声明为
Thread的子类,即继承Thread类。 这个子类应该重写run方法 ,然后可以创建该线程类的实例,并启动该实例的线程。
方式一、案例:
// 一个是将一个类声明为Thread的子类。
// 这个子类应该重写run类的方法Thread 。
// 然后可以分配并启动子类的实例。
class MyThread1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 500; i++) {
System.out.println("我是myThread1线程:" + i);
}
}
}
class MyThread2 extends Thread {
@Override
public void run() {//run方法中写功能代码 就是一个线程中执行的一个功能
for (int i = 0; i < 500; i++) {
System.out.println("我是mythread2线程:" + i);
}
}
}
public class Demo1 {
public static void main(String[] args) {
//官方手册中说,要去实例化Thread的子类,并启动线程
MyThread1 myThread1 = new MyThread1();
//启动线程 使用start方法 在主线程中开启子线程
myThread1.start();
MyThread2 myThread2 = new MyThread2();
myThread2.start();
//现在有几个线程? 4个
//一个是MyThread1线程 一个是MyThread2线程 一个是主线程(main) 一个垃圾回收机制线程
for (int i = 0; i < 500; i++) {
System.out.println("主函数线程:" + i);
}
//发现先执行了主线程,然后再执行子线程,然后又执行主线程
//这就线程的抢占式的运行
/四个线程:
//你自己吃四盘菜,咋吃?
//一盘菜代表一个线程,一盘菜夹一下,随机的吧。
//开启一个线程,就是在执行一个任务。
//上面这个代码,你们执行结果和我执行的结果一样吗?绝对不一样的
//抢占式的,随机执行线程的!!!
}
}
方式一、练习
练习:
main主线程 打印100遍的吃大盘鸡
子线程1 打印100遍的吃水煮肉片
子线程2 打印100遍的吃毛血旺
一定要注意打印的结果,多执行几遍,看看每次执行的结果是否一样!!!
方式二、实现Runable接口
另一种方法来创建一个类后实现
Runnable接口 (并不能叫做线程类,应该称为任务类) , 任务类实现了run方法。 然后任务类的实例需要被"包装"为线程,即在创建Thread时作为参数传递,然后作为一个线程启动。
方式二、案例
//另一种方法来创建一个线程是声明实现类Runnable接口。
// 那个类然后实现了run方法。
// 然后可以分配类的实例(创建类的对象),在创建Thread实例时作为参数传递,并启动。
class MyThread3 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("MyThread3:" + i);
}
}
}
class MyaThread4 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("MyThread4:" + i);
}
}
}
public class Demo2 {
public static void main(String[] args) {
MyThread3 myThread3 = new MyThread3();
//Thread(Runnable target) 参数是Runnable这个接口对象
//分配一个新的 Thread对象。
Thread thread = new Thread(myThread3);
thread.start();
MyaThread4 myaThread4 = new MyaThread4();
Thread thread1 = new Thread(myaThread4);
thread1.start();
for (int i = 0; i < 100; i++) {
System.out.println("main主线程:" + i);
}
//有几个线程做任务的线程 3个 一个是main主线程 一个是MyThread3
//一个MyThread4这个线程
//可以发现运行的结果是随机执行的!!!
}
}
方式二、练习
练习:
main主线程 打印100遍的吃大盘鸡
子线程1 打印100遍的吃水煮肉片
子线程2 打印100遍的吃毛血旺
一定要注意打印的结果,多执行几遍,看看每次执行的结果是否一样!!!
题目:start方法和run方法的区别
在 Java 多线程中,
run()是线程执行的任务入口,像普通方法一样执行;而start()方法会通知 JVM 创建一个新的线程,并自动调用线程的run()方法。调用start()才真正开启一个新的线程,而直接调用run()方法并不会创建新线程,只是当前线程顺序执行一段代码。
6.线程下面的几个方法
1.构造方法
方法名 说明 Thread()创建一个新的线程对象,但还没有指定它要执行的任务。适合子类继承Thread后使用。此构造方法是第一种线程创建方式的核心。
Thread(Runnable target)创建一个线程对象,并传入一个实现了Runnable接口的任务对象,线程启动后会执行这个任务。此构造方法是第二种线程创建方式的核心。
Thread(Runnable target, String name)和上面一样,但同时给线程取一个名字,便于调试和日志分析。
2.线程方法:
static Thread
currentThread()返回对当前正在执行的线程对象的引用
String
getName()返回此线程的名称。
void
setName(String name)将此线程的名称更改为等于参数name。
int
getPriority()返回此线程的优先级。
void
setPriority(int newPriority)更改此线程的优先级。设置优先并不一定优先,只是增加了执行的概率。最小值是1,最大值是10,默认的是5
static void
sleep(long millis)使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。
class MyThread1 implements Runnable {
@Override
public void run() {
Thread thread = Thread.currentThread();
//此时这个thread对象是MyThread1这个线程
//对子线程设置名字
thread.setName("mythread1子线程");
System.out.println(thread.getName());//Thread-0
}
}
class MyThread2 implements Runnable {
@Override
public void run() {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());//狗蛋
}
}
public class Demo1 {
public static void main(String[] args) {
//currentThread() 获取当前线程对象
Thread thread = Thread.currentThread();
//现在thread 这个对象是哪个线程?主线程
//为啥是main主线程
//给main主线程设置名字
thread.setName("主线程");
//获取的是main主线程的名字,Jvm会给主线程还有其他线程一个默认的名字
System.out.println(thread.getName());//main
// MyThread1 myThread1 = new MyThread1();
// Thread thread1 = new Thread( myThread1);
// thread1.start();
new Thread(new MyThread1()).start();
//Java中默认的主线程叫main 子线程叫Thread-0 Thread-1....
//默认的有名字,咱们能不能对线程自定义名字?能
//我在启动MyThread2的这个线程的时候顺便起名字
MyThread2 myThread2 = new MyThread2();
//myThread2线程的名字就叫狗蛋,是在创建线程的时候就已经起好名字了
Thread thread1 = new Thread(myThread2, "狗蛋");
thread1.start();
}
}
class MyThread3 implements Runnable {
@Override
public void run() {
//想看一下MyThread3这个线程的优先级是几
Thread thread = Thread.currentThread();
thread.setPriority(10);
System.out.println(thread.getPriority());
for (int i = 0; i < 100; i++) {
System.out.println("MyThread3线程:" + i);
}
}
}
class MyThread4 implements Runnable {
@Override
public void run() {
//想看一下MyThread3这个线程的优先级是几
Thread thread = Thread.currentThread();
thread.setPriority(1);
System.out.println(thread.getPriority());
for (int i = 0; i < 100; i++) {
System.out.println("MyThread4线程:" + i);
}
}
}
public class Demo2 {
public static void main(String[] args) {
//默认的优先级都是5,能不能手动去修改某一个线程的优先级?
//可以
Thread thread = Thread.currentThread();
//主线程的优先级设置1
//thread.setPriority(1);
//获取主线程的优先级
System.out.println(thread.getPriority());//5
//优先级 1 ~10 1的优先级最低 10的优先级最高
//jvm默认线程的优先级是5
// for (int i = 0; i < 100; i++) {
// System.out.println("主线程:" + i);
// }
new Thread(new MyThread3()).start();
new Thread(new MyThread4()).start();
//所以优先级不要用啦!!!并不一定真正的优先!!!
//线程执行的结果不可控!!!很尴尬!!!
}
}
class MyThread5 implements Runnable {
@Override
public void run() {
try {
//发现 Thread.sleep 有一个运行时异常,
//但是发现没有抛出,只有try-catch 为啥?
//sleep方法写在了run方法中了,因为
//run方法是重写的, 父类的 public abstract void run();
//父类有抛出吗?没有抛出,重写是比较严格的
//父类没有抛出,子类也同样不能抛出
Thread.sleep(10000);//10秒
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 500; i++) {
System.out.println("MyThread5:" + i);
}
}
}
class MyThread6 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 500; i++) {
System.out.println("MyThread6:" + i);
}
}
}
public class Demo3 {
public static void main(String[] args) {
new Thread(new MyThread5()).start();
new Thread(new MyThread6()).start();
//发现运行的结果是不可控的,是随机的抢占式的
//咱们接下来学习一个东西叫sleep 让某一个线程睡一会儿
//这个线程在睡觉的期间不会去抢占cpu 不执行
//现在我让MyTread5睡了一会儿。就意味着绝对MyThread6线程先执行
//实现可控的效果
//思考:sleep方法再开发的时候敢用不敢用?
//不敢用。睡多久你知道吗?睡多久合适?不能确定
//cpu最大利用化。不可能让cpu闲置
//如果sleep睡眠时间少的话,还是抢占
//如果sleep睡眠你时间太长的话,就会cpu就会闲置
//没有办法把控的!!! 接下来要学习锁!!!
}
}
上午的内容
1.新建线程的两种方式
1.继承Thread
2.实现Runnable接口【开发要用的】
代码要自己学会写的!!!
2.线程的方法
Thread.currentThread();获取当前线程对象
setPriority();设置优先级的
getPriority(); 获取当前线程的优先级
getName();得到线程的名字
setName();设置线程的名字
sleep();线程的休眠
7.线程的同步和锁【重要】
为什么要进行线程的同步?
Java是允许多线程(多个线程),当多个线程操作同一个资源(咋操作)的时候,会导致得到或者打印的数据不准确。从而发生冲突。咋解决?加同步锁。
美团
淘票票
这个两个线程,都去麦同一场次的票
结果美团卖出去一张1排1列的票
结果淘票票也卖出去了1排1列的票 你感觉合适吗?
就是上面的这种结果!!!不合适的,分享同一个资源的时候,要保证分享资源的数据,合法性!!!
分析结果:
//最理想的状态!!!
//先线程1进入到ticket=50,循环 循环结束以后 此时
//tiket=49了
//循环第二次的时候 线程2抢到资源了 此时ticket=49
//循环 打印49 tiket-- ticket=48了
//循环第三次的时候 线程2 抢到资源了, 此时ticket=48
//打印卖第48张票。ticket-- tiket=47
//线程1又抢到循环
//现在的情况是:有可能两个线程同时进入到while循环
//
class MySync implements Runnable {
int ticket = 50;
@Override
public void run() {
//
while (true) {//死循环
//两个线程都进入到了循环了
//此时两个线程所持有的ticket 都是50
//但是两个线程都要往下执行
//有可能线程1 先执行了sout(50) 线程2在等待哦!!!
//线程1执行了--操作并出了循环 线程1ticket = 49
//线程1又抢到循环了 sout(49) tiket--
//再进入倒这个循环,有可能线程2抢到这个执行权
//线程2要往下执行输出语句 ticket=50 打印50
if (ticket > 0) {
//线程具有抢占式的运行
//咱们有没有可能,线程3进入到if语句
//此时线程1也进入到if语句了
//线程3去打印 卖出了50张票
//在线程1里面 ticket=50
//线程3又抢到ticket-- 又进入到循环了 ticket = 49
//线程3又抢到了ticket-- 又进入倒循环 ticket=48
//线程1又抢到资源要执行,执行输出语句 tiekct=50
System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket + "票");
ticket--;
} else {
System.out.println("买完了");
break;//终止循环!!!
}
}
}
}
public class Demo1 {
public static void main(String[] args) {
MySync mySync = new MySync();
//这三个线程
Thread thread1 = new Thread(mySync, "线程1");
thread1.start();
Thread thread2 = new Thread(mySync, "线程2");
thread2.start();
Thread thread3 = new Thread(mySync, "线程3");
thread3.start();
}
}
解决方案:
1.同步方法:
使用一个关键字synchronized修饰方法。因为Java对象都有一个内置的锁对象。当使用这个关键字的时候,修饰方法的时候,这个方法就会被锁保护起来被锁锁住
当一个线程进来以后,会立马锁住当前的方法。意味着只有一个线程进来,其他线程都在外面等着。
public synchronized void run () { }
//最理想的状态!!!
//先线程1进入到ticket=50,循环 循环结束以后 此时
//tiket=49了
//循环第二次的时候 线程2抢到资源了 此时ticket=49
//循环 打印49 tiket-- ticket=48了
//循环第三次的时候 线程2 抢到资源了, 此时ticket=48
//打印卖第48张票。ticket-- tiket=47
//线程1又抢到循环
//现在的情况是:有可能两个线程同时进入到while循环
//
class MySync1 implements Runnable {
int ticket = 50;
//对这个run方法加了锁 就意味着只有一个线程进入到run方法中
//其他线程都在run方法外面等待
@Override
public synchronized void run() {
//
while (true) {//死循环
//两个线程都进入到了循环了
//此时两个线程所持有的ticket 都是50
//但是两个线程都要往下执行
//有可能线程1 先执行了sout(50) 线程2在等待哦!!!
//线程1执行了--操作并出了循环 线程1ticket = 49
//线程1又抢到循环了 sout(49) tiket--
//再进入倒这个循环,有可能线程2抢到这个执行权
//线程2要往下执行输出语句 ticket=50 打印50
if (ticket > 0) {
//线程具有抢占式的运行
//咱们有没有可能,线程3进入到if语句
//此时线程1也进入到if语句了
//线程3去打印 卖出了50张票
//在线程1里面 ticket=50
//线程3又抢到ticket-- 又进入到循环了 ticket = 49
//线程3又抢到了ticket-- 又进入倒循环 ticket=48
//线程1又抢到资源要执行,执行输出语句 tiekct=50
System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket + "票");
ticket--;
} else {
System.out.println("买完了");
break;//终止循环!!!
}
}
}
}
public class Demo2 {
public static void main(String[] args) {
MySync1 mySync = new MySync1();
//这三个线程
Thread thread1 = new Thread(mySync, "线程1");
thread1.start();
Thread thread2 = new Thread(mySync, "线程2");
thread2.start();
Thread thread3 = new Thread(mySync, "线程3");
thread3.start();
//为啥都是线程1卖出去的票?
//很巧 线程1抢到执行权了,进入到run方法中
//线程2和线程3在外面等着。
//一个循环进来以后,把循环全部执行完!!!
//会出现一家独大的情况!!!也是不符合咱们生活场景的!!!
//咋解决?咱们 不能方法中加锁,在其他地方加锁
}
}
换另外一种解决方法:
同步代码块:就是拥有了synchronized 关键字修饰一个语句块。被修饰的语句块会被加锁。从而实现同步。
语法格式:
synchronized (this) { 被加锁的代码块 }
//最理想的状态!!!
//先线程1进入到ticket=50,循环 循环结束以后 此时
//tiket=49了
//循环第二次的时候 线程2抢到资源了 此时ticket=49
//循环 打印49 tiket-- ticket=48了
//循环第三次的时候 线程2 抢到资源了, 此时ticket=48
//打印卖第48张票。ticket-- tiket=47
//线程1又抢到循环
//现在的情况是:有可能两个线程同时进入到while循环
//
class MySync2 implements Runnable {
int ticket = 500;
//对这个run方法加了锁 就意味着只有一个线程进入到run方法中
//其他线程都在run方法外面等待
@Override
public void run() {
//能不能对循环加锁?不能 因为循环加锁以后,还是一个线程循环完,没有任何意义
while (true) {//死循环
//if语句加了锁以后
//就意味着只有一个线程进入到if语句
//假如线程1进入if语句了,线程2和线程3就会等待
//线程1打印50 并-- ticket变量为49
//线程2抢到了49 sout(49) tiket-- 48
//其他线程再抢!!!
//核心业务 加了锁,只让一个线程进入,操作完以后。锁释放掉
//然后这三个线程再抢。还只能进一个,再操作核心业务
synchronized (this) {//只能让一个线程进入操作,其他线程在外面等待排队
if (ticket > 0) {
//线程具有抢占式的运行
//咱们有没有可能,线程3进入到if语句
//此时线程1也进入到if语句了
//线程3去打印 卖出了50张票
//在线程1里面 ticket=50
//线程3又抢到ticket-- 又进入到循环了 ticket = 49
//线程3又抢到了ticket-- 又进入倒循环 ticket=48
//线程1又抢到资源要执行,执行输出语句 tiekct=50
System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket + "票");
ticket--;
} else {
System.out.println("买完了");
break;//终止循环!!!
}
}
}
}
}
public class Demo3 {
public static void main(String[] args) {
MySync2 mySync = new MySync2();
//这三个线程
Thread thread1 = new Thread(mySync, "线程1");
thread1.start();
Thread thread2 = new Thread(mySync, "线程2");
thread2.start();
Thread thread3 = new Thread(mySync, "线程3");
thread3.start();
//为啥都是线程1卖出去的票?
//很巧 线程1抢到执行权了,进入到run方法中
//线程2和线程3在外面等着。
//一个循环进来以后,把循环全部执行完!!!
//会出现一家独大的情况!!!也是不符合咱们生活场景的!!!
//咋解决?咱们 不能方法中加锁,在其他地方加锁
}
}
针对于同步代码块举个例子:
上厕所的时候,有坑位,这个坑位就是资源。
三个人去抢这个资源,如果不加锁的话,会出现问题的?是的。加上锁以后就会保证数据准确性。
案例:
加锁的目的为了保证数据的准确性。
卖电影票:
三个线程:
淘票票
美团
猫眼
100张票
class SaleTicket implements Runnable {
//声明一个变量票
//静态的变量和对象没有关系了
private static int ticket = 100;
@Override
public void run() {
//美团
while (true) {
//美团 猫眼 淘票票
synchronized (this) {
if (ticket > 0) {
//淘票票 和猫眼同时进入到if语句,但是都没有执行ticket--这个操作
System.out.println(Thread.currentThread().getName() + "卖出了第" + ticket + "票");
//所以会打印 淘票票 100 猫眼100
ticket--;
} else {
System.out.println("卖完了");
break;
}
}
}
}
}
public class Demo4 {
public static void main(String[] args) {
SaleTicket saleTicket = new SaleTicket();
new Thread(saleTicket, "淘票票").start();
new Thread(saleTicket, "美团").start();
new Thread(saleTicket, "猫眼").start();
//淘票票卖出了第100票
//猫眼卖出了第100票
//淘票票卖出了第99票
//淘票票卖出了第97票
//淘票票卖出了第96票
//淘票票卖出了第100票
//淘票票卖出了第99票
//猫眼卖出了第98票
//猫眼卖出了第97票
}
}
线程就是这样,不可控制,但是可以加锁。让他可控制。
作业:
1.锁的问题,一定要自己去百度一些资料
增长自己的见识
lock();
unlock();
2.将今天讲东西弄懂。理解透侧!!!
今天的内容
> 1.守护线程 > > 2.死锁 > > 3.线程的生命周期 > > 4.关于Object类下面的方法和线程有关 > > 5.生产者消费者模式【难点】
1.守护线程
> 守护线程是用来守护非守护线程的。 > > 非守护线程:就是平常写的线程 > > 非守护线程一旦结束,守护线程就会自动消亡 > > 守护线程依附非守护线程,如果非守护线程消亡,那么守护线程随之消亡。
前台线程(非守护线程)与后台线程(守护线程)
后台线程:指为其他线程提供服务的线程,也称为守护线程。JVM的垃圾回收线程就是一个后台线程。
前台线程:是指接受后台线程服务的线程,也称为非守护线程。,由前台线程创建的线程默认也是前台线程。可以通过isDaemon()和setDaemon()方法来判断和设置一个线程是否为后台线程。
区别:
前台线程会各自运行各自的,都运行结束,java程序进程才会结束,前台线程互不干扰
后台线程需要有前台线程支持,如果前台线程都行完了,程序进程也就结束了,后台线程不管有没有运行结束,都会被终止结束
class MyThread1 implements Runnable {
@Override
public void run() {
System.out.println("软件更新中......");
for (int i = 1; i <= 1000; i++) {
System.out.println("downloading:" + i + "%");
}
}
}
public class Demo1 {
public static void main(String[] args) {
Thread thread = new Thread(new MyThread1());
//在线程的启动之前调用设置守护线程的
thread.setDaemon(true);
thread.start();
for (int i = 1; i <= 300; i++) {
System.out.println("主线程正在执行.....");
}
}
}
2.死锁
> 开发中禁止使用死锁。 > > 面试会问: > > 应用场景:并发场景,多线程。线程之间互不相让。得借助锁。 > > 加锁的目的是为了线程安全,但是物极必反。尤其是加了锁以后。 > > 死锁是一种状态,当两个线程互相持有对方的资源的时候,却又不主动释放这个资源的时候。会导致死锁。这两个线程就会僵持住。代码就无法继续执行。 > > 线程1 有锁1 > > 线程2 锁2 > > 线程1会等待锁2 的释放 > > 线程2会等待锁1的释放
class DeadLock implements Runnable{
private boolean flag;//标记属性
private Object obj1;//
private Object obj2;
//有参构造
public DeadLock(boolean flag, Object obj1, Object obj2) {
this.flag = flag;
this.obj1 = obj1;
this.obj2 = obj2;
}
@Override
public void run() {
if (flag) {//如果是个true的时候,让线程1进来
synchronized (obj1) {//锁的是obj1这个对象
System.out.println(Thread.currentThread().getName() + "拿到了锁1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("等待锁2的释放....");
//对obj2对象加锁的时候,结果人家线程2没有释放对obj2加的锁
synchronized (obj2) {
System.out.println(Thread.currentThread().getName() + "->拿到锁2");
}
}
}
if (!flag) {//如果flag是个false的话,让线程2进来
synchronized (obj2) {//锁的是obj2这个对象
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "拿到了锁2");
System.out.println("等待锁1的释放....");
synchronized (obj1) {
System.out.println(Thread.currentThread().getName() + "->拿到锁1");
}
}
}
}
}
public class Demo1 {
public static void main(String[] args) {
Object obj1 = new Object();
Object obj2 = new Object();
//第一个线程 flag 是true
DeadLock deadLock = new DeadLock(true, obj1, obj2);
new Thread(deadLock, "线程1").start();
//第二个线程 flag 是flase
DeadLock deadLock1 = new DeadLock(false, obj1, obj2);
new Thread(deadLock1, "线程2").start();
}
}
3.生命周期
> 1.线程的创建,开启线程 > > 2.可运行状态: CPU等待 CPU抢占 > > 3.运行状态: CPU等待 CPU抢占 CPU使用 > > 4.阻塞状态: 锁 sleep > > 5.消亡状态
4.和线程相关的Object类下面的方法
> Object类下面的方法: > >
> public final void wait() > throws InterruptedException >> > 导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法。 换句话说,这个方法的行为就好像简单地执行呼叫wait(0)。 > > 总结:至少两个线程,其中一个线程中使用对象.wait() 那么这个线程就会阻塞,代码不会往下执行了。如何想让这个线程往下执行呢?再开另外一个线程,使用对象.notify()去唤醒另外那个等待线程。
//为啥要写这个类? wait方法 需要对象来调用的额,先有类
class Message {
private String message;
public Message(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "Message{" +
"message='" + message + '\'' +
'}';
}
}
//现在开始写线程, 这个线程里面有一个 对象.wait() 线程会阻塞 等待
class WaiterThread implements Runnable {
private Message msg;
public WaiterThread(Message msg) {
this.msg = msg;
}
@Override
public void run() {
//获取当前线程的名字
String name = Thread.currentThread().getName();
synchronized (msg) {
System.out.println(name + "等待唤醒时间:" + System.currentTimeMillis());
try {
//当对象调用wait方法的时候,锁会释放,进入到对象的等待池中。
msg.wait();//让线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("123456");
System.out.println(name + "被唤醒的时间:" + System.currentTimeMillis());
System.out.println(name + "线程" + msg.getMessage());
}
}
}
class NotifierThread implements Runnable {
private Message msg;
public NotifierThread(Message msg) {
this.msg = msg;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String name = Thread.currentThread().getName();
System.out.println(name + "开始唤醒等待线程");
// Thread.sleep(1000);
synchronized (msg) {
msg.setMessage("我是修改之后的messgae属性的值");
//msg.notify();
msg.notifyAll();
}
}
}
public class Demo1 {
public static void main(String[] args) {
Message message = new Message("我是messag属性");
WaiterThread waiterThread = new WaiterThread(message);
new Thread(waiterThread, "wait").start();
new Thread(waiterThread, "wait1").start();
new Thread(waiterThread, "wait2").start();
NotifierThread notifierThread = new NotifierThread(message);
new Thread(notifierThread, "notify").start();
}
}
总结:
新建两个线程:
- 一个是等待线程 :
线程里面的代码从上往下执行的,但是使用object.wait(),就这个方法一用,你的线程就
阻塞了,就处于等待状态。意味着当前的代码到了wait方法以后的代码暂时不执行了
- 另外一个是唤醒线程 :
唤醒线程中使用object.notify()方法,这个方法是专门唤醒刚才那个等待线程。让等待线程继续执行
5.生产者消费者模式【重点难点】
> 生活中例子: > > 卖家: 汽车厂商 > > 买家: 咱们75名学生 > > 张启想买一辆 比亚迪汉 , 告知汽车厂商我要买车。这个张启会进入倒等待状态 > > 等到比亚迪厂家造完完以后,再通知张启来提车。如果比亚迪厂家有现车,张启就直接提车。 > > 如果产品需要生产的话,消费者进入到阻塞状态 > > 如果产品不需要生产的话,消费者直接购买
//为啥要写这个类?这个类是作为两个线程之间通信的桥梁
class Goods {
private String name;//名字
private double price;//价格
private boolean isProduct;//是否有这个商品, true 需要生产
//false 不需要生产商品
public Goods(String name, double price, boolean isProduct) {
this.name = name;
this.price = price;
this.isProduct = isProduct;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public boolean isProduct() {
return isProduct;
}
public void setProduct(boolean product) {
isProduct = product;
}
@Override
public String toString() {
return "Goods{" +
"name='" + name + '\'' +
", price=" + price +
", isProduct=" + isProduct +
'}';
}
}
//消费者线程
class Customer implements Runnable {
private Goods goods;
public Customer(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (true) {
synchronized (goods) {
//一直消费
//goods.isProduct() true 需要生产 没有商品
//false 不需要生产的
if (!goods.isProduct()) {
System.out.println("消费者购买了:" + goods.getName() + ",价格为:" + goods.getPrice());
//购买完以后 商品没了 isProduct 是true 没有商品了
goods.setProduct(true);
//唤醒生产者去生产
goods.notify();
} else {
//需要生产 消费者进入阻塞
try {
goods.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
//生产者线程
class Productor implements Runnable {
private Goods goods;
public Productor(Goods goods) {
this.goods = goods;
}
@Override
public void run() {
int count = 0 ;
while (true) {//一直生产商品
synchronized (goods) {
if (goods.isProduct()) {//true 需要生产者
//造车,奇数造一种车, 偶数的话造另外一种车
if (count % 2 == 0) {//偶数
goods.setName("玛莎拉蒂");
goods.setPrice(200);
} else {
goods.setName("五菱宏光");
goods.setPrice(400);
}
//生产完以后一定要记得 将标记改false
goods.setProduct(false);
System.out.println("生产者生产了:" + goods.getName() + ",价格为:" + goods.getPrice());
count++;
//生产者完了以后,人家消费者在等待这你呢
//唤醒消费者
goods.notify();
} else {
//不需要生产车,你就等着就行
try {
goods.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
public class Demo2 {
public static void main(String[] args) {
Goods goods = new Goods("东风", 89, false);
//false不需要生产的 谁阻塞 生产者阻塞
//最好是让生产者线程先执行
Productor productor = new Productor(goods);
new Thread(productor).start();
Customer customer = new Customer(goods);
new Thread(customer).start();
/**
* 消费者购买了:东风,价格为:89.0
* 生产者生产了:玛莎拉蒂,价格为:200.0
* 消费者购买了:玛莎拉蒂,价格为:200.0
* 生产者生产了:五菱宏光,价格为:400.0
* 消费者购买了:五菱宏光,价格为:400.0
* 生产者生产了:玛莎拉蒂,价格为:200.0
* isProduct = false
* 走了else 代码是生产者阻塞了 消费在睡了一秒之后立马执行
* 看消费者 有商品 买走了,之后 isProduct=true
* 唤醒生产者了,
* 生产者生产了:玛莎拉蒂,价格为:200.0 isProduct=false
*
*/
}
}
2160

被折叠的 条评论
为什么被折叠?



