首页
关于
友链
Search
1
wlop 4K 壁纸 4k8k 动态 壁纸
1,468 阅读
2
Nacos持久化MySQL问题-解决方案
932 阅读
3
Docker搭建Typecho博客
751 阅读
4
滑动时间窗口算法
728 阅读
5
Nginx反向代理微服务配置
699 阅读
生活
解决方案
JAVA基础
JVM
多线程
开源框架
数据库
前端
分布式
框架整合
中间件
容器部署
设计模式
数据结构与算法
安全
开发工具
百度网盘
天翼网盘
阿里网盘
登录
Search
标签搜索
java
javase
docker
java8
springboot
thread
spring
分布式
mysql
锁
linux
redis
源码
typecho
centos
git
map
RabbitMQ
lambda
stream
少年
累计撰写
189
篇文章
累计收到
24
条评论
首页
栏目
生活
解决方案
JAVA基础
JVM
多线程
开源框架
数据库
前端
分布式
框架整合
中间件
容器部署
设计模式
数据结构与算法
安全
开发工具
百度网盘
天翼网盘
阿里网盘
页面
关于
友链
搜索到
2
篇与
的结果
2022-04-21
安全发布对象,详解各种单例模式
安全发布对象什么是安全发布?发布对象:使一个对象能够被当前范围之外的代码所使用代码示例package com.yanxizhu.demo.concurrency.unSafePublish; import com.yanxizhu.demo.concurrency.annotation.UnThreadSafety; import lombok.extern.slf4j.Slf4j; import java.util.Arrays; /** * @description: 安全发布,这个例子是线程不安全的 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/4/21 21:45 * @version: 1.0 */ @Slf4j @UnThreadSafety public class UnSafePublish { private String[] states = {"a", "b", "c"}; public String[] getStates() { return states; } public static void main(String[] args) { UnSafePublish unSafePublish = new UnSafePublish(); log.info("{}", Arrays.toString(unSafePublish.getStates())); unSafePublish.getStates()[0] = "d"; log.info("{}", Arrays.toString(unSafePublish.getStates())); } }运行结果:21:48:00.633 [main] INFO com.yanxizhu.demo.concurrency.unSafePublish.UnSafePublish - [a, b, c] 21:48:00.636 [main] INFO com.yanxizhu.demo.concurrency.unSafePublish.UnSafePublish - [d, b, c]说明:上面代码,可以通过对象,调用getStates()方法获取数据,并可以外部直接修改里面的值。这样states的值是不安全的。对象溢出对象逸出:一种错误的发布。当一个对象还没有构造完成时,就使它被其他线程所见代码示例package com.yanxizhu.demo.concurrency.unSafePublish; import lombok.extern.slf4j.Slf4j; /** * @description: 对象溢出 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/4/21 21:53 * @version: 1.0 */ @Slf4j public class Escape { private int thisCanBeEascpe = 0; public Escape() { new InnerClass(); } private class InnerClass { public InnerClass() { log.info("{}", Escape.this.thisCanBeEascpe); } } public static void main(String[] args) { new Escape(); } }输出结果:21:55:01.416 [main] INFO com.yanxizhu.demo.concurrency.unSafePublish.Escape - 0说明:Escape对象还没构造完,就被new InnerClass()线程使用Escape.this.thisCanBeEascpe。安全发布对象4种方法在静态初始化函数中初始化一个对象引用将对象的引用保存到volatile类型域或者AtomicReference对象中将对象的引用保存到某个正确构造对象的final类型域中将对象的引用保存到一个由锁保护的域中单例-懒汉模式代码实例,线程不按钮package com.yanxizhu.demo.concurrency.singleton; import com.yanxizhu.demo.concurrency.annotation.UnThreadSafety; /** * @description: 单例-懒汉模式 * 在第一次使用时创建 * 单线程下没问题,多线程下可能出现问题 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/4/21 22:05 * @version: 1.0 */ @UnThreadSafety public class SingLetonLazyMode { //私有构造方法 private SingLetonLazyMode() {} //实例对象 private static SingLetonLazyMode instance = null; //静态工厂方法 public static SingLetonLazyMode getInstance() { //注意:多线程下问题,当2个线程同时判断到不为null,是都会调用私有构造方法,创建2个示例对象。 // 如果在构造方法中有对环境一些设置,2次执行会出现一些错误 if (null == instance) { instance = new SingLetonLazyMode(); } return instance; } }说明:上面的单例-懒汉模式是线程不安全的。多线程下问题,当2个线程同时判断到不为null,是都会调用私有构造方法,创建2个示例对象。如果在构造方法中有对环境一些设置,2次执行会出现一些错误。单例-饿汉模式代码实例,线程按钮package com.yanxizhu.demo.concurrency.singleton; import com.yanxizhu.demo.concurrency.annotation.ThreadSafety; /** * @description: 单例-饿汉模式 * 类装载时创建,线程安全的 * 不足: 1、如果私有构造方法里面有过多的设置,会加载很慢 * 2、每次都会创建示例对象,造成资源浪费 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/4/21 22:20 * @version: 1.0 */ @ThreadSafety public class SingLetonHungryManMode { private SingLetonHungryManMode() {} private static SingLetonHungryManMode instance = new SingLetonHungryManMode(); public static SingLetonHungryManMode getInstance() { return instance; } }说明:单例-饿汉模式,类装载时创建,线程安全的。不足地方:1、如果私有构造方法里面有过多的设置,会加载很慢。2、每次都会创建示例对象,造成资源浪费。使用需要考虑:私有构造方法里面是否有过多设置,创建对象是否被使用。单例-懒汉模式改进(synchronized)代码实例,线程安全,但是不推荐使用,性能不太好package com.yanxizhu.demo.concurrency.singleton; import com.yanxizhu.demo.concurrency.annotation.ThreadSafety; import com.yanxizhu.demo.concurrency.annotation.UnRecommend; /** * @description: 单例-懒汉模式(synchronized保证线程保证线程安全) * 在第一次使用时创建 * 单线程下没问题,多线程下通过synchronized保证线程只被执行一次,线程安全 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/4/21 22:05 * @version: 1.0 */ @ThreadSafety @UnRecommend public class SingLetonLazyModeSynchronized { //私有构造方法 private SingLetonLazyModeSynchronized() {} //实例对象 private static SingLetonLazyModeSynchronized instance = null; //静态工厂方法 public static synchronized SingLetonLazyModeSynchronized getInstance() { //注意:多线程下问题,当2个线程同时判断到不为null,是都会调用私有构造方法,创建2个示例对象。 // 如果在构造方法中有对环境一些设置,2次执行会出现一些错误 if (null == instance) { instance = new SingLetonLazyModeSynchronized(); } return instance; } }说明:synchronized保证线程只能同时被执行一次,但是带来了性能上的开销。因此是线程安全但不推荐的写法。单例-懒汉模式改进(双重同步锁单例)代码实例,线程不安全的package com.yanxizhu.demo.concurrency.singleton; import com.yanxizhu.demo.concurrency.annotation.UnThreadSafety; /** * @description: 单例-懒汉模式(双重同步锁单例) * 线程不安全 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/4/21 22:05 * @version: 1.0 */ @UnThreadSafety public class SingLetonLazyModeSynchronized { //私有构造方法 private SingLetonLazyModeSynchronized() {} //实例对象 private static SingLetonLazyModeSynchronized instance = null; //静态工厂方法 public static SingLetonLazyModeSynchronized getInstance() { if (null == instance) { //双重检测机制 //当第一个线程执行后,已经实例化一个对象了,第二个线程再次判断兑现是否实例。 //线程不安全问题所在: // synchronized(SingLetonLazyModeSynchronized.class) { //同步锁 if(null == instance) { instance = new SingLetonLazyModeSynchronized(); } } } return instance; } }说明:instance = new SingLetonLazyModeSynchronized();分为3步骤。1、分配对象的内存空间。2、初始化对象。3、设置instance指向刚分配的内存。问题分析:多线程情况下,上面3步,可能发生指令重排。JVM和CPU优化,发生了指令重排。因为指令2和3没有直接关联,所以上面3步指令可能优化重排成:1、分配对象的内存空间。3、设置instance指向刚分配的内存。2、初始化对象。当第一个线程执行instance = new SingLetonLazyModeSynchronized();设置对象指向刚分配的内存地址,还没执行2初始化对象时,第二个线程判断对象的执行步为空,直接返回对象,此时由于2初始化对象还没执行,导致调用对象报错。单列-懒汉模式(volatile+双重检测机制)最终版代码实例,线程安全,懒汉模式最终版package com.yanxizhu.demo.concurrency.singleton; import com.yanxizhu.demo.concurrency.annotation.ThreadSafety; import com.yanxizhu.demo.concurrency.annotation.UnRecommend; /** * @description: 单例-懒汉模式(volatile+双重检测机制单列) 线程安全 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/4/21 22:05 * @version: 1.0 */ @ThreadSafety public class SingLetonLazyModeSynchronizedDoubleSyncVolatile { //私有构造方法 private SingLetonLazyModeSynchronizedDoubleSyncVolatile() {} //实例对象 //volatile禁止指令重排 private volatile static SingLetonLazyModeSynchronizedDoubleSyncVolatile instance = null; //静态工厂方法 public static SingLetonLazyModeSynchronizedDoubleSyncVolatile getInstance() { if (null == instance) { //双重检测机制 synchronized (SingLetonLazyModeSynchronizedDoubleSyncVolatile.class) { //同步锁 if (null == instance) { instance = new SingLetonLazyModeSynchronizedDoubleSyncVolatile(); } } } return instance; } }说明:volatile+双重检测机制,volatile禁止指令重排,双重检测,加同步锁,保证多线程下线程安全。单例-饿汉模式(静态代码块写法)代码实例,错误的,会发生控制在异常package com.yanxizhu.demo.concurrency.singleton; import com.yanxizhu.demo.concurrency.annotation.ThreadSafety; /** * @description: 单例-饿汉模式,静态代码写法 * 注意:静态代码和静态域的顺序,不然会导致空指针 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/4/21 22:20 * @version: 1.0 */ @ThreadSafety public class SingLetonHungryManModeStatic { private SingLetonHungryManModeStatic() {} //静态代码块 static{ instance = new SingLetonHungryManModeStatic(); } //静态作用域 private static SingLetonHungryManModeStatic instance = null; public static SingLetonHungryManModeStatic getInstance() { return instance; } public static void main(String[] args) { System.out.println(getInstance().hashCode()); System.out.println(getInstance().hashCode()); } }输出结果:Exception in thread "main" java.lang.NullPointerException at com.yanxizhu.demo.concurrency.singleton.SingLetonHungryManModeStatic.main(SingLetonHungryManModeStatic.java:30)错误原因:静态代码和静态域的顺序,导致空指针代码调整后,正确代码package com.yanxizhu.demo.concurrency.singleton; import com.yanxizhu.demo.concurrency.annotation.ThreadSafety; /** * @description: 单例-饿汉模式,静态代码写法 * 注意:静态代码和静态域的顺序,不然会导致空指针 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/4/21 22:20 * @version: 1.0 */ @ThreadSafety public class SingLetonHungryManModeStatic { private SingLetonHungryManModeStatic() {} //静态作用域 private static SingLetonHungryManModeStatic instance = null; //静态代码块 static{ instance = new SingLetonHungryManModeStatic(); } public static SingLetonHungryManModeStatic getInstance() { return instance; } public static void main(String[] args) { System.out.println(getInstance().hashCode()); System.out.println(getInstance().hashCode()); } }输出结果:75457651 75457651单例-枚举模式线程安全最终推荐版本时枚举模式的单例。示例代码:package com.yanxizhu.demo.concurrency.singleton; import com.yanxizhu.demo.concurrency.annotation.Recommend; import com.yanxizhu.demo.concurrency.annotation.ThreadSafety; /** * @description: 单例-枚举模式,线程最安全的,推荐使用的 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/4/21 23:11 * @version: 1.0 */ @ThreadSafety @Recommend public class SingLetonEnum { //私有构造方法 private SingLetonEnum() {}; public static SingLetonEnum getInstance() { return SingLeton.INSTANCE.getSingLetonEnum(); }; private enum SingLeton { INSTANCE; private SingLetonEnum singLetonEnum; //JVM保证这个方法绝对只会被执行一次 SingLeton(){ singLetonEnum = new SingLetonEnum(); } public SingLetonEnum getSingLetonEnum() { return singLetonEnum; } } }说明:枚举的构造方法,JVM保证这个方法绝对只会被执行一次。线程安全,推荐使用的单例模式-枚举模式。比饿汉模式更能保证安全,比懒汉模式更能保证性能。枚举模式只有使用时调用一次,不会造成资源浪费。总结:饿汉模式:有2种写法,最后这种饿汉模式注意静态代码块和作用域顺序。懒汉模式:只有最终版(volatile+双重检测机制)正确,其它都存在线程安全或性能问题。单例模式,线程安全,最终推荐枚举模式。
2022年04月21日
206 阅读
0 评论
3 点赞
2022-03-10
JAVA多线程4种实现方式
异步与线程池1、初始化线程的4种方式1)、继承Thread2)、实现Runnable接口3)、实现Callable接口+FutureTask(可以拿到返回结果,可以处理异常)4)、线程池方式1和方式2:主进程无法获取线程的运算结果。不适合当前场景。方式3:主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。可以导致服务器资源耗尽。方式4:通过如下两种方式初始化线程池Executors.newFiexed ThreadPool(3); //或者 new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,TimeUnit unit, workQueue,threadFactory,handler);方式1、继续Thread/** * @description: 继承Thread * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 21:06 * @version: 1.0 */ public class MyThread { public static void main(String[] args) { System.out.println("启动main方法开始"); OneMyThread oneMyThread = new OneMyThread(); oneMyThread.start(); System.out.println("结束main方法"); } public static class OneMyThread extends Thread{ @Override public void run() { System.out.println("当前线程:"+Thread.currentThread().getId()); int i = 10/2; System.out.println("运行结果:"+i); } } }运行结果:启动main方法开始 结束main方法 当前线程:22 运行结果:5方式2、实现Runable接口/** * @description: 实现Runnable接口 * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 21:06 * @version: 1.0 */ public class MyThread { public static void main(String[] args) { System.out.println("启动main方法开始"); TwoMyThread twoMyThread = new TwoMyThread(); new Thread(twoMyThread).start(); System.out.println("结束main方法"); } /** * 实现Runnable接口 */ public static class TwoMyThread implements Runnable { @Override public void run() { System.out.println("当前线程:"+Thread.currentThread().getId()); int i = 10/2; System.out.println("运行结果:"+i); } } }运行结果:启动main方法开始 结束main方法 当前线程:22 运行结果:5方式3、实现Callable泛型接口/** * @description: 继承Thread * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 21:06 * @version: 1.0 */ public class MyThread { public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("启动main方法开始"); FutureTask<Integer> futureTask = new FutureTask<>(new ThreeMyThread()); new Thread(futureTask).start(); //等待线程执行完成,返回结果 Integer i = futureTask.get(); System.out.println("线程执行完,返回结果:"+i); System.out.println("结束main方法"); } /** * 实现Callable<T>泛型接口 */ public static class ThreeMyThread implements Callable<Integer> { @Override public Integer call() throws Exception { System.out.println("当前线程:"+Thread.currentThread().getId()); int i = 10/2; System.out.println("运行结果:"+i); return i; } } }运行结果:启动main方法开始 当前线程:22 运行结果:5 线程执行完,返回结果:5 结束main方法注意:结果的顺序,可以看到是一个阻塞等待通过FutureTask类源码可以看到,FutureTask不仅可以接受Callable还可以接收Runnable.FutureTask源码如下:public class FutureTask<V> implements RunnableFuture<V> { /** * Creates a {@code FutureTask} that will, upon running, execute the * given {@code Runnable}, and arrange that {@code get} will return the * given result on successful completion. * * @param runnable the runnable task * @param result the result to return on successful completion. If * you don't need a particular result, consider using * constructions of the form: * {@code Future<?> f = new FutureTask<Void>(runnable, null)} * @throws NullPointerException if the runnable is null */ public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable }FutureTask实现RunnableFuture接口:RunnableFuture最终是继承的Runnable。package java.util.concurrent; /** * A {@link Future} that is {@link Runnable}. Successful execution of * the {@code run} method causes completion of the {@code Future} * and allows access to its results. * @see FutureTask * @see Executor * @since 1.6 * @author Doug Lea * @param <V> The result type returned by this Future's {@code get} method */ public interface RunnableFuture<V> extends Runnable, Future<V> { /** * Sets this Future to the result of its computation * unless it has been cancelled. */ void run(); }方式4、直接提交线程池,线程池会自动开启任务。每次通过new Thread()创建线程问题:导致资源耗尽。以上三种启动方式都不用。应该将所有异步多线程任务交给线程池执行。线程池:package com.yanxizhu.family.booking; import java.util.concurrent.*; /** * @description: 继承Thread * @author: <a href="mailto:batis@foxmail.com">清风</a> * @date: 2022/3/10 21:06 * @version: 1.0 */ public class MyThread { //应保证,当前系统中线程池只有一两个,每个异步任务,提交给线程池自己执行。 public static ExecutorService executorService = Executors.newFixedThreadPool(10); public static void main(String[] args) throws ExecutionException, InterruptedException { System.out.println("启动main方法开始"); executorService.submit(new TwoMyThread()); //有返回值 //每个异步任务,提交给线程池自己执行。 executorService.execute(new TwoMyThread());//无返回值 System.out.println("结束main方法"); } /** * 实现Runnable接口 */ public static class TwoMyThread implements Runnable { @Override public void run() { System.out.println("当前线程:"+Thread.currentThread().getId()); int i = 10/2; System.out.println("运行结果:"+i); } } } 运行结果:启动main方法开始 结束main方法 当前线程:22 运行结果:5使用线程池带来的好处:1、降低资源的消耗通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗2、提高响应速度因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行3、提高线程的可管理性线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配总结区别1、直接继承Thread、实现Runnable接口:不能直接得到返回值。2、实现Callable泛型接口:可以得到返回值。3、继承Thread、实现Runnable接口、实现Callable泛型接口,都不能达到控制资源的效果。4、只有线程池可以控制资源,性能稳定。
2022年03月10日
171 阅读
0 评论
1 点赞