diff --git a/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/DeadLock.java b/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/DeadLock.java new file mode 100644 index 0000000..42bb8b1 --- /dev/null +++ b/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/DeadLock.java @@ -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("再给钱"); + } + } + } + } +} diff --git a/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/ThreadCommunicate.java b/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/ThreadCommunicate.java new file mode 100644 index 0000000..34caaa6 --- /dev/null +++ b/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/ThreadCommunicate.java @@ -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(); + } + } + } + } + + + +} diff --git a/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/ThreadCommunicate1.java b/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/ThreadCommunicate1.java new file mode 100644 index 0000000..ef674e3 --- /dev/null +++ b/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/ThreadCommunicate1.java @@ -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(); + } + } + } + + + + + + + + + + + + +} diff --git a/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/ThreadDemo1.java b/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/ThreadDemo1.java index 5d62249..eed5161 100644 --- a/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/ThreadDemo1.java +++ b/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/ThreadDemo1.java @@ -30,6 +30,7 @@ public class ThreadDemo1 { //当main打印到5之后,需要等join进来的线程停止后才会继续了。 if(i==5){ try { + //join以后,如果这个线程不停,程序就不会往下运行,所以start是开始运行,join以后是必须等他运行完再往后运行 t.join(); } catch (InterruptedException e) { e.printStackTrace(); diff --git a/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/ThreadSafe.java b/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/ThreadSafe.java new file mode 100644 index 0000000..6b82211 --- /dev/null +++ b/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/ThreadSafe.java @@ -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(); + } + } + } +} diff --git a/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/ThreadSafe1.java b/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/ThreadSafe1.java new file mode 100644 index 0000000..431d3b8 --- /dev/null +++ b/Big_data_example/java_learning/src/main/java/com/markilue/java_learning/thread/ThreadSafe1.java @@ -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变量 + *

+ * 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; + } + + } + + } + } + } +}