手写生产者与消费者
使用synchroinzed与ReentrantLock完成手写生产者与消费者
狂神说Java学习笔记
生产者和消费者 synchroinzed 版
/**
* Created by Xumx on 2022/6/14 20:25
*
* 生产者和消费者 synchroinzed 版
*
* 题目:现在两个线程,可以操作初始值为0的一个变量
* 实现一个线程对该变量 + 1,一个线程对该变量 -1
* 实现交替10次
*
* 诀窍:
* 1. 高内聚低耦合的前提下,线程操作资源类
* 2. 判断 、干活、通知
*/
public class Test1 {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
}
}
class Data{
private int number = 0;
public synchronized void increment() throws InterruptedException {
while (number!=0){
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"\t"+number);
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
while (number==0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"\t"+number);
this.notifyAll();
}
}
问题升级:4个线程,两个加,两个减
/**
* Created by Xumx on 2022/6/14 20:34
*
* synchroinzed 版
* 题目:现在四个线程,可以操作初始值为0的一个变量
* 实现两个线程对该变量 + 1,两个线程对该变量 -1
* 实现交替10次
*
* 诀窍:
* 1. 高内聚低耦合的前提下,线程操作资源类
* 2. 判断 、干活、通知
* 3. 多线程交互中,必须要防止多线程的虚假唤醒,也即(判断不能用if,只能用while)
*/
public class Test2 {
public static void main(String[] args) {
final Data data = new Data();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Data{
private int number = 0;
public synchronized void increment() throws InterruptedException {
while (number!=0){
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"\t"+number);
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
while (number==0){
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"\t"+number);
this.notifyAll();
}
}
ReentrantLock版生产者和消费者写法
/**
* Created by Xumx on 2022/6/14 20:48
*
* ReentrantLock版生产者和消费者写法
* 题目:现在四个线程,可以操作初始值为0的一个变量
* 实现两个线程对该变量 + 1,两个线程对该变量 -1
* 实现交替10次
* <p>
* 诀窍:
* 1. 高内聚低耦合的前提下,线程操作资源类
* 2. 判断 、干活、通知
* 3. 多线程交互中,必须要防止多线程的虚假唤醒,也即(判断不能用if,只能用while)
*/
public class Test3 {
public static void main(String[] args) {
Data data = new Data();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 1; i <= 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
class Data{
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void increment() throws InterruptedException {
lock.lock();
try {
while (number!=0){
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName()+"\t"+number);
condition.signalAll();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
while (number==0){
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName()+"\t"+number);
condition.signalAll();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
精确通知顺序访问
/**
* Created by Xumx on 2022/6/14 20:57
/**
* 题目:多线程之间按顺序调用,实现 A->B->C
* 三个线程启动,要求如下:
* AA 打印5次,BB 打印10次。CC打印15次,依次循环
*
* 重点:标志位
*/
public class Test4 {
public static void main(String[] args) {
Resources resources = new Resources();
new Thread(()->{
for (int i = 1; i <=10; i++) {
resources.print5();
}
},"AA").start();
new Thread(()->{
for (int i = 1; i <=10; i++) {
resources.print10();
}
},"BB").start();
new Thread(()->{
for (int i = 1; i <=10; i++) {
resources.print15();
}
},"CC").start();
}
}
class Resources{ // 资源类
private int number = 1; // 1A 2B 3C
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public void print5(){
lock.lock();
try {
// 判断
while (number!=1){
condition1.await();
}
// 干活
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName()+'\t'+i);
}
// 通知,指定的干活!
number = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print10(){
lock.lock();
try {
// 判断
while (number!=2){
condition2.await();
}
// 干活
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName()+'\t'+i);
}
// 通知,指定的干活!
number = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print15(){
lock.lock();
try {
// 判断
while (number!=3){
condition3.await();
}
// 干活
for (int i = 1; i <= 15; i++) {
System.out.println(Thread.currentThread().getName()+'\t'+i);
}
// 通知,指定的干活!
number = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
手写单例模式
饿汉式
/*
* 饿汉式
*
* 在Hungry类中,如果定义了四个byte数组,当代码一运行,这四个数组就被初始化,并且放入内存了,如
果长时间没有用到getInstance方法,不需要Hungry类的对象,这不是一种浪费吗?我希望的是 只有用
到了 getInstance方法,才会去初始化单例类,才会加载单例类中的数据。所以就有了 第二种单例模
式:懒汉式。
* */
public class Hungry {
private Hungry(){}
private final static Hungry hungry = new Hungry();
public static Hungry getInstance(){
return hungry;
}
}
DCL懒汉式
/*
* 懒汉模式
* lazyMan = new LazyMan();
不是原子性操作,至少会经过三个步骤:
1. 分配对象内存空间
2. 执行构造方法初始化对象
3. 设置instance指向刚分配的内存地址,此时instance !=null;
由于指令重排,导致A线程执行 lazyMan = new LazyMan();的时候,可能先执行了第三步(还没执行第
二步),此时线程B又进来了,发现lazyMan已经不为空了,直接返回了lazyMan,并且后面使用了返回
的lazyMan,由于线程A还没有执行第二步,导致此时lazyMan还不完整,可能会有一些意想不到的错
误,所以增加一个volatile关键字来避免指令重排。
private volatile static LazyMan lazyMan;
* */
public class LazyMan {
private LazyMan(){}
public static volatile LazyMan lazyMan = null;
public static LazyMan getLazyMan(){
if (lazyMan==null){
synchronized (LazyMan.class){
if (lazyMan==null){
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
//演示 反射可以破解当前方式的单例
public static void main(String[] args) throws Exception {
LazyMan lazyMan = LazyMan.getLazyMan();
Constructor<LazyMan> constructor = LazyMan.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
LazyMan lazyMan2 = constructor.newInstance();
System.out.println(lazyMan);
System.out.println(lazyMan2);
System.out.println(lazyMan==lazyMan2); // 所以反射是一个比较霸道的东西,无视private修饰的构造方法,可以直接在外面newInstance,破坏我们辛辛苦苦写的单例模式
}
}
静态内部类实现饿汉式
/*
* 还有这种方式是第一种饿汉式的改进版本,同样也是在类中定义static变量的对象,并且直接初始化,不
过是移到了静态内部类中,十分巧妙。既保证了线程的安全性,同时又满足了懒加载。
* */
public class LazyMan2 {
private LazyMan2(){}
public static LazyMan2 getLazyMan(){
return InnerClass.lazyMan;
}
private static class InnerClass{
public static final LazyMan2 lazyMan = new LazyMan2();
}
//演示 反射可以破解当前方式的单例
public static void main(String[] args) throws Exception {
LazyMan2 lazyMan = LazyMan2.getLazyMan();
Constructor<LazyMan2> constructor = LazyMan2.class.getDeclaredConstructor(null);
constructor.setAccessible(true);
LazyMan2 lazyMan2 = constructor.newInstance();
System.out.println(lazyMan);
System.out.println(lazyMan2);
System.out.println(lazyMan==lazyMan2); // 所以反射是一个比较霸道的东西,无视private修饰的构造方法,可以直接在外面newInstance,破坏我们辛辛苦苦写的单例模式
}
}
枚举实现饿汉式
public enum LazyMan3 {
INSTANCE;
public LazyMan3 getInstance(){
return INSTANCE;
}
public static void main(String[] args) throws Exception {
LazyMan3 instance1 = LazyMan3.INSTANCE;
LazyMan3 instance2 = LazyMan3.INSTANCE;
System.out.println(instance1);
System.out.println(instance2);
Constructor<LazyMan3> constructor = LazyMan3.class.getDeclaredConstructor(String.class,int.class);
constructor.setAccessible(true);
LazyMan3 instance3 = constructor.newInstance(); //Cannot reflectively create enum objects
System.out.println(instance3);
}
}