多线程学习
This commit is contained in:
parent
9afc59fc65
commit
dfa5a3299c
|
|
@ -0,0 +1,84 @@
|
|||
package com.markilue.java_learning.thread;
|
||||
|
||||
/**
|
||||
* @BelongsProject: java_learning
|
||||
* @BelongsPackage: com.markilue.java_learning.thread
|
||||
* @Author: dingjiawen
|
||||
* @CreateTime: 2022-09-16 17:27
|
||||
* @Description:
|
||||
* TODO 死锁问题:
|
||||
* 不同的线程分别锁住对方需要的同步监视器对象不释放,都在等待对方先放弃时就形成了线程的死锁。
|
||||
* 一旦出现死锁,整个程序既不会发生异常,也不会给出任何提示,只是所有线程处于阻塞状态,无法继续。
|
||||
* @Version: 1.0
|
||||
*/
|
||||
public class DeadLock {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Object g = new Object();
|
||||
Object m = new Object();
|
||||
//两个人彼此都需要对方的同步锁释放才能运行下面的同步锁内容,但是对方都没有释放锁的操作
|
||||
Owner s = new Owner(g,m);
|
||||
Customer c = new Customer(g,m);
|
||||
new Thread(s).start();
|
||||
new Thread(c).start();
|
||||
|
||||
/*
|
||||
先给钱
|
||||
先发货
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
public static class Owner implements Runnable{
|
||||
private Object goods;
|
||||
private Object money;
|
||||
|
||||
public Owner(Object goods, Object money) {
|
||||
super();
|
||||
this.goods = goods;
|
||||
this.money = money;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (goods) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
System.out.println("先给钱");
|
||||
synchronized (money) {
|
||||
System.out.println("发货");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class Customer implements Runnable{
|
||||
private Object goods;
|
||||
private Object money;
|
||||
|
||||
public Customer(Object goods, Object money) {
|
||||
super();
|
||||
this.goods = goods;
|
||||
this.money = money;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (money) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
System.out.println("先发货");
|
||||
synchronized (goods) {
|
||||
System.out.println("再给钱");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
package com.markilue.java_learning.thread;
|
||||
|
||||
/**
|
||||
* @BelongsProject: java_learning
|
||||
* @BelongsPackage: com.markilue.java_learning.thread
|
||||
* @Author: dingjiawen
|
||||
* @CreateTime: 2022-09-16 16:05
|
||||
* @Description:
|
||||
* TODO 线程之间的通信:
|
||||
* 当线程之间彼此额操作具有先后顺序,比如线程A做包子,线程B吃包子,需要在A做完以后B在做时,两个线程之间需要怎么进行通信呢? ->等待唤醒机制
|
||||
* 等待唤醒机制:就是在一个线程进行了规定操作后,就进入等待状态wait(), 等待其他线程执行完他们的指定代码过后 再将其唤醒notify();
|
||||
* 在有多个线程进行等待时, 如果需要,可以使用 notifyAll()来唤醒所有的等待线程。wait/notify 就是线程间的一种协作机制。
|
||||
* 1) wait:线程不再活动,不再参与调度,进入 wait set 中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时的线程状态即是 WAITING。它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中
|
||||
* 2) notify:则选取所通知对象的 wait set 中的一个线程释放;
|
||||
* 3) notifyAll:则释放所通知对象的 wait set 上的全部线程。
|
||||
* 需要注意的是:并不是notify以后就一定直接从wait的地方恢复运行,而是需要看是否存在同步锁的竞争问题,只有不存在或者其他线程的该同步锁代码运行完,才恢复
|
||||
* 如果能获取锁,线程就从 WAITING 状态变成 RUNNABLE(可运行) 状态;
|
||||
* 否则,线程就从 WAITING 状态又变成 BLOCKED(等待锁) 状态
|
||||
* 还需要注意的是:
|
||||
* 1. wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。
|
||||
* 2. wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。
|
||||
* 3. wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为:必须要通过锁对象调用这2个方法。
|
||||
* @Version: 1.0
|
||||
*/
|
||||
public class ThreadCommunicate {
|
||||
|
||||
public static void main(String[] args) {
|
||||
//等待唤醒案例
|
||||
BaoZi bz = new BaoZi();
|
||||
|
||||
//这里使用同一个bz,同时在同步锁中也是加入bz,以此保证注意1、2、3
|
||||
ChiHuo ch = new ChiHuo("吃货",bz);
|
||||
BaoZiPu bzp = new BaoZiPu("包子铺",bz);
|
||||
|
||||
ch.start();
|
||||
bzp.start();
|
||||
|
||||
/*
|
||||
包子铺开始做包子
|
||||
包子造好了:厚皮牛肉大葱
|
||||
吃货来吃吧
|
||||
吃货正在吃:厚皮,牛肉大葱包子
|
||||
包子铺开始做包子
|
||||
包子造好了:薄皮蟹黄灌汤
|
||||
吃货来吃吧
|
||||
吃货正在吃:薄皮,蟹黄灌汤包子
|
||||
包子铺开始做包子
|
||||
包子造好了:厚皮牛肉大葱
|
||||
吃货来吃吧
|
||||
吃货正在吃:厚皮,牛肉大葱包子
|
||||
包子铺开始做包子
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static class BaoZi {
|
||||
String pier ;
|
||||
String xianer ;
|
||||
boolean flag = false ;//包子资源 是否准备好 包子资源状态
|
||||
}
|
||||
|
||||
public static class ChiHuo extends Thread{
|
||||
private BaoZi bz;
|
||||
|
||||
public ChiHuo(String name,BaoZi bz){
|
||||
super(name);
|
||||
this.bz = bz;
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
while(true){
|
||||
synchronized (bz){
|
||||
if(bz.flag == false){//没包子
|
||||
try {
|
||||
bz.wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("吃货正在吃:"+bz.pier+","+bz.xianer+"包子");
|
||||
bz.flag = false;
|
||||
bz.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class BaoZiPu extends Thread {
|
||||
|
||||
private BaoZi bz;
|
||||
|
||||
public BaoZiPu(String name,BaoZi bz){
|
||||
super(name);
|
||||
this.bz = bz;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
int count = 0;
|
||||
//造包子
|
||||
while(true){
|
||||
//同步
|
||||
synchronized (bz){
|
||||
if(bz.flag == true){//包子资源 存在
|
||||
try {
|
||||
bz.wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// 没有包子 造包子
|
||||
System.out.println("包子铺开始做包子");
|
||||
if(count%2 == 0){
|
||||
// 薄皮 蟹黄包
|
||||
bz.pier = "薄皮";
|
||||
bz.xianer = "蟹黄灌汤";
|
||||
}else{
|
||||
// 厚皮 牛肉大葱
|
||||
bz.pier = "厚皮";
|
||||
bz.xianer = "牛肉大葱";
|
||||
}
|
||||
count++;
|
||||
|
||||
bz.flag=true;
|
||||
System.out.println("包子造好了:"+bz.pier+bz.xianer);
|
||||
System.out.println("吃货来吃吧");
|
||||
//唤醒等待线程 (吃货)
|
||||
bz.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
package com.markilue.java_learning.thread;
|
||||
|
||||
/**
|
||||
* @BelongsProject: java_learning
|
||||
* @BelongsPackage: com.markilue.java_learning.thread
|
||||
* @Author: dingjiawen
|
||||
* @CreateTime: 2022-09-16 16:05
|
||||
* @Description:
|
||||
* TODO 线程之间的通信:等待唤醒机制之多对多问题:生产者消费者问题
|
||||
* 案例:有家餐馆的取餐口比较小,只能放10份快餐,厨师做完快餐放在取餐口的工作台上,服务员从这个工作台取出快餐给顾客。现在有多个厨师和多个服务员。
|
||||
* @Version: 1.0
|
||||
*/
|
||||
public class ThreadCommunicate1 {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Workbench bench = new Workbench();
|
||||
Cook c1 = new Cook("大拿",bench);
|
||||
Cook c2 = new Cook("吉祥",bench);
|
||||
Waiter w1 = new Waiter("翠花",bench);
|
||||
Waiter w2 = new Waiter("如意",bench);
|
||||
|
||||
c1.start();
|
||||
c2.start();
|
||||
w1.start();
|
||||
w2.start();
|
||||
/*
|
||||
大拿厨师制作了一份快餐,现在工作台上有:1份快餐
|
||||
大拿厨师制作了一份快餐,现在工作台上有:2份快餐
|
||||
大拿厨师制作了一份快餐,现在工作台上有:3份快餐
|
||||
大拿厨师制作了一份快餐,现在工作台上有:4份快餐
|
||||
大拿厨师制作了一份快餐,现在工作台上有:5份快餐
|
||||
如意服务员取走了一份快餐,现在工作台上有:4份快餐
|
||||
如意服务员取走了一份快餐,现在工作台上有:3份快餐
|
||||
翠花服务员取走了一份快餐,现在工作台上有:2份快餐
|
||||
吉祥厨师制作了一份快餐,现在工作台上有:3份快餐
|
||||
吉祥厨师制作了一份快餐,现在工作台上有:4份快餐
|
||||
吉祥厨师制作了一份快餐,现在工作台上有:5份快餐
|
||||
翠花服务员取走了一份快餐,现在工作台上有:4份快餐
|
||||
翠花服务员取走了一份快餐,现在工作台上有:3份快餐
|
||||
翠花服务员取走了一份快餐,现在工作台上有:2份快餐
|
||||
翠花服务员取走了一份快餐,现在工作台上有:1份快餐`
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
public static class Workbench {
|
||||
private static final int MAX_VALUE = 10;
|
||||
private int num;
|
||||
public synchronized void put() {
|
||||
while(num >= MAX_VALUE){
|
||||
try {
|
||||
this.wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
//加入睡眠时间是放大问题现象,去掉同步和wait等,可观察问题
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
num++;
|
||||
System.out.println(Thread.currentThread().getName()+ "厨师制作了一份快餐,现在工作台上有:" + num + "份快餐");
|
||||
this.notifyAll();
|
||||
}
|
||||
public synchronized void take() {
|
||||
while(num <=0){
|
||||
try {
|
||||
this.wait();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
//加入睡眠时间是放大问题现象,去掉同步和wait等,可观察问题
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
num--;
|
||||
System.out.println(Thread.currentThread().getName()+"服务员取走了一份快餐,现在工作台上有:" + num + "份快餐");
|
||||
this.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class Waiter extends Thread{
|
||||
private Workbench workbench;
|
||||
|
||||
public Waiter(String name,Workbench workbench) {
|
||||
super(name);
|
||||
this.workbench = workbench;
|
||||
}
|
||||
|
||||
public void run(){
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
workbench.take();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class Cook extends Thread{
|
||||
private Workbench workbench;
|
||||
|
||||
public Cook(String name,Workbench workbench) {
|
||||
super(name);
|
||||
this.workbench = workbench;
|
||||
}
|
||||
|
||||
public void run(){
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
workbench.put();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ public class ThreadDemo1 {
|
|||
//当main打印到5之后,需要等join进来的线程停止后才会继续了。
|
||||
if(i==5){
|
||||
try {
|
||||
//join以后,如果这个线程不停,程序就不会往下运行,所以start是开始运行,join以后是必须等他运行完再往后运行
|
||||
t.join();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
package com.markilue.java_learning.thread;
|
||||
|
||||
/**
|
||||
* @BelongsProject: java_learning
|
||||
* @BelongsPackage: com.markilue.java_learning.thread
|
||||
* @Author: dingjiawen
|
||||
* @CreateTime: 2022-09-16 14:42
|
||||
* @Description:
|
||||
* TODO 线程安全问题演示:
|
||||
* 电影院要卖票,我们模拟电影院的卖票过程。假设要播放的电影是 “葫芦娃大战奥特曼”,本次电影的座位共100个
|
||||
* (本场电影只能卖100张票)。
|
||||
* 我们来模拟电影院的售票窗口,实现多个窗口同时卖 “葫芦娃大战奥特曼”这场电影票(多个窗口一起卖这100张票)
|
||||
*
|
||||
*
|
||||
* @Version: 1.0
|
||||
*/
|
||||
public class ThreadSafe {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 创建线程任务对象
|
||||
Ticket ticket = new Ticket();
|
||||
// 创建三个窗口对象
|
||||
Thread t1 = new Thread(ticket, "窗口1");
|
||||
Thread t2 = new Thread(ticket, "窗口2");
|
||||
Thread t3 = new Thread(ticket, "窗口3");
|
||||
// 同时卖票
|
||||
t1.start();
|
||||
t2.start();
|
||||
t3.start();
|
||||
/*
|
||||
线程安全问题在于:多个线程在操作同一个共享变量时,可能出现多重篡改的问题
|
||||
窗口1正在卖:5
|
||||
窗口3正在卖:4
|
||||
窗口2正在卖:5
|
||||
窗口2正在卖:3
|
||||
窗口3正在卖:1
|
||||
窗口1正在卖:2
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
//第一步 创建资源类,在资源类中定义属性和方法
|
||||
public static class Ticket implements Runnable {
|
||||
private int ticket = 100;
|
||||
|
||||
//第二步,在资源操作方法中进行判断和干活
|
||||
private void saleTicket(){
|
||||
|
||||
|
||||
|
||||
|
||||
if (ticket > 0) { // 有票可以卖
|
||||
// 出票操作
|
||||
// 使用sleep模拟一下出票时间
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// 获取当前线程对象的名字
|
||||
String name = Thread.currentThread().getName();
|
||||
System.out.println(name + "正在卖:" + ticket--);
|
||||
}else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 执行卖票操作
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
// 每个窗口卖票的操作
|
||||
// 窗口永远开启
|
||||
while (true) {
|
||||
saleTicket();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
package com.markilue.java_learning.thread;
|
||||
|
||||
/**
|
||||
* @BelongsProject: java_learning
|
||||
* @BelongsPackage: com.markilue.java_learning.thread
|
||||
* @Author: dingjiawen
|
||||
* @CreateTime: 2022-09-16 14:52
|
||||
* @Description: TODO 线程安全问题演示:
|
||||
* 第二种情况演示 :使用一个static变量ticket来作为共享变量,放入常量池中,因此new ticket操作的都是同一个ticket变量
|
||||
* <p>
|
||||
* TODO 解决线程安全问题的方法:同步代码块或者同步方法或者锁机制(暂时不管)
|
||||
* 1)同步代码块:
|
||||
* synchronized (需要锁住的东西如 ThreadSafe1.class){需要锁的代码
|
||||
* }
|
||||
* 2)同步方法:
|
||||
* 在方法名前加synchronized关键字
|
||||
* 如public synchronized void method(){
|
||||
* 可能会产生线程安全问题的代码
|
||||
* }
|
||||
* 同步方法的锁对象:
|
||||
* (1)静态方法:当前类的Class对象
|
||||
* (2)非静态方法:this
|
||||
* 所以当需要创建多个对象时,需要锁住的方法必须使用static关键字
|
||||
* @Version: 1.0
|
||||
*/
|
||||
public class ThreadSafe1 {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Ticket t1 = new Ticket("窗口一");
|
||||
Ticket t2 = new Ticket("窗口二");
|
||||
Ticket t3 = new Ticket("窗口三");
|
||||
|
||||
t1.start();
|
||||
t2.start();
|
||||
t3.start();
|
||||
/*
|
||||
窗口一正在卖:10
|
||||
窗口二正在卖:10
|
||||
窗口三正在卖:9
|
||||
窗口一正在卖:7
|
||||
窗口二正在卖:8
|
||||
窗口三正在卖:5
|
||||
窗口一正在卖:4
|
||||
窗口二正在卖:6
|
||||
窗口二正在卖:3
|
||||
窗口三正在卖:3
|
||||
窗口一正在卖:3
|
||||
窗口三正在卖:1
|
||||
窗口二正在卖:0
|
||||
窗口一正在卖:2
|
||||
|
||||
加了同步代码块以后,连顺序都是对的:
|
||||
窗口二正在卖:47
|
||||
窗口二正在卖:46
|
||||
窗口二正在卖:45
|
||||
窗口三正在卖:44
|
||||
窗口三正在卖:43
|
||||
窗口三正在卖:42
|
||||
窗口一正在卖:41
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
public static class Ticket extends Thread {
|
||||
private static int ticket = 100;
|
||||
|
||||
public Ticket() {
|
||||
super();
|
||||
}
|
||||
|
||||
public Ticket(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
/*
|
||||
* 执行卖票操作
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
// 每个窗口卖票的操作
|
||||
// 窗口永远开启
|
||||
while (true) {
|
||||
|
||||
synchronized (Ticket.class) { //这里不能选用this作为锁,因为这几个线程的this不是同一个,如果是同一个ticket对象才可以使用
|
||||
|
||||
if (ticket > 0) { // 有票可以卖
|
||||
// 出票操作
|
||||
// 使用sleep模拟一下出票时间
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// 获取当前线程对象的名字
|
||||
System.out.println(getName() + "正在卖:" + ticket--);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue