多线程学习

This commit is contained in:
dingjiawen 2022-09-16 17:32:23 +08:00
parent 9afc59fc65
commit dfa5a3299c
6 changed files with 542 additions and 0 deletions

View File

@ -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("再给钱");
}
}
}
}
}

View File

@ -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,以此保证注意123
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();
}
}
}
}
}

View File

@ -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();
}
}
}
}

View File

@ -30,6 +30,7 @@ public class ThreadDemo1 {
//当main打印到5之后需要等join进来的线程停止后才会继续了 //当main打印到5之后需要等join进来的线程停止后才会继续了
if(i==5){ if(i==5){
try { try {
//join以后如果这个线程不停程序就不会往下运行所以start是开始运行join以后是必须等他运行完再往后运行
t.join(); t.join();
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();

View File

@ -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();
}
}
}
}

View File

@ -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;
}
}
}
}
}
}