多线程(四) ~ wait,join,sleep及单例与工厂模式
文章目录
- 一. wait 与 notify
- (一) 定义
- (二) 使用
- 二. wait与join及sleep的区别
- 三. 单例模式(安全写法)
- (一) 饿汉模式
- (二) 懒汉模式
- (1) 原始版本
- (2) 解决非原子性问题
- (3) 解决加锁后导致的效率低问题
- (4) 解决指令重排序与内存可见性问题
- 四. 工厂模式
一. wait 与 notify
(一) 定义
首先我们要明白, 为什么要有这个wait 与 notify方法?
答: 1. 在程序运行过程中, 操作系统内核对于多线程是随机调度执行的, 如果多个线程竞争同一把锁的时候, 会触发阻塞等待, 这时如果竞争到锁的线程1还没有做好要执行任务的准备,或者时机还不成熟, 不轮到他做, 那么就会一直等到时机成熟后才会继续执行从而释放锁, 同时, 由于锁的阻塞效果, 其他与该线程1竞争同一把锁的其他线程就会也一直陪着线程1一起等待, 而这显然不是我们想要的, 这是因为其他线程可能早就做好准备了, 就等锁一获取就能背起行囊立即出发, 但偏偏有线程1拉了后腿, 如果我们让所有要竞争同一把锁的所有线程都等着线程1, 那其他线程就会迟迟吃不到CPU资源, 导致线程饿死, 就与鸟妈妈喂鸟宝宝吃虫子一样, 不能因为一个小鸟不吃饭就饿死整窝鸟宝宝~
- 为了规避这种没做好准备 / 时机不成熟 的线程先抢到锁之后, 啥事也不做, 就干等着导致的线程饿死, wait 的作用就体现出来了, 线程1先抢到锁之后, 时机不成熟, 那么就会进入 wait 方法, 进入后会先释放锁然后陷入阻塞, 阻塞到时机成熟后我们用 notify将其唤醒, 从而继续把锁加上, 执行线程中的任务
(二) 使用
注意: wait 与 notify实在Object类中定义的方法, 等于任何一个对象都可以调用这两个方法, 而锁也是任何一个对象都可以为锁对象, 换而言之, wait 与 notify 只有在加锁情况下且是同一个锁对象调用的方法才会起到唤醒对应wait的方法
package test;import java.util.Scanner;/*** Created with IntelliJ IDEA.* Description:* User: ran* Date: 2025-08-02* Time: 16:59*/
public class test6 {public static void main(String[] args) throws InterruptedException {Object lock = new Object();Thread thread = new Thread(() -> {synchronized (lock) {try {System.out.println("获取锁, 进入wait方法等待!");lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread.start();Thread.sleep(1);System.out.println("输入任意字符唤醒线程:");Scanner scanner = new Scanner(System.in);scanner.next();synchronized (lock) {lock.notify();}}
}
二. wait与join及sleep的区别
wait | join | sleep |
---|---|---|
必须搭配锁来使用 | 不需要锁 | 不需要锁 |
可被notify提前唤醒 | 可以被interrupt方法提前唤醒 | 可以被interrupt方法提前唤醒 |
提供了等待时间的参数版本 | 提供了等待时间的参数版本 | 提供了等待时间的参数版本 |
在线程中,synchronized中使用时wait等待期间会释放锁 | 不能在未定义完成的线程中使用 | 在线程中,synchronized中使用时sleep沉睡期间不会释放锁 |
任意对象都可调用wait方法 | 只能由线程对象调用 | 由类对象Thread调用 |
异常终止会抛出InterruptedException异常 | 异常终止会抛出InterruptedException异常 | 异常终止会抛出InterruptedException异常 |
三. 单例模式(安全写法)
单例模式指的就是将构造方法私有化, 从而在其他类当中不能直接实例化单例对象, 只能通过对应方法来获取对象, 而获取的对象则是一个对象
(一) 饿汉模式
饿, 在程序意味着慌不择食, 饿怕了, 所以一有机会我就申请一个资源, 不管我用不用得到, 饿汉模式就是程序运行时就直接实例化一个对象, 不管外部是否调用, 毋庸置疑有时候会导致资源浪费, 但是因为只涉及到读操作, 所以天然线程安全
package test;/*** Created with IntelliJ IDEA.* Description:* User: ran* Date: 2025-08-02* Time: 17:45*/
public class Single {private static Single single = new Single();private Single() {}public static Single getSingle() {return single;}
}
class test {public static void main(String[] args) {Single single1 = Single.getSingle();Single single2 = Single.getSingle();System.out.println(single1.equals(single2));}
}
(二) 懒汉模式
懒在计算机中意味着高效, 是褒义词, 用到了我才申请资源创建一个实例, 没人调用那么我事不关己高高挂起, 懒汉模式也是如此, 因为懒汉模式的对象实在用到的时候才创建, 涉及到写操作, 所以在多线程中, 我们要解决线程安全问题
(1) 原始版本
package test;/*** Created with IntelliJ IDEA.* Description:* User: ran* Date: 2025-08-02* Time: 17:55*/
public class SingleLazy {private static SingleLazy singleLazy;private SingleLazy() {}public static SingleLazy getSingleLazy() {if (singleLazy == null) {return new SingleLazy();}return singleLazy;}
}
(2) 解决非原子性问题
在涉及到判断与写操作的外面加上锁
package test;/*** Created with IntelliJ IDEA.* Description:* User: ran* Date: 2025-08-02* Time: 17:55*/
public class SingleLazy {private static SingleLazy singleLazy;private static Object lock = new Object();private SingleLazy() {}public static SingleLazy getSingleLazy() {synchronized (lock) {if (singleLazy == null) {return new SingleLazy();}return singleLazy;}}
}
(3) 解决加锁后导致的效率低问题
在锁的外层再加上一层判断语句, 当singleLazy != null, 那么不进入竞争锁阻塞环节, 直接返回singleLazy 对象
package test;/*** Created with IntelliJ IDEA.* Description:* User: ran* Date: 2025-08-02* Time: 17:55*/
public class SingleLazy {private static SingleLazy singleLazy;private static Object lock = new Object();private SingleLazy() {}public static SingleLazy getSingleLazy() {if (singleLazy == null) {synchronized (lock) {if (singleLazy == null) {return new SingleLazy();}}}return singleLazy;}
}
(4) 解决指令重排序与内存可见性问题
在涉及到修改的变量前加上volatile 关键字
package test;/*** Created with IntelliJ IDEA.* Description:* User: ran* Date: 2025-08-02* Time: 17:55*/
public class SingleLazy {private volatile static SingleLazy singleLazy;private static Object lock = new Object();private SingleLazy() {}public static SingleLazy getSingleLazy() {if (singleLazy == null) {synchronized (lock) {if (singleLazy == null) {return new SingleLazy();}}}return singleLazy;}
}
四. 工厂模式
有时我们在实现构造方法的时候, 我们会需要多个同种类型的参数来表示不同的意义, 这时传统的构造方法已经不能满足我们了, 例如: 我们表示一个点的位置时, 可以用直角坐标系x , y 也可以用极坐标系r , a ,这时因为他们都是double类型数据, 写成两个构造方法不能重载而产生冲突, 于是引入了工厂模式, 通过另一个工厂类构造静态方法来返回需要实例化确产生冲突的对象
package demo_thread;/*** Created with IntelliJ IDEA.* Description:* User: 32309* Date: 2025-07-20* Time: 21:34*/
class Point {public double row;public double col;
}class Factory {public static Point makePointByXOrY(double x, double y) {Point point = new Point();// 通过x与y来给point进行属性配置,直角坐标系point.row = x;point.col = y;return point;}public static Point makePointByROrA(double r, double a) {Point point = new Point();// 通过r与a进行属性配置,极坐标point.row = r;point.col = a;return point;}
}
public class demo28_factory {public static void main(String[] args) {Point point1 = Factory.makePointByROrA(2.1,0.5);Point point2 = Factory.makePointByXOrY(2,3);}
}