知了小站 - IT人的小站 - 日常杂记 https://www.ydyno.com/category/stories/ Java 模拟死锁以及如何避免死锁 https://www.ydyno.com/archives/1747.html 2024-02-29T22:30:00+08:00 模拟死锁死锁是多线程编程中常见的问题,它发生在两个或多个线程相互等待对方释放资源的情况下。以下是一个简单的Java死锁模拟示例:public class DeadlockExample { public static void main(String[] args) { // 创建两个共享资源 final Object resource1 = new Object(); final Object resource2 = new Object(); // 线程1尝试获取资源1,然后资源2 Thread thread1 = new Thread(() -> { synchronized (resource1) { System.out.println("Thread 1: Locked resource 1"); try { // 为了增加死锁的可能性,线程1休眠一段时间 Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (resource2) { System.out.println("Thread 1: Locked resource 2"); } } }); // 线程2尝试获取资源2,然后资源1 Thread thread2 = new Thread(() -> { synchronized (resource2) { System.out.println("Thread 2: Locked resource 2"); try { // 为了增加死锁的可能性,线程2休眠一段时间 Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (resource1) { System.out.println("Thread 2: Locked resource 1"); } } }); // 启动线程1和线程2 thread1.start(); thread2.start(); } }在这个例子中,两个线程分别尝试获取两个共享资源,但它们的获取顺序相反。如果这两个线程在不同的时刻开始执行,可能不会发生死锁,但如果它们同时开始执行,就有可能因为资源争夺而导致死锁。如何避免死锁避免死锁是多线程编程中非常重要的一个方面,以下是一些常见的避免死锁的策略:锁的顺序:定义一个全局的锁获取顺序,然后在所有线程中都按照相同的顺序获取锁。这样可以避免不同线程以不同的顺序获取锁而导致死锁。锁的超时机制:在获取锁的时候,设置一个超时机制。如果某个线程在一定时间内无法获取到所需的锁,就释放已经获取的锁,并重新尝试获取锁,或者执行其他逻辑来避免死锁。使用 tryLock() 方法:在Java中,Lock 接口提供了 tryLock() 方法,它可以尝试获取锁,但不会一直等待。通过使用这个方法,你可以在获取锁失败时执行一些逻辑,而不是一直等待锁。锁的粒度:设计时考虑锁的粒度。如果锁的范围太大,竞争会增加,容易导致死锁。将锁的范围缩小到最小必要范围,可以减少死锁的概率。使用事务:在数据库操作中,使用事务可以避免某些类型的死锁。数据库事务通常会自动处理资源的锁定和释放。死锁检测和处理:一些系统提供死锁检测机制,可以检测到死锁的发生,并采取一些措施,例如自动释放锁或终止某些线程。避免循环等待:设定一个全局的资源获取顺序,并要求所有线程按照相同的顺序获取资源,以避免循环等待的情况。使用高级同步工具:Java提供了一些高级的同步工具,如 java.util.concurrent 包中的 ReentrantLock、Semaphore 等,它们提供更灵活的控制和避免死锁的机制。避免死锁是一个复杂的问题,需要在设计和实现阶段考虑。以上策略可以根据具体情况进行选择和组合,以提高多线程程序的稳定性。 Jpa 持久层中使用自定义对象接收数据 https://www.ydyno.com/archives/1739.html 2023-05-12T16:32:00+08:00 在 JPA 持久层中,可以自定义接收数据的对象。这通常用于查询操作,其中查询结果不完全匹配现有的实体类,或者需要仅返回某些字段的结果。以下示例,展示如何在 JPA 持久层中自定义接收数据的对象假设有一个名为 Person 的实体类,包含 id、name 和 age 字段:@Entity @Table(name = "person") public class Person { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private int age; // 省略构造函数、getter 和 setter 方法 }现在我们只想查询人员的姓名和年龄,并将结果封装到自定义的数据对象 PersonInfo 中:public class PersonInfo { private String name; private int age; // 省略构造函数、getter 和 setter 方法 }第一种方式@Repository public class PersonRepository { @PersistenceContext private EntityManager entityManager; public List<PersonInfo> getPersonInfo() { String query = "SELECT new com.example.PersonInfo(p.name, p.age) FROM Person p"; TypedQuery<PersonInfo> typedQuery = entityManager.createQuery(query, PersonInfo.class); return typedQuery.getResultList(); } }在上面的代码中,我们使用 SELECT new 关键字创建了一个 PersonInfo 对象,并将查询结果映射到该对象。通过使用构造函数,可以选择性地指定要接收的字段。第二种方式使用 Spring Data JPA 的 Repository 接口@Repository public interface PersonRepository extends JpaRepository<Person, Long> { @Query("SELECT new com.example.PersonInfo(p.name, p.age) FROM Person p") List<PersonInfo> getPersonInfo(); }在上面的示例中,我们使用了 @Query 注解,并指定了一个自定义的查询语句。在查询语句中,我们使用了 new 关键字创建了一个 PersonInfo 对象,并将查询结果映射到该对象。注意点在查询语句中,com.example.PersonInfo 是 PersonInfo 类的完全限定名,确保使用正确的包名,确保创建了对应的构造方法。 debian11 系统增加开机自启脚本 https://www.ydyno.com/archives/1736.html 2023-04-21T10:30:00+08:00 在 debian11 系统增加开机自启脚本时发现 /etc 目录里面没有 rc.local 文件,导致无法添加自启动脚本,这里记录下 debian11 增加开机自启脚本的全过程。修改 rc-local.service 文件cd /lib/systemd/system添加下面内容到 rc-local.service 文件最后[Install] WantedBy=multi-user.target创建 /etc/rc.local 文件创建 /etc/rc.local 文件,并写入下面内容#!/bin/sh -e # 在这里输入需要自启的脚本 exit 0创建完成后需要给其赋予运行权限sudo chmod +x /etc/rc.local启动 rc-local 服务即可sudo systemctl enable rc-local # 启用 sudo systemctl start rc-local.service # 开始运行 sudo systemctl status rc-local.service # 查看状态 使用 qshell 工具上传文件夹到七牛云 https://www.ydyno.com/archives/1709.html 2023-03-08T15:32:00+08:00 qshell 是利用七牛文档上公开的API实现的一个方便开发者测试和使用七牛API服务的命令行工具。下载地址: 直达 ,根据自己系统平台下载即可。使用教程下载后,随意放到哪都行。1、赋予qshell执行权限我这里是Linux系统,默认放到了 /root 目录,进入 /root 目录后赋予 qshell 执行权限chmod +x qshell2、配置七牛云账户需要鉴权的命令都需要依赖七牛账号下的 AccessKey 和 SecretKey, 点我直达 ,拿到 AccessKey 和 SecretKey 输入下面的命令./qshell account ak sk name此处操作后在当前用户主目录中生成 qshell 目录:ls ~/.qshell/ account.json3、上传同步文件夹./qshell qupload2 --src-dir=需要上传的文件夹 --bucket=对象存储桶的名称更多使用方式:https://developer.qiniu.com/kodo/1302/qshell Docker容器 - 数据打包迁移全过程记录 https://www.ydyno.com/archives/1704.html 2023-02-23T18:09:00+08:00 记录下Docker容器打包迁移的一些命令:# 确认要迁移容器的名称 docker ps -a # 打包容器为新的镜像 docker commit 容器名 镜像名 # 把镜像保存为tar文件 docker save 镜像名 >文件名称.tar # 将文件传输到另一台服务器,用SCP命令 scp -P 22 /文件路径/文件名称.tar root@接收文件的服务器的IP:/存放文件的目录 # 恢复镜像 docker load < 文件名称.tar恢复镜像后,用同样的方法创建容器即可。 记 Docker 运行 Logstash out of memory 问题 https://www.ydyno.com/archives/1667.html 2022-08-06T15:50:00+08:00 最近在 Docker 容器上搭建 Zookeeper+Kafka+Logstash+Elasticsearch+Kibana 日志分析系统,在运行 Logstash 和 Elasticsearch 时遇到了如下错误:library initialization failed - unable to allocate file descriptor table - out of memorylibrary initialization failed - unable to allocate file descriptor table - out of memory在这里记录下解决方案。解决方案通过重写 Docker 的 ExecStart 的参数解决sudo systemctl edit docker进入后,添加或者修改对应参数[Service] ExecStart= ExecStart=/usr/bin/dockerd --default-ulimit nofile=65536:65536 -H fd://最后重启 Dockersudo systemctl daemon-reload sudo systemctl restart docker参考:https://stackoverflow.com/questions/68776387/docker-library-initialization-failed-unable-to-allocate-file-descriptor-tabl ELADMIN 项目官网域名更换通知与加群答案 https://www.ydyno.com/archives/1646.html 2022-08-02T15:18:00+08:00 为方便访问,已将 ELADMIN 官网域名更换为 eladmin.vip 访问老域名 el-admin.vip 会重定向到新域名 eladmin.vip。QQ交流群:891137268、947578238、 659622532入群答案:eladmin.vip有关 ELADMIN 的更多问题访问https://eladmin.vip/pages/020101/ Jpa进阶,使用 Specification 进行高级查询 https://www.ydyno.com/archives/1583.html 2022-06-08T21:45:00+08:00 前言上一篇文章主要讲了 Jpa 的简单使用,而在实际项目中并不能满足我们的需求。如对多张表的关联查询,以及查询时需要的各种条件,这个时候你可以使用自定义 SQL 语句,但是Jpa并不希望我们这么做,于是就有了一个扩展:使用 Specification 进行查询修改相应代码1、修改 User.class代码用的上一篇文章的,这里在 User 类中进行扩展,待会查询时会用到@Entity @Table(name = "user") public class User { //部分代码略 /** * 加上该注解,在保存该实体时,Jpa将为我们自动设置上创建时间 */ @CreationTimestamp private Timestamp createTime; /** * 加上该注解,在保存或者修改该实体时,Jpa将为我们自动创建时间或更新日期 */ @UpdateTimestamp private Timestamp updateTime; /** * 关联角色,测试多表查询 */ @ManyToOne @JoinColumn(name = "role_id") private Role role; //部分代码略 }2、新增Role.class@Entity @Table(name = "role") public class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(unique = true,nullable = false) private String name; //get set略 }3、修改UserRepository要使用 Specification,需要继承 JpaSpecificationExecutor 接口,修改后的代码如下public interface UserRepo extends JpaRepository<User,Long>, JpaSpecificationExecutor { }4、查看 JpaSpecificationExecutor 源码Specification 是 Spring Data JPA 提供的一个查询规范,这里所有的操作都是围绕 Specification 来进行public interface JpaSpecificationExecutor<T> { Optional<T> findOne(@Nullable Specification<T> var1); List<T> findAll(@Nullable Specification<T> var1); Page<T> findAll(@Nullable Specification<T> var1, Pageable var2); List<T> findAll(@Nullable Specification<T> var1, Sort var2); long count(@Nullable Specification<T> var1); }封装查询Service我这里简单做了下简单封装,编写 UserQueryService.class@Service public class UserQueryService { @Autowired private UserRepo userRepo; /** * 分页加高级查询 */ public Page queryAll(User user, Pageable pageable , String roleName){ return userRepo.findAll(new UserSpec(user,roleName),pageable); } /** * 不分页 */ public List queryAll(User user){ return userRepo.findAll(new UserSpec(user)); } class UserSpec implements Specification<User>{ private User user; private String roleName; public UserSpec(User user){ this.user = user; } public UserSpec(User user,String roleName){ this.user = user; this.roleName = roleName; } @Override public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) { List<Predicate> list = new ArrayList<Predicate>(); /** * 左连接,关联查询 */ Join<Role,User> join = root.join("role",JoinType.LEFT); if(!StringUtils.isEmpty(user.getId())){ /** * 相等 */ list.add(cb.equal(root.get("id").as(Long.class),user.getId())); } if(!StringUtils.isEmpty(user.getUsername())){ /** * 模糊 */ list.add(cb.like(root.get("username").as(String.class),"%"+user.getUsername()+"%")); } if(!StringUtils.isEmpty(roleName)){ /** * 这里的join.get("name"),就是对应的Role.class里面的name */ list.add(cb.like(join.get("name").as(String.class),"%"+roleName+"%")); } if(!StringUtils.isEmpty(user.getCreateTime())){ /** * 大于等于 */ list.add(cb.greaterThanOrEqualTo(root.get("createTime").as(Timestamp.class),user.getCreateTime())); } if(!StringUtils.isEmpty(user.getUpdateTime())){ /** * 小于等于 */ list.add(cb.lessThanOrEqualTo(root.get("createTime").as(Timestamp.class),user.getUpdateTime())); } Predicate[] p = new Predicate[list.size()]; return cb.and(list.toArray(p)); } } }查询测试1、新增测试数据 @Test public void test3() { /** * 新增角色 */ Role role = new Role(); role.setName("测试角色"); role = roleRepo.save(role); /** * 新增并绑定角色 */ User user = new User("小李",20,"男",role); User user1 = new User("小花",21,"女",role); userRepo.save(user); userRepo.save(user1); }查看数据都已经新增成功了,并且 createTime 和 updateTime 也帮我们加上了2、简单查询 @Test public void Test4(){ /** * 添加查询数据,模糊查询用户名 */ User user = new User(); user.setUsername("花"); List<User> users = userQueryService.queryAll(user); users.forEach(user1 -> { System.out.println(user1.toString()); }); }运行结果如下3、分页+关联查询 @Test public void test5() { //页码,Pageable中默认是从0页开始 int page = 0; //每页的个数 int size = 10; Sort sort = new Sort(Sort.Direction.DESC,"id"); Pageable pageable = PageRequest.of(page,size,sort); Page<User> users = userQueryService.queryAll(new User(),pageable,"测试角色"); System.out.println("总数据条数:"+users.getTotalElements()); System.out.println("总页数:"+users.getTotalPages()); System.out.println("当前页数:"+users.getNumber()); users.forEach(user1 -> { System.out.println(user1.toString()); }); } }通过角色的名称查询用户,运行结果如下 JPA入门,Spring Boot 整合 JPA 操作数据库 https://www.ydyno.com/archives/1571.html 2022-06-01T20:23:00+08:00 简单了解Jpa(java Persistence API,java持久化 api),它定义了对象关系映射(ORM)以及实体对象持久化的标准接口。在 Spring boot中 JPA 是依靠 Hibernate才得以实现对的,Hibernate 在 3.2 版本中对 JPA 的实现有了完全的支持。Spring Boot 整合 JPA 可使开发者用极简的代码实现对数据的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!添加依赖#这里添加 Jpa 和 Mysql 的依赖 <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>开发Jpa编写实体类定义用户实体类 User//@Entity 表明这个是一个实体类 @Entity //指定表名 @Table(name = "user") public class User { /** * 表明这个字段是主键,并且ID是自增的 */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; /** * 这样则表示该属性,在数据库中的名称是 username,并且使唯一的且不能为空的 */ @Column(name = "username",unique = true,nullable = false) private String username; private Integer age; private String sex; //get set略 }配置文件说明Spring Boot 配置文件 application.yml 内容如下server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/jpa username: root password: 123456 jpa: hibernate: #注入方式 ddl-auto: update naming: #Hibernate 命名策略,这里修改下 physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl properties: hibernate: #数据库方言 dialect: org.hibernate.dialect.MySQL5InnoDBDialectddl-auto属性说明常用属性: 自动创建|更新|验证数据库表结构。 **create:** 每次启动时都会删除上一次的生成的表,然后根据你的实体类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。 **create-drop :** 每次加载 hibernate 时根据 model 类生成表,但是 sessionFactory 一关闭,表就自动删除。 **update:** 最常用的属性,第一次加载启动时根据实体类会自动建立起表的结构(前提是先建立好数据库),以后以后再次启动时会根据实体类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。 **validate :** 每次应用启动时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。这里我们使用 update,让应用启动时自动给我们生成 User 表基础操作1、编写 UserRepo 继承 JpaRepositoryimport me.zhengjie.domain.User; import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepo extends JpaRepository<User,Long> { }2、使用默认方法在 test 目录中,新建 UserTests @RunWith(SpringRunner.class) @SpringBootTest public class UserTests { @Autowired private UserRepo userRepo; @Test public void test1() { User user=new User(); //查询全部 List<User> userList = userRepo.findAll(); //根据ID查询 Optional<User> userOptional = userRepo.findById(1L); //保存,成功后会返回成功后的结果 user = userRepo.save(user); //删除 userRepo.delete(user); //根据ID删除 userRepo.deleteById(1L); //计数 Long count = userRepo.count(); //验证是否存在 Boolean b = userRepo.existsById(1l); } } 自定义简单查询自定义的简单查询就是根据方法名来自动生成 SQL,主要的语法是 findXXBy, readAXXBy, queryXXBy, countXXBy, getXXBy 后面跟属性名称:public interface UserRepo extends JpaRepository<User,Long> { /** * 根据 username 查询 * @param username * @return */ User findByUsername(String username); /** * 根据 username 和 age 查询 * @param username * @param age * @return */ User findByUsernameAndAge(String username,Integer age); }具体的关键字,使用方法和生产成 SQL 如下表所示KeywordSampleJPQL snippetAndfindByLastnameAndFirstname… where x.lastname = ?1 and x.firstname = ?2OrfindByLastnameOrFirstname… where x.lastname = ?1 or x.firstname = ?2Is,EqualsfindByFirstnameIs,findByFirstnameEquals… where x.firstname = ?1BetweenfindByStartDateBetween… where x.startDate between ?1 and ?2LessThanfindByAgeLessThan… where x.age < ?1LessThanEqualfindByAgeLessThanEqual… where x.age ⇐ ?1GreaterThanfindByAgeGreaterThan… where x.age > ?1GreaterThanEqualfindByAgeGreaterThanEqual… where x.age >= ?1AfterfindByStartDateAfter… where x.startDate > ?1BeforefindByStartDateBefore… where x.startDate < ?1IsNullfindByAgeIsNull… where x.age is nullIsNotNull,NotNullfindByAge(Is)NotNull… where x.age not nullLikefindByFirstnameLike… where x.firstname like ?1NotLikefindByFirstnameNotLike… where x.firstname not like ?1StartingWithfindByFirstnameStartingWith… where x.firstname like ?1 (parameter bound with appended %)EndingWithfindByFirstnameEndingWith… where x.firstname like ?1 (parameter bound with prepended %)ContainingfindByFirstnameContaining… where x.firstname like ?1 (parameter bound wrapped in %)OrderByfindByAgeOrderByLastnameDesc… where x.age = ?1 order by x.lastname descNotfindByLastnameNot… where x.lastname <> ?1InfindByAgeIn(Collection ages)… where x.age in ?1NotInfindByAgeNotIn(Collection age)… where x.age not in ?1TRUEfindByActiveTrue()… where x.active = trueFALSEfindByActiveFalse()… where x.active = falseIgnoreCasefindByFirstnameIgnoreCase… where UPPER(x.firstame) = UPPER(?1)分页查询Page<User> findALL(Pageable pageable); Page<User> findByUserName(String userName,Pageable pageable);Pageable 是 spring 封装的分页实现类,使用的时候需要传入页数、每页条数和排序规则@Test public void test2() { //页码,Pageable中默认是从0页开始 int page = 0; //每页的个数 int size = 10; Sort sort = new Sort(Sort.Direction.DESC,"id"); Pageable pageable = PageRequest.of(page,size,sort); Page<User> list = userRepo.findAll(pageable); }限制查询有时候我们只需要查询前N个元素 /** * 限制查询 */ List<User> queryFirstByAge(Integer age); List<User> queryFirst10ByAge(Integer age);自定义SQL如果项目中由于某些原因 Jpa 自带的已经满足不了我们的需求了,这个时候我们就可以自定义的 SQL 来查询,只需要在 SQL 的查询方法上面使用@Query注解,如涉及到删除和修改在需要加上 @Modifying /** * 自定义SQL,nativeQuery = true,表明使用原生sql */ @Modifying @Query(value = "update User u set u.userName = ?1 where u.id = ?2",nativeQuery = true) void modifyUsernameById(String userName, Long id); @Modifying @Query(value = "delete from User where id = ?1",nativeQuery = true) void deleteByUserId(Long id); @Query(value = "select u from User u where u.id = ?1",nativeQuery = true) User findByUserId(Long id);本文主要讲解了 Jpa 的一些简单的操作,下篇文章将讲解 Jpa 如何使用 Specification 实现复杂的查询,如多表查询,模糊查询,日期的查询等 Java开发,配置线程池时线程数应该怎么设置 https://www.ydyno.com/archives/1555.html 2022-05-20T16:41:00+08:00 合理的设置线程数能有效提高 CPU 的利用率,设置线程数又得区分任务是CPU密集型还是 IO密集型。解释CPU密集型 就是需要大量进行计算任务的线程,如:计算1+2+3+...、计算圆周率、视频解码等,这种任务本身不太需要访问I/O设备,CPU的使用率高;IO密集型 就是任务运行时大部分的时间都是CPU在等I/O (硬盘/内存) 的读/写操作,如:查询数据库、文件传输、网络请求等,CPU的使用率不高。根据经验1、CPU密集型:线程数少一点,推荐:CPU内核数 + 1 2、IO密集型:线程数多一些,推荐:CPU内核数 * 2 3、混合型:可以将CPU密集和IO密集的操作分成两个线程池去执行即可!PS:这种方式可能会被面试官找茬根据计算公式根据《Java并发编程实战》书中的计算线程数的公式Ncpu = CPU的数量 Ucpu = 目标CPU的使用率, 0 <= Ucpu <= 1 W/C = 等待时间与计算时间的比率 为保持处理器达到期望的使用率,最优的池的大小等于: Nthreads = Ncpu x Ucpu x (1 + W/C)实战假如在一个请求中,计算操作需要10ms,DB操作需要100ms,对于一台2个CPU的服务器,设置多少合适假设我们需要CPU的使用率达到100%,那么套入公式:`2 x 1 x (1 + 100/10) = 22`但是实际开发中,可能有各种因素的影响,因此就需要我们在这个结果的基础上进行压力测试,最终得到一个完美的线程数量最后补个网图,解释了CPU密集型、IO密集型