首页
归档
留言
友链
广告合作
壁纸
更多
美女主播
Search
1
博瑞GE车机升级/降级
5,643 阅读
2
Mac打印机设置黑白打印
5,040 阅读
3
修改elementUI中el-table树形结构图标
4,944 阅读
4
Mac客户端添加腾讯企业邮箱方法
4,704 阅读
5
intelliJ Idea 2022.2.X破解
4,428 阅读
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
登录
/
注册
Search
标签搜索
Spring Boot
Java
Vue
Mac
Spring Cloud
MyBatis
WordPress
MacOS
asp.net
Element UI
Nacos
MySQL
.Net
Spring Cloud Alibaba
Mybatis-Plus
Typecho
jQuery
Java Script
IntelliJ IDEA
微信小程序
Laughing
累计撰写
629
篇文章
累计收到
1,421
条评论
首页
栏目
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
页面
归档
留言
友链
广告合作
壁纸
美女主播
搜索到
629
篇与
的结果
2025-06-10
iPhone 14 plus初步体验IOS 26
苹果在今日凌晨一点召开的wwdc25上,苹果发布了其最新的系统版本,包括iOS、iPadOS、macOS、visionOS、watchOS,与之前网上曝光的一样,苹果统一了其操作系统版本,摒弃了传统的序号命名规则,统一使用年度命名,除此之外,苹果统一了其UI,抛弃了以往的扁平化风格,改为了液态玻璃的风格。我的手机、手表还有Mac也是第一时间完成了升级,接下来也是针对自己的亲身体验,简单的说一下当前版本存在的一些问题,各位看官可以看下,自己是否适合升级体验。这篇文章,主要说一下iPhone的使用体验吧。手机型号iPhone 14 plus 256g版本,购买于2022年10月,目前电池健康度84%。系统版本iOS 26.0 Beta1(23A5260n),也就是iOS 26第一个开发预览版。整体感官这里我说的是感官,给人视觉冲击力最大的就是目前的液态玻璃界面了,从功能上来讲其实个人感觉跟以前差不太多,因为苹果的AI至今没有进入国内(进入国内我这老手机也不支持了,嘻嘻)系统层面目前发现的问题主要说一下目前遇到的问题吧,方便大家判断是否适合自己升级。系统整体卡顿比较严重,不够流畅,非要对比的话,感觉跟我手上Mate 60 Pro安装的鸿蒙4.2版本差不多卡,这种卡顿可以说已经影响了使用的体验。系统整体发热严重,我不玩游戏,平时刷刷新闻啥的,发热已经很严重了,充电的时候,几乎可以用烫手形容了,带来的问题,就是耗电很快,原来使用iOS18基本上一天一充,现在可能一天得2~3冲了。很多地方没有汉化,比如电话、Safari浏览器等等,特别是电池界面,电池信息几乎没有汉化。暗黑模式,如果有文件夹,打开文件夹,在即将黑屏之前,偶发的整个界面背景变成红色(还是挺瘆人的)。新增主题,拉伸上面的时间时,会出现遮挡,不知道是否故意为之。解锁屏幕时,因为现在系统做了一些缩放的动画,存在掉帧的情况。系统弹出通知的动画不够流畅。应用不兼容的情况除了系统层面的问题,应用不兼容的情况也有。微信输入法无法用,不管是iOS还是macOS上,微信输入法都不能用。QQ、微信、支付宝、淘宝、京东、腾讯视频、高德地图等常见App目前没有发现不兼容的情况。以上就是今天一天使用下来的体验。
2025年06月10日
13 阅读
0 评论
0 点赞
2025-06-10
Mac Safari浏览器调整默认缩放级别
平时都是使用Mac自带的Safari浏览器,最近换了个5k的显示器,发现Safari浏览器显示的字体特别的小,看着特别难受,而且Safari浏览器没有像Google或者edge浏览器提供设置缩放的地方,如果想调整网页的缩放,只能在打开网站之后,通过command +、command -进行页面的缩放,但是每个网站都设置,显然是比较麻烦的。其实Safari浏览器也支持通过加载样式表的方式设置缩放。首先,我们在电脑任意位置创建一个css文件,文件名称任意,比如我创建了一个Safari_zoom.css的文件。将以下内容粘贴到文件中html { zoom: 1.25; }其中1.25代表我希望网页内容缩放到1.25倍,这个具体的值按需设置就可以了,只要自己看着舒服就行。然后打开Safari,找到左上角,点击Safari浏览器,选择设置切换到高级页签,在下面的样式表选择我们刚才创建的css文件,这样浏览器就能缩放了。当然,因为是通过css样式的方式调整的浏览器网页内容的缩放,所以这个方法只能缩放网页正文的内容,无法改变浏览器自身的显示。
2025年06月10日
9 阅读
0 评论
0 点赞
2025-06-06
druid discard long time none received connection解决办法
Spring Boot集成阿里巴巴druid时,如果你使用的是MySQL数据库,可能会在控制台不定时的输出以下警告信息虽然这个警告信息不会影响程序的正常运行,但是这种日志不仅会让人感到疑惑,心理上也会比较难受。网上出现这种问题的很多,也有很多人提供了类似修改环境变量的方式进行屏蔽,但是很多说的都比较含糊。问题分析通过日志,我们可以看到,警告信息是c.a.druid.pool.DruidAbstractDataSource这个类打印的,我们可以打开对应jar包的位置具体查看一下打印日志的方法经过分析,可以看到这个问题的根源在于Druid连接池对MySQL连接的处理逻辑。在DruidAbstractDataSource类的testConnectionInternal方法中,如果连接空闲时间超过60秒,Druid会认为该连接已经不再有效,并将其丢弃。{message type="warning" content="我这里出现问题的druid版本是1.2.4"/}解决办法这里主要介绍两种处理方式,方法一是升级版本,升级到一个不打印警告的版本即可,第二方法可以通过druid.mysql.usePingMethod系统属性禁用掉ping检测。方法一、升级版本具体哪个版本去掉的提示我没细查,我这里是升级到了1.2.23版本,可以确认这个版本不会打印这条警告信息,低于或者高于此版本都可能会再次打印这条警告信息。方法二、设置系统属性通过设置druid.mysql.usePingMethod=false来避免使用MySQL的Ping方法来检查连接有效性。我是通过在启动类main方法设置的public static void main(String[] args) { System.setProperty("druid.mysql.usePingMethod", "false");//去掉discard long time none received connection提示 SpringApplication.run(LeeFrameApplication.class, args); System.out.println("启动成功"); }{message type="info" content="⚠️设置属性值的方法一定要在run方法前面"/}
2025年06月06日
9 阅读
0 评论
0 点赞
2025-05-12
IDEA、Datagrip 2025.1 最新破解版安装教程(至2099年)
截止到本文发文时,IntelliJ IDEA最新版本是2025.1.1,Datagrip的最新版本是2025.1.2,本文介绍的破解教程都适用。安装最新版本软件直接在官网下载最新版本的(也可以参考我们上文介绍的版本)IntelliJ IDEA 或者Datagrip安装即可。下载破解文件通过以下百度网盘地址,下载最新的破解文件。{cloud title="jetbra.zip" type="bd" url="https://pan.baidu.com/s/1JU6J-e-Zh8WGIrfOvxksFg?pwd=yndh" password="yndh"/}下载完成后,解压到一个不包含空格及中文的路径下。里面有三个文件夹及两个文本文件:win2021-2015: 存放的是 Windows 系统对应的破解脚本;mac2021-2015: 苹果 Mac 系统对应的破解脚本;linux2021-2015: Linux 系统对应的破解脚本;Datagrip激活码.txt:对应Datagrip的激活码,破解完成后适用IDEA激活码.txt:对应IDEA的激活码,破解完成后适用执行激活Windows系统为例,进入到/win2020-2025文件夹,双击install-current-user.vbs 脚本,表示为当前系统用户执行破解。然后,点击弹框中的确定按钮,等待一会,弹出OK弹窗代表激活成功。# 激活激活成功后,打开IDEA或Datagrip。在激活页面,选择激活码,然后按照我们压缩包内提供的激活码选择激活至此,就完成了激活过程,有效期至2099年12月31日
2025年05月12日
44 阅读
0 评论
0 点赞
2025-05-09
LogicFlow节点实现呼吸闪烁效果
LogicFlow是滴滴开源的流程图编辑框架,提供了一系列流程图编辑和交互功能。我们这里主要实现某个节点呼吸闪烁效果。如下图蓝色的节点,我们假设当前节点是活动节点。一、定义样式我们借助animation属性实现动画效果。.breathing-border { animation: breathing 2s infinite; } @keyframes breathing { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }二、加载样式上面定义了样式之后,我们要做的就是将样式加载到我们的节点上。因为我这里使用的矩形节点,所以这里继承RectNode实现自己的规则。//自定义View class RectRadiusView extends RectNode { getShape() { // 获取XxxNodeModel中定义的形状属性 const { model } =this.props; const { x, y, width, height, radius, properties } =model; // 获取XxxNodeModel中定义的样式属性 const style=model.getNodeStyle(); const isActiveStep=properties.isActiveStep; return h('g', {} , [ h('rect', { ...style, x: x - width / 2, y: y - height / 2, width, height, rx: radius, ry: radius, className: isActiveStep ? 'breathing-border' : '', }), ]); } } // 自定义model class RectRadiusModel extends RectNodeModel { // 样式属性 getNodeStyle() { const style=super.getNodeStyle() const { fill } =this.properties style.fill=fill style.strokeWidth=0 return style } // 形状属性 initNodeData(data) { super.initNodeData(data) this.width=150 this.height=50 this.radius=25 } }扩展完自己的节点后,注册上lf.register({ type: "rectRadiusNode", view: RectRadiusView, model: RectRadiusModel })至此,就实现了我们节点的一个呼吸闪烁的效果。
2025年05月09日
14 阅读
0 评论
0 点赞
2025-04-24
Java线程组和线程优先级
一、线程组Java中ThreadGroup表示线程组,每个线程比如属于一个线程组,创建线程时,如果没有指定线程组,那么新创建的线程默认属于其父线程所在线程组。public class ThreadDemo { public static void main(String[] args) { Thread testThread = new Thread(() -> { System.out.println("测试线程组:" + Thread.currentThread().getThreadGroup().getName()); System.out.println("测试线程名:" + Thread.currentThread().getName()); }); testThread.start(); System.out.println("main线程名:" + Thread.currentThread().getName()); } }线程组管理其中的线程,线程组是一个标准的树状结构,这样涉及的原因是防止“上级”线程被“下级”线程引用而无法进行有效的GC回收。二、线程优先级Java中线程优先级是支持指定的,范围是1-10,但并不是所有操作系统都支持10级优先级,,Java只给操作系统一个优先级的参考量,线程在操作系统中最终的优先级还是有操作系统决定的。线程默认优先级是5,线程的执行顺序由调度程序来决定,通常情况下,高优先级的线程会比低优先级的线程有更高的机率得到执行。import java.util.stream.IntStream; public class ThreadDemo { public static void main(String[] args) { IntStream.range(1, 10).forEach(i -> { Thread thread = new Thread(() -> { System.out.println(String.format("当前线程名称%s,优先级%s", Thread.currentThread().getName(), Thread.currentThread().getPriority())); }); thread.setPriority(i); thread.start(); }); } }某次的输出结果当前线程名称Thread-5,优先级6 当前线程名称Thread-0,优先级1 当前线程名称Thread-1,优先级2 当前线程名称Thread-4,优先级5 当前线程名称Thread-7,优先级8 当前线程名称Thread-2,优先级3 当前线程名称Thread-8,优先级9 当前线程名称Thread-6,优先级7 当前线程名称Thread-3,优先级4Java提供⼀个线程调度器来监视和控制处于RUNNABLE状态的线程。线程的调度策略采⽤抢占式,优先级⾼的线程⽐优先级低的线程会有更⼤的⼏率优先执⾏。在优先级相同的情况下,按照“先到先得”的原则。每个Java程序都有⼀个默认的主线程,就是通过JVM启动的第⼀个线程main线程。线程组与线程都有优先级,当线程组内的线程优先级高于线程组的优先级时,系统会自动设置线程的优先级为线程组的优先级。public class ThreadDemo { static int i = 0; public static void main(String[] args) throws Exception { ThreadGroup threadGroup = new ThreadGroup("TestThreadGroup"); threadGroup.setMaxPriority(4); Thread thread = new Thread(threadGroup, () -> { System.out.println("测试线程优先级与线程组优先级"); }); thread.setPriority(6); System.out.println("线程实际优先级:" + thread.getPriority()); } }输出结果线程实际优先级:4三、守护线程如果某线程是守护线程,那如果所有的⾮守护线程结束,这个守护线程也会⾃动结束。守护线程优先级较低。⼀个线程默认是⾮守护线程,可以通过setDaemon(boolean on)来设置一个线程是否是守护线程。import java.util.stream.IntStream; public class ThreadDemo { static int i = 0; public static void main(String[] args) throws Exception { Thread thread = new Thread(() -> { while (true) { System.out.println("我还活着" + ++i); } }); thread.start(); System.out.println("main方法执行完成"); } }输出结果:main方法执行完成 我还活着1 我还活着2 ......引入thread线程默认不是守护线程,所以即使main线程结束后,thread线程也会一直循环下去。我们稍作稍作修改,把thread线程设置成非守护线程。import java.util.stream.IntStream; public class ThreadDemo { static int i = 0; public static void main(String[] args) throws Exception { Thread thread = new Thread(() -> { while (true) { System.out.println("我还活着" + ++i); } }); thread.setDaemon(true); thread.start(); //让main线程暂停10ms,防止守护线程未执行main线程就结束的情况 thread.sleep(10); System.out.println("main方法执行完成"); } }某次输出结果我还活着1 我还活着2 我还活着3 我还活着4 我还活着5 我还活着6 我还活着7 我还活着8 我还活着9 我还活着10 我还活着11 我还活着12 我还活着13 我还活着14 我还活着15 我还活着16 我还活着17 我还活着18 我还活着19 我还活着20 我还活着21 我还活着22 我还活着23 我还活着24 我还活着25 我还活着26 我还活着27 我还活着28 我还活着29 我还活着30 我还活着31 我还活着32 我还活着33 我还活着34 我还活着35 我还活着36 我还活着37 我还活着38 我还活着39 我还活着40 我还活着41 我还活着42 我还活着43 我还活着44 我还活着45 我还活着46 我还活着47 我还活着48 我还活着49 我还活着50 我还活着51 我还活着52 我还活着53 我还活着54 我还活着55 我还活着56 我还活着57 我还活着58 我还活着59 我还活着60 我还活着61 我还活着62 我还活着63 我还活着64 我还活着65 我还活着66 我还活着67 我还活着68 我还活着69 我还活着70 我还活着71 我还活着72 我还活着73 我还活着74 我还活着75 我还活着76 我还活着77 我还活着78 我还活着79 我还活着80 我还活着81 我还活着82 我还活着83 我还活着84 我还活着85 我还活着86 我还活着87 我还活着88 我还活着89 我还活着90 我还活着91 我还活着92 我还活着93 我还活着94 我还活着95 我还活着96 我还活着97 我还活着98 我还活着99 我还活着100 我还活着101 我还活着102 我还活着103 我还活着104 我还活着105 我还活着106 我还活着107 我还活着108 我还活着109 我还活着110 我还活着111 我还活着112 我还活着113 我还活着114 我还活着115 我还活着116 我还活着117 我还活着118 我还活着119 main方法执行完成 我还活着120 我还活着121 我还活着122 我还活着123 我还活着124可以看到main线程执行完成后,守护线程会自动关闭。
2025年04月24日
22 阅读
0 评论
0 点赞
2025-04-24
Java Callable接口、Future接口及FutureTask类用法
通常来讲,我们使用Runnable接口或者Thread类来创建一个新线程,但是他们有一个缺点,就是run方法没有返回值,如果我们需要开启线程后获得一个返回值,就可以使用Callable接口配合Future类来实现。一、Callable接口Callable⼀般是配合线程池⼯具ExecutorService来使⽤。ExecutorService可以使⽤submit⽅法来让⼀个Callable接⼝执⾏,它会返回⼀个Future,我们后续的程序可以通过这个Future的get⽅法得到结果。可以看一下下面的Demoimport java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class FutureDemo implements Callable<String> { @Override public String call() throws Exception { return "Hello,World"; } public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); FutureDemo futureDemo = new FutureDemo(); Future<String> tFuture = executorService.submit(futureDemo); try { System.out.println(tFuture.get()); } catch (Exception exception) { } } }运行后,控制台会输出Hello,World二、Future接口看一下Future接口的源代码public interface Future<V> { /** * 试图取消一个线程的执行,但是不一定能成功,因为任务可能已完成或已取消或其他因素不能取消 * * @param 是否允许打断正在执行的任务 * @return 是否取消成功 */ boolean cancel(boolean mayInterruptIfRunning); /** * 获取线程是否已取消 * * @return 线程完成前被取消返回true */ boolean isCancelled(); /** * 获取线程是否已完成 * * 如果线程异常、被终止或者取消,都会返回true * * @return 线程完成返回true */ boolean isDone(); /** * 获取线程返回值 * * @return 线程返回结果 */ V get() throws InterruptedException, ExecutionException; /** * 在给定时间内获取返回值,如果超时未获取到返回超时异常 * * @param 获取结果最大等待时间 * @param 时间计量单位 * @return 结果 */ V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }下面通过实际代码看一下Future接口的基本用法。修改上面代码如下import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class FutureDemo implements Callable<String> { @Override public String call() throws Exception { return "Hello,World"; } public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); FutureDemo futureDemo = new FutureDemo(); Future<String> tFuture = executorService.submit(futureDemo); // 判断线程是否完成 System.out.println("线程是否完成:" + tFuture.isDone()); // 判断线程是否取消 System.out.println("线程是否取消:" + tFuture.isCancelled()); try { System.out.println(tFuture.get()); // 判断线程是否完成 System.out.println("线程是否完成:" + tFuture.isDone()); // 判断线程是否取消 System.out.println("线程是否取消:" + tFuture.isCancelled()); } catch (Exception exception) { } } } 输出结果线程是否完成:false 线程是否取消:false Hello,World 线程是否完成:true 线程是否取消:false可以看到,在获取到结果前,线程是未完成、未取消的,获取到结果后,线程正常结束,此时线程是已完成、未取消的。下面代码继续修改,我们在获取结果之前先取消,因为线程可能来不及取消已经完成,所以我们加个让call方法睡眠1s。import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.CancellationException; public class FutureDemo implements Callable<String> { @Override public String call() throws Exception { Thread.sleep(1000); return "Hello,World"; } public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); FutureDemo futureDemo = new FutureDemo(); Future<String> tFuture = executorService.submit(futureDemo); // 判断线程是否完成 System.out.println("线程是否完成:" + tFuture.isDone()); // 判断线程是否取消 System.out.println("线程是否取消:" + tFuture.isCancelled()); try { // 试图取消线程 boolean isCancelled = tFuture.cancel(true); System.out.println("任务是否取消成功:" + isCancelled); // 判断线程是否完成 System.out.println("线程是否完成:" + tFuture.isDone()); // 判断线程是否取消 System.out.println("线程是否取消:" + tFuture.isCancelled()); // get的时候需要判断线程状态 System.out.println(tFuture.get()); } catch (CancellationException cancellationException) { System.out.println("线程已经被取消"); } catch (Exception exception) { exception.printStackTrace(); } } } 输出结果线程是否完成:false 线程是否取消:false 任务是否取消成功:true 线程是否完成:true 线程是否取消:true 线程已经被取消可以看到,线程已经取消成功,线程取消成功后,如果还是调用get方法,会抛出CancellationException异常,并且线程取消成功后,isDone方法也会返回true,代表线程已经完成。最后一个方法,我们看一下增加get的重载方法。import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.CancellationException; public class FutureDemo implements Callable<String> { @Override public String call() throws Exception { Thread.sleep(1000); return "Hello,World"; } public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); FutureDemo futureDemo = new FutureDemo(); Future<String> tFuture = executorService.submit(futureDemo); try { // get方法如果0.5秒内未获取到结果就抛出超时异常 System.out.println(tFuture.get((long)0.5, TimeUnit.SECONDS)); } catch (TimeoutException timeoutException) { System.out.println("超过0.5s未获取到结果"); } catch (Exception exception) { exception.printStackTrace(); } } } 输出结果超过0.5s未获取到结果三、FutureTask类FutureTask是Java自带的Future接口的实现类。FutureTask是实现的RunnableFuture接⼝,⽽RunnableFuture接⼝同时继承了Runnable接⼝和`Future接⼝。import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.CancellationException; public class FutureDemo implements Callable<String> { @Override public String call() throws Exception { return "Hello,World"; } public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); FutureDemo futureDemo = new FutureDemo(); FutureTask<String> futureTask = new FutureTask<>(futureDemo); executorService.submit(futureTask); try { System.out.println(futureTask.get()); }catch (Exception exception) { exception.printStackTrace(); } } } 输出结果:Hello,World文章摘抄自《深入浅出Java多线程.pdf》并对内容进行完善。
2025年04月24日
17 阅读
0 评论
0 点赞
2025-04-23
Java volatile关键字的用法
volatile是Java中的关键字,用于修饰变量,被volatile修饰的变量可以确保线程对这个变量的读写都是直接从主内存在中进行的。我们先来看下面一段代码public class Volatile { private boolean ready; public void setReady(boolean ready) { this.ready = ready; } public boolean getReady() { return this.ready; } public static void main(String[] args) throws Exception { Volatile volatile1 = new Volatile(); new Thread(() -> { try { Thread.sleep(1000); } catch (InterruptedException exception) { exception.printStackTrace(); } volatile1.setReady(true); System.out.println("设置ready值为true"); }).start(); new Thread(() -> { while (!volatile1.getReady()) { } System.out.println("检测到ready变成" + volatile1.getReady()); }).start(); } }运行这段代码,我们查看控制台,可以看到虽然我们在第一个线程中将ready值设置成了true,但是第二个线程中,while仍然进入了一个死循环。这是因为在两个线程中,ready的值被隔离了,所以我们在第一个线程中修改的ready值在第二个线程中是获取不到的。这个时候,我们可以给ready变量加上volatile关键字进行修饰,修改后代码如下public class Volatile { private volatile boolean ready; public void setReady(boolean ready) { this.ready = ready; } public boolean getReady() { return this.ready; } public static void main(String[] args) throws Exception { Volatile volatile1 = new Volatile(); new Thread(() -> { try { Thread.sleep(1000); } catch (InterruptedException exception) { exception.printStackTrace(); } volatile1.setReady(true); System.out.println("设置ready值为true"); }).start(); new Thread(() -> { while (!volatile1.getReady()) { } System.out.println("检测到ready变成" + volatile1.getReady()); }).start(); } }再次运行代码,可以发现第一个线程中的内容正常输出了,这就是volatile关键字的作用,一个线程中修改被volatile修饰的变量,另外一个变量能立即看到修改后的值。volatile关键字有两个作用:可见性及有序性。可见性当一个线程修改了被volatile修饰的变量的值,其他线程能够立刻看到修改后的值。这是因为被volatile修饰的变量不会被缓存到寄存器或其他处理器不可见的地方,因此保证了每次读取volatile变量都会从住内存中读取最新的值。有序性被volatile修饰的变量的读写具有一定的有序性,也就是禁止了指令重排序优化,这就意味着,在一个线程中,对被volatile修饰的变量的写操作一定发生在后续对这个变量的读操作之前。
2025年04月23日
21 阅读
0 评论
0 点赞
2025-04-21
Java Runnable接口的run()方法和start()方法
在Java 1.8及后续的版本中,我们有三种(也可以说是两种)方法开启线程。继承Thread类实现Runnable接口使用Java 8的函数式编程其中1和3的方式没有什么歧义,我们重点说一下Runnable接口。其实标题说Runnable接口的run()方法和start()方法是不准确的,严格来讲,应该是Runnable接口的run()方法和实现Runnable接口的实例的start()。先说一下结论,start()方法来启动线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码;而run()方法当作普通方法的方式调用。程序还是要顺序执行,要等待run方法体执行完毕后,才可继续执行下面的代码,程序中只有主线程——这一个线程,其程序执行路径还是只有一条,这样就没有达到写线程的目的。我们可以通过下面代码进行验证。public class Demo { static class Runner1 implements Runnable { // 实现了Runnable接口,jdk就知道这个类是一个线程 public void run() { for (int i = 0; i < 10; i++) { System.out.println("进入Runner1运行状态——————————" + i); } } } static class Runner2 implements Runnable { // 实现了Runnable接口,jdk就知道这个类是一个线程 public void run() { for (int i = 0; i < 10; i++) { System.out.println("进入Runner2运行状态==========" + i); } } } public static void main(String[] args) { Runner1 runner1 = new Runner1(); Runner2 runner2 = new Runner2(); runner1.run(); runner2.run(); // new Thread(runner1).start(); // new Thread(runner2).start(); } }看一下控制台输出内容,程序是先执行完runner1之后才执行的runner2方法,系统并没有给线程排队,相当于runner1和runner2是顺序执行的。我们修改上面的代码,改成start()方法执行public class Demo { static class Runner1 implements Runnable { // 实现了Runnable接口,jdk就知道这个类是一个线程 public void run() { for (int i = 0; i < 10; i++) { System.out.println("进入Runner1运行状态——————————" + i); } } } static class Runner2 implements Runnable { // 实现了Runnable接口,jdk就知道这个类是一个线程 public void run() { for (int i = 0; i < 10; i++) { System.out.println("进入Runner2运行状态==========" + i); } } } public static void main(String[] args) { Runner1 runner1 = new Runner1(); Runner2 runner2 = new Runner2(); // runner1.run(); // runner2.run(); new Thread(runner1).start(); new Thread(runner2).start(); } }再次查看控制台可以看到runner1和runner2是交替执行的,也就证明了,此时正确的开启了线程。
2025年04月21日
13 阅读
0 评论
0 点赞
2025-04-21
JProfiler 14注册码
JProfiler是ej-technologies公司开发一款专业的Java性能分析工具,它可以帮助开发人员识别和解决Java应用程序中的性能瓶颈、内存泄漏和线程问题。它提供直观、强大的图形界面,支持多种操作系统,包括Windows、macOS和Linux。{message type="success" content="JProfiler 14注册码"/}隐藏内容,请前往内页查看详情
2025年04月21日
35 阅读
2 评论
0 点赞
2025-04-20
钢琴键花洒顶喷嗡嗡异响问题解决
2025年刚过完年的时候,把家里换成了智能马桶及还有钢琴键花洒,当时安装的时候我没在家,晚上到家洗澡的时候,发现顶喷嗡嗡的响,当时嫌麻烦,也没让卖家过来修。最近闲着没事,想着这花洒一直响着也不是个事,特别是晚上洗澡的时候,次卧的声音还是挺大的,于是上网搜索了一下,发现别人也有遇到过的,找到了几个解决方案,最终解决了问题。先说一下我这边的问题只有顶喷响,其他的都不响。当时把顶喷拆下来只有,异响仍然存在,所以可以排除是花洒头导致的问题。把热水器热水出口关闭之后,顶喷只出凉水,也不会响解决问题上网搜索了一下,有人说是高层水压问题,有人说是总出水口的问题,也有人说是止逆阀的问题,因为水压问题或者出水口问题,咱们自己也无法解决,所以我首先按照止逆阀的思路来处理了,结果拆掉止逆阀之后,异响真的没有了。其实,解决也很简单。先把顶喷逆时针旋转拧下来找个尖嘴钳把止逆阀拧下来然后重新把顶喷顺时针安装上,再次开水,异响就这么解决了。{message type="error" content="不清楚止逆阀有什么作用"/}就这,找啄木鸟,不得收你300快钱~~~
2025年04月20日
33 阅读
0 评论
0 点赞
2025-04-20
JVisualVM:多合一故障处理工具
VisualVM是功能最强大的虚拟机运行监视和故障处理程序之一,曾经在很长一段时间内是Oracle官方助理发展的虚拟机故障处理工具。VisualVM的性能分析功能,比起JProfiler等专业收费工具也不遑多让,相比于第三方的功能,VisualVM还有一个很大的优点:不需要被监视的程序基于特殊的Agent去运行,因此它的通用性很强,对应用程序实际性能影响也比较小,使得它可以直接应用于生产环境。在这个注重颜值的时代,相较于JProfiler,可能界面比较丑陋是最大的缺点了吧。在强大的插件加持下,VisualVM可以实现包括但不限于以下的功能:显示虚拟机进程以及进程的配置、环境信息显示应用程序的处理器、垃圾收集、堆、方法区及线程信息dump以及分析堆转储快照方法级的应用程序性能分析,找出被调用最多、运行时间最长的方法壹、安装插件VisualVM插件可以直接在线安装,依次点击【tools】- 【plugins】勾选需要安装的插件,点击【install】即可进行安装。贰、生成、浏览堆转储快照生成和浏览堆转储快照可能是日常开发过程中分析性能最常见的操作,下面介绍下在visualVM中如何生成或者分析堆转储快照。一、生成堆转储快照我们运行一个程序后,在visualVM左侧Local下,会自动加载对应的进程信息。比如我这里的JHSDB_TestCae,我们下面都以这个为例进行说明。可以通过以下两种方式之一生成堆转储快照选择应用程序,点击右键,然后选择【Heap Dump】双击应用程序,在打开的页面中,选择【Monitor】,然后选择【Heap Dump】快照存储后会自动打开。二、打开堆转储快照如果是别人发过来的快照文件,可以点击左上角的文件图标打开。Overview页签显示的是一些基本信息,包括pid、Java版本、虚拟机参数、环境参数等信息。Monitor页签是图标信息显示的CPU、堆、元空间、类及线程等信息。Threads页签显示的是线程信息Sampler页签以一定的时间间隔对CPU、内存进行采样,可检查出占用CPU时间较多或占用内存空间较大的线程,有助于性能调优。对CPU采样时,该页提供CPU样例(CPU samples)和线程CPU时间(Thread CPU time)两个子页签,前者可用于分析调用链上的方法耗时,后者可用于比较线程CPU耗时。profiler是性能分析页签,因为性能分析对程序运行性能影响较大,生产环境不建议使用。叁、常见插件介绍上面所过,VisualVM是支持插件的,下面我们介绍几款常用的插件。一、BTrace动态日志跟踪BTrace允许在不中断目标程序的前提下,通过HotSpot虚拟机的Instrument功能动态加入原本并不存在的调试代码。在插件页面,选择BTrace Workbench,然后安装我们创建一段输出固定内容的死循环并运行package cc.lisen.test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /** * description: * * @author: lisen * @Blog:<a href="https://lisen.cc">lisen.cc</a> * @DateTime: 2025-04-20 20:34 */ public class BTraceTest { public String sayHello(String name) { return "Hello " + name; } public static void main(String[] args) throws IOException { while (true) { BTraceTest bTraceTest = new BTraceTest(); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); System.out.println(bTraceTest.sayHello(reader.readLine())); } } }打开VisualVM,找到我们的程序,点击右键选择【Trace Application】,然后输入以下脚本/* BTrace Script Template */ import org.openjdk.btrace.core.annotations.*; import static org.openjdk.btrace.core.BTraceUtils.*; @BTrace(unsafe=true) public class TracingScript { @OnMethod( clazz="cc.lisen.test.BTraceTest", method="sayHello", location=@Location(Kind.RETURN) ) public static void func(@Self cc.lisen.test.BTraceTest instance,String test,@Return String result) { System.out.println("调用堆栈:"); jstack(); } }
2025年04月20日
18 阅读
0 评论
0 点赞
1
2
...
53