首页 文章 精选 留言 我的

精选列表

搜索[java],共10000篇文章
优秀的个人博客,低调大师

关于java线程中stop interrupt daemon wait notify

一。关于终止线程stop与interrupt 一般来说,线程执行结束后就变成消亡状态,乍看之下我们并不需要人为进行干预(人为停止线程),不过凡事都有例外吧,在服务器或者其他应用场景下,线程为了提供服务而一直在不停的运转,因此必要时刻我们还需“人为干涉的”。 通常情况下,终止线程有两种方式:stop与interrupt 1) stop:暴力的停止线程(不管线程执行到哪段代码,立刻干掉),这个方法因为过于暴力会导致安全问题,因此JDK不推荐使用。 2) interrupt:优雅停止,调用该方法会通知线程可以进行停止操作了,此时线程只是变成可停止状态(thread.isInterrupted的值为true),实际上并没有停止 请看下段代码: 1 package com.bdqn.lyrk.basic; 2 3 /** 4 * 一个线程设置共享变量的值,保持ID与name值相同 5 * 另外一个线程读取共享变量的值,发现ID与name值不同时打印 6 * 7 * @author chen.nie 8 * @date 2018/1/30 9 **/ 10 public class StopThreadUnsafe { 11 12 public static User user = new User(); 13 14 public static class User { 15 private int id; 16 private String name; 17 18 public int getId() { 19 return id; 20 } 21 22 public void setId(int id) { 23 this.id = id; 24 } 25 26 public String getName() { 27 return name; 28 } 29 30 public void setName(String name) { 31 this.name = name; 32 } 33 34 public User() { 35 id = 0; 36 name = "0"; 37 } 38 39 @Override 40 public String toString() { 41 return "User [id=" + id + ",name=" + name + "]"; 42 } 43 } 44 45 public static class ChangeObjectThread extends Thread { 46 47 @Override 48 public void run() { 49 while (true) { 50 synchronized (user) { 51 if (Thread.currentThread().isInterrupted()) { 52 break; 53 } 54 int i = (int) System.currentTimeMillis() / 1000; 55 user.setId(i); 56 try { 57 Thread.sleep(100); 58 } catch (InterruptedException e) { 59 Thread.currentThread().interrupt(); 60 } 61 user.setName("" + i); 62 63 } 64 Thread.yield(); 65 } 66 } 67 } 68 69 public static class ReadObjectThread extends Thread { 70 71 @Override 72 public void run() { 73 while (true) { 74 synchronized (user) { 75 if (user.getId() != Integer.parseInt(user.getName())) { 76 System.out.println(user); 77 } 78 } 79 Thread.yield(); 80 } 81 } 82 } 83 } View Code Main方法: 1 package com.bdqn.lyrk.basic; 2 3 public class Main { 4 public static void main(String[] args) throws InterruptedException { 5 new StopThreadUnsafe.ReadObjectThread().start(); 6 while (true) { 7 StopThreadUnsafe.ChangeObjectThread thread = new StopThreadUnsafe.ChangeObjectThread(); 8 thread.start(); 9 Thread.sleep(200); 10 thread.stop(); 11 // thread.interrupt(); 12 } 13 } 14 } View Code 此时调用stop终止线程时,得到如下结果 User [id=1197577,name=1197576] User [id=1197577,name=1197576] User [id=1197577,name=1197576] User [id=1197577,name=1197576] User [id=1197578,name=1197577] 原因很简单:stop方法会释放对象锁,并终止线程,当线程还没有与name赋值时,已经被干掉了因此其他线程在读取时,很有可能读到NAME与ID值不一致的情况 二。守护线程Daemon 守护线程顾名思义,是系统的守护者,这个线程为系统的运行默默提供服务,当系统任务运行完毕,守护线程也就完成使命了,比如说垃圾回收线程,JIT线程都是守护线程,设置守护线程的方式:在调用start方法前,通过线程对象.setDaemon(true) 代码如下: 1 package com.bdqn.lyrk.basic; 2 3 public class DaemonDemo { 4 public static class DaemonT extends Thread { 5 @Override 6 public void run() { 7 while (true){ 8 System.out.println("I am alive"); 9 try { 10 Thread.sleep(1000); 11 } catch (InterruptedException e) { 12 e.printStackTrace(); 13 } 14 } 15 } 16 } 17 18 public static void main(String[] args) throws InterruptedException { 19 DaemonT daemonT = new DaemonT(); 20 daemonT.setDaemon(true); 21 daemonT.start(); 22 Thread.sleep(5000); 23 } 24 } View Code 运行结果 I am alive I am alive I am alive I am alive I am alive Process finished with exit code 0 我们可以看到,当主线程执行完毕后,守护线程也随之结束 三。wait与notify wait与notify是多线程协同工作的最基本手段,可是这两个方法属于Object的方法,当需要使用wait和notify时,必须配合synchronized使用,此时调用wait方法,当前线程会进入等待队列并释放当前的对象锁,直到线程被唤醒(notify),notify方法会随机唤醒一个在等待队列中的线程,notifyAll方法则唤醒所有在等待队列中的线程 代码示例: 1 package com.bdqn.lyrk.basic; 2 3 public class SimpleWN { 4 public static final Object object = new Object(); 5 6 public static class T1 extends Thread { 7 8 @Override 9 public void run() { 10 synchronized (object) { 11 System.out.println("开始执行线程..."); 12 try { 13 object.wait(); 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 System.out.println("结束执行线程..."); 18 } 19 } 20 } 21 22 public static class T2 extends Thread { 23 @Override 24 public void run() { 25 synchronized (object) { 26 System.out.println("5秒后准备唤醒线程.."); 27 try { 28 Thread.sleep(5000); 29 } catch (InterruptedException e) { 30 e.printStackTrace(); 31 } 32 object.notify(); 33 } 34 } 35 } 36 37 public static void main(String[] args) { 38 T1 t1 = new T1(); 39 T2 t2 = new T2(); 40 t1.start(); 41 t2.start(); 42 } 43 } View Code 输出内容: 开始执行线程... 5秒后准备唤醒线程.. 结束执行线程... Process finished with exit code 0 注意以下几点: 1)当线程T1被notify过后,也必须要重新获取对象锁,才能够继续执行 2)sleep也能达到wait的效果,但是唯一区别时,sleep时并不会释放对象锁,因此其他线程并没有得到执行的机会

优秀的个人博客,低调大师

java中的单元测试Junit4

首先就是主要简单的讲一下Junit的使用,属于傻瓜式速成教程,gogogogogogogo! 一:什么是单元测试: 我们在编写大型程序的时候,需要写成千上万个方法或函数,这些函数的功能可能很强大,但我们在程序中只用到该函数的一小部分功能,并且经过调试可以确定,这一小部分功能是正确的。但是,我们同时应该确保每一个函数都完全正确,因为如果我们今后如果对程序进行扩展,用到了某个函数的其他功能,而这个功能有bug的话,那绝对是一件非常郁闷的事情。所以说,每编写完一个函数之后,都应该对这个函数的方方面面进行测试,这样的测试我们称之为单元测试。传统的编程方式,进行单元测试是一件很麻烦的事情,你要重新写另外一个程序,在该程序中调用你需要测试的方法,并且仔细观察运行结果,看看是否有错。这样的话太过于麻烦了,本文简要介绍一下在Eclipse中使用JUnit4进行单元测试的方法。用更加通俗的话来描述单元测试就是:写了个类,要给别人用,会不会有bug?怎么办?测试一下。用main方法测试好不好?这种方法我们经常用,就是写一个方法实现一些功能,把方法的调用方式放在main函数中。这样的测试方式一个是使得main函数太过于混乱,再者测试过程需要人的仔细观察来辨别每个函数的功能实现,哪一个函数出错了,哪一个函数没有输出之类的问题层出不穷,单元测试就是来解决这些问题的。下面我会就单元测试的每一步给出详细的图解和描述。 二:进行单元测试: 首先我先创建一个就是一个简单的计算机类,里面就写一个加除的方法; 1 package junit; 2 3 public class Reckon {//一个计算类 4 5 //简单的实现两个计算方法 6 public int add(int num1,int num2){ 7 return num1+num2; 8 } 9 10 public int divide(int num1,int num2){ 11 return num1/num2; 12 } 13 14 public static void main(String[] args) { 15 //传统的把方法的调用方式放在main函数中测试运行 16 int num=new Reckon().add(1, 2); 17 System.out.println(num); 18 } 19 } 下面进行单元测试: 右键新建:选择junit Test Case 接着next,出现下面的画面,用junit4,在下面选择要测试的类,给这个测试类起一个名字。 点击next出现下面的画面,这是选择要测试的方法; 点击finish,添加jar包; 这一步提示我们是否将我们需要的JUnit 4的相关包加入到我们项目的ClassPath路径下,点击OK就行,因为Eclipse中包含JUnit的jar包。 做完上面的会自动生成这样一个测试类; 1 package junit; 2 3 import static org.junit.Assert.*; 4 5 import org.junit.Test; 6 7 public class Reckon_Test { 8 9 @Test 10 public void testAdd() { 11 fail("Not yet implemented"); 12 } 13 14 @Test 15 public void testDivide() { 16 fail("Not yet implemented"); 17 } 18 19 } Test注解表示这是个测试方法; 修改里面的方法: 1 package junit; 2 3 import static org.junit.Assert.*; 4 5 import org.junit.Test; 6 7 public class Reckon_Test { 8 9 @Test 10 public void testAdd() { 11 int z=new Reckon().add(2, 8); 12 assertEquals(10,z);//判断得到的z是不是10也就是正确的答案 13 } 14 15 @Test 16 public void testDivide() { 17 int z=new Reckon().divide(8, 2); 18 System.out.println(z); 19 } 20 21 } 右键选择Junit Test 运行 绿色代表测试成功,其中Error:程序出错 Failures:测试失败 如果我们把除数修改为零,则会出现数学异常 常用的方法: 欢迎大家一起说出自己的想法。

优秀的个人博客,低调大师

JAVA 并发编程学习(1)之基本概念

1,为什么需要并发? 在早期计算机中没有操作系统,一台计算机从头到尾只执行一个程序,如果这个程序的任务是先进行CPU计算再把计算结果写入文件。那么,在进行CPU计算时,这台计算机的IO模块是空闲的,把计算结果写入文件中时,这台计算机的CPU是空闲的。 为此,引入了操作系统。这样,在一台计算机中可以运行多个程序了。由操作系统来管理多个程序的运行,一个程序在进行CPU计算的同时,另一个程序可以进行IO操作。提高了计算机的利用率(CPU和IO操作 可以同时在发生了) 引入操作系统之后,不可避免地出现了进程这个概念。因为操作系统管理若干个程序了,而每个运行中的程序可以用进程来表示(进程的动态性)。 那为什么还需要线程呢? ①进程之间需要通信、需要共享一些数据,需要协作……这些用线程来“表示”更方便。因为,不同进程的地址空间是相互独立的,一个进程不能直接访问另一个进程的地址空间。一个进程内部的多个线程共享这个进程状态空间,如果把数据的共享放到线程这个层次来实现,就方便多了。 ②进一步提高资源利用率---这个有点类似于:从没有操作系统的计算机到有操作系统的计算机转变---这里是从没有线程到有线程转变。 2,我们就是因为方便数据共享……一些原因引入了线程,那么多个线程同时访问一个可变的变量时,这个变量到底处理哪一个状态?这就存在一种线程安全性问题。 3,什么是线程安全性 讨论线程安全性针对的是 类。核心就是正确性。即,某个类的行为和其规范完全一致:即,我写了一个类,这个类具有哪些功能,需要完成哪些操作……这些是类的规范;而多个线程运行这个类的代码时,其执行效果和规范描述的要一致。 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协调,这个类都能表现出正确的行为,那么就称这个类是线程安全的。 4,无状态对象 5,Race Condition 竞态条件 ①读--修改--写入 ②check--then--act 本文转自hapjin博客园博客,原文链接:http://www.cnblogs.com/hapjin/,如需转载请自行联系原作者

优秀的个人博客,低调大师

分析一种前缀树的java实现

private class TrieNode{ /** * 标识当前结点是否是一个“关键词”的最后一个结点 * true 关键词的终结 false 继续 */ private boolean isEnd = false; /** * 用map来存储当前结点的所有子节点,非常的方便 * key 下一个字符 value 对应的结点 */ private Map<Character , TrieNode> subNodes = new HashMap<>(); /** * 向指定位置添加结点树 * @param key * @param node */ public void addSubNode(Character key , TrieNode node){ subNodes.put(key , node); } /** * 根据key获得相应的子节点 * @param key * @return */ public TrieNode getSubNode(Character key){ return subNodes.get(key); } //判断是否是关键字的结尾 public boolean isKeyWordEnd(){ return isEnd; } //设置为关键字的结尾 public void setKeyWordEnd(boolean isEnd){ this.isEnd = isEnd; } } /** * 核心算法一:构建字典树 * 根据输入的字符串,逐步构建字典树 * @param textLine */ private void addDirTreeNode(String textLine){ //边界处理 if(textLine == null) return; //临时结点指向根结点 TrieNode tempNode = root; for(int i = 0; i < textLine.length(); i++){ char charWord = textLine.charAt(i); //直接跳过非法文字 if (isSymbol(charWord)) continue; TrieNode node = tempNode.getSubNode(charWord); if (node == null){ //说明tempNode第一次碰到该关键字结点 node = new TrieNode(); tempNode.addSubNode(charWord , node); } //tempNode指向下一个结点,开始下一次循环 tempNode = node; //到敏感词的最后一个字时,标记为红色(关键词结尾) if (i == textLine.length() - 1) tempNode.setKeyWordEnd(true); } }

优秀的个人博客,低调大师

CentOS 7.4 java验证码乱码的问题

新服务器配置发布网站 配置后程序顺利启动在登录时发现验证码无法识别显示出了图片,但是字是乱码 初步估计应该是字体问题 ssh登录服务器查看默认字体 #fc-match msam10.ttf: "msam10" "LyX"确认是字体问题 解决字体问题 1. 从windows系统拷贝 微软雅黑字体 打开文件夹 C:WindowsFonts选中微软雅黑字体,复制拷贝到别的文件夹 2. 将字体文件上传到centos服务器 #mkdir /usr/local/font/default将字体文件上传到default目录中 3.更新字体缓存 #fc-cache 4.再次查看默认字体 #fc-match msyh.ttc: "Microsoft YaHei" "Normal"字体正确 5.重启tomcat #systemctl restart tomcat重启之后 验证码显示正确。

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

用户登录
用户注册