当前位置: 首页 > 编程日记 > 正文

Lock和Synchronize区别详解

1、两者所处层面不同
synchronized是Java中的一个关键字,当我们调用它时会从在虚拟机指令层面加锁,关键字为monitorenter和monitorexit
Lock是Java中的一个接口,它有许多的实现类来为它提供各种功能,加锁的关键代码为大体为Lock和unLock;
2、获锁方式
synchronized可对实例方法、静态方法和代码块加锁,相对应的,加锁前需要获得实例对象的锁或类对象的锁或指定对象的锁。说到底就是要先获得对象的监视器(即对象的锁)然后才能够进行相关操作。
Lock的使用离不开它的实现类AQS,而它的加锁并不是针对对象的,而是针对当前线程的,并且AQS中有一个原子类state来进行加锁次数的计数
3、获锁失败
使用关键字synchronized加锁的程序中,获锁失败的对象会被加入到一个虚拟的等待队列中被阻塞,直到锁被释放;1.6以后加入了自旋操作
使用Lock加锁的程序中,获锁失败的线程会被自动加入到AQS的等待队列中进行自旋,自旋的同时再尝试去获取锁,等到自旋到一定次数并且获锁操作未成功,线程就会被阻塞
4、偏向或重入
synchronized中叫做偏向锁
当线程访问同步块时,会使用 CAS 将线程 ID 更新到锁对象的 Mark Word 中,如果更新成功则获得偏向锁,并且之后每次进入这个对象锁相关的同步块时都不需要再次获取锁了。
Lock中叫做重入锁
AQS的实现类ReentrantLock实现了重入的机制,即若线程a已经获得了锁,a再次请求锁时则会判断a是否持正有锁,然后会将原子值state+1来实现重入的计数操作
5、Lock独有的队列
condition队列是AQS中的一个Lock的子接口的内部现类,它一般会和ReentrantLock一起使用来满足除了加锁和解锁以外的一些附加条件,比如对线程的分组和临界数量的判断(阻塞队列)
6、解锁操作
synchronized:不能指定解锁操作,执行完代码块的对象会自动释放锁
Lock:可调用ulock方法去释放锁比synchronized更灵活

来看下面具体例子:

public class DeadLockTest implements Runnable{

	//final ReentrantLock lock = new ReentrantLock();
	
	static String source1 = "A";
	static String source2 = "B";
	
	public void run() {
		if(Thread.currentThread().getName().equals("1")) {
			synchronized(source1) {
			//lock.lock();
				System.out.println("线程1获取资源1锁");
				try {
					Thread.currentThread().sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				//lock.lock();
				synchronized(source2) {
					System.out.println("线程1获取资源2锁");
					try {
						Thread.currentThread().sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
				}
				}
			}
					//lock.unlock();
				//lock.unlock();
		}else {
			synchronized(source2) {
			//lock.lock();
				System.out.println("线程2获取资源2锁");
				try {
					Thread.currentThread().sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				synchronized(source1) {
				//lock.lock();
					System.out.println("线程2获取资源1锁");
					try {
						Thread.currentThread().sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				//lock.unlock();
				//lock.unlock();
				}
				}
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		DeadLockTest run = new DeadLockTest();
		new Thread(run,"1").start();
		new Thread(run,"2").start();
	}
}

上述代码使用JVM关键字synchronized简单的实现了一个死锁的效果

我们先来分析一下使用synchronized关键字得出的结果:
结果:

线程1获取资源1锁
线程2获取资源2

分析:
我们由两个资源source1和source2、两个线程1和2,这两个线程都想获取资源source1和source2
首先,最好先将两个资源source1和source2设置为static属性,让两个线程能够共享它
source1和source2都是String类型的,因为synchronized关键字修饰的应该是一个对象,普通的变量(int、float等)不能获取对象锁
接下来,两个资源
1、线程1使用synchronized来对资源source1加锁(即资源source1尝试获取对象锁),成功后获取资源source1,接下来调用sleep方法阻塞,但不释放对象锁
2、线程2使用synchronized来对资源source2加锁,成功后获取资源source2,接下来调用sleep方法阻塞,但不释放对象锁
3、线程1醒来,再次使用synchronized来对资源source2加锁,因为资源source2已被线程2所占用,资源source2还没有释放自己的锁,所以线程1阻塞,等待资源source2释放锁
4、线程2醒来,再次使用synchronized来对资源source1加锁,因为资源source1已被1所占用,资源source1还没有释放自己的锁,所以线程2阻塞,等待资源source1释放锁
5、线程1和线程2都等待着资源source1和资源source2释放锁,导致死锁

接下来,我们将synchronized关键字注释掉,注释掉的ReentrantLock锁对象,然后打开lock和unlock的注释,运行结果如下:

线程2获取资源2锁
线程2获取资源1锁
线程1获取资源1锁
线程1获取资源2

1、线程2尝试获取ReentrantLock对象lock,成功后加锁,并使用资源2,然后睡眠,阻塞自己
2、线程1尝试获锁去lock,因为线程2正持有此对象并且还未释放锁,所以线程1获锁失败,并被加入到等待队列中然后阻塞
3、线程2醒来,再次尝试获锁,因为ReentrantLock可重入,所以再次获锁成功,于是线程2使用资源1,然后两次释放锁。释放完毕后,会唤醒在等待队列中等待的线程1
4、线程1尝试获锁,因为ReentrantLock对象空着,所以获锁成功,然后执行流程如上述第三步
那么同样的代码使用ReentrantLock为什么不会产生死锁呢?
第一、 因为synchronized关键字是JVM为我们提供的属性,sleep方法也是一个native方法,两者确实应该配套使用。而ReentrantLock锁是Java类库中提供的一个类,使用Java代码来模拟锁,synchronized和ReentrantLock在层次级别上不同
第二、 两者实现原理稍有不同。synchronized修饰的必定是一个对象,并且每一个对象都有着属于自己的锁记录指针,我们每次加锁或释放锁都是修改此指针的指向
而ReentrantLock的内部类实现了AQS部分方法,实现了对线程操作的功能。每次加锁失败会将当前线程放入等待队列中,并阻塞它;否则,当前尝试获取资源的线程可以无阻的执行
即synchronized操作的是对象,ReentrantLock操作的是线程

ReentrantLock产生死锁的过程:

public class IntLock implements Runnable{
	private boolean flag;
	private static ReentrantLock lock1=new ReentrantLock();
	private static ReentrantLock lock2=new ReentrantLock();

	public IntLock(boolean flag) {
	    this.flag = flag;
	}

	@Override
	public void run() {
	    try {
	        if(flag){
	            lock1.lock();
	            System.out.println(flag + "线程获取了Lock1");
	            Thread.currentThread().sleep(100);
	            lock2.lock();
	            System.out.println(flag+"线程获取了Lock2");
	        }else{
	            lock2.lock();
	            System.out.println(flag + "线程获取了Lock2");
	            Thread.currentThread().sleep(100);
	            lock1.lock();
	            System.out.println(flag+"线程获取了Lock1");
	        }
	    } catch (InterruptedException e) {
	        e.printStackTrace();
	    } finally {
	        if(lock1.isHeldByCurrentThread()){
	            lock1.unlock();
	        }
	        if(lock2.isHeldByCurrentThread()){
	            lock2.unlock();
	        }
	    }
	}

	public static void main(String[] args) throws InterruptedException {
	    Thread thread1=new Thread(new IntLock(true));
	    Thread thread2=new Thread(new IntLock(false));
	    thread1.start();
	    thread2.start();
	    thread1.join();
	    thread2.join();
	    System.out.println("主线程已结束");
	}
}

以上代码创建了两个ReentrantLock对象,同时开启两个副线程去进行加锁,每个线程对应各自的ReentrantLock对象。当线程1持有了它的锁对象lock1,并且线程2持有了它的对象锁lock2后两个线程再次尝试获取对方的对象锁时,因为ReentrantLock为排他锁(即一把锁同时只能被一个线程锁持有),所以其它线程去获取当前线程的ReentrantLock时就会被放入此ReentrantLock的等待队列中被阻塞,这样就造成了死锁。

我们使用ReentrantLock时最好用static来修饰它的对象来确保此对象被所有线程共享

附上死锁产生原因和避免死锁方法:
以下资源来自网络:
死锁产生条件:
1、 互斥条件:一个资源每次只能被一个进程使用,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。

2、 请求与保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。

3、 不可剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能 由获得该资源的进程自己来释放(只能是主动释放)。

4、 循环等待条件: 若干进程间形成首尾相接循环等待资源的关系

这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。



两者区别:

1.首先synchronized是java内置关键字,在jvm层面,Lock是个java类;
2.synchronized无法判断是否获取锁的状态,Lock可以判断是否获取到锁;
3.synchronized会自动释放锁(a 线程执行完同步代码会释放锁 ;b 线程执行过程中发生异常会释放锁),Lock需在finally中手工释放锁(unlock()方法释放锁),否则容易造成线程死锁;
4.用synchronized关键字的两个线程1和线程2,如果当前线程1获得锁,线程2线程等待。如果线程1阻塞,线程2则会一直等待下去,而Lock锁就不一定会等待下去,如果尝试获取不到锁,线程可以不用一直等待就结束了;
5.synchronized的锁可重入、不可中断、非公平,而Lock锁可重入、可判断、可公平(两者皆可)
6.Lock锁适合大量同步的代码的同步问题,synchronized锁适合代码少量的同步问题。

相同点:
Lock是一个接口,为了使用一个Lock对象,需要用到
Lock lock = new ReentrantLock();
与 synchronized (someObject) 类似的,lock()方法,表示当前线程占用lock对象,一旦占用,其他线程就不能占用了。
与 synchronized 不同的是,一旦synchronized 块结束,就会自动释放对someObject的占用。 lock却必须调用unlock方法进行手动释放,为了保证释放的执行,往往会把unlock() 放在finally中进行。
synchronized 是不占用到手不罢休的,会一直试图占用下去。
与 synchronized 的钻牛角尖不一样,Lock接口还提供了一个trylock方法。
trylock会在指定时间范围内试图占用。 如果时间到了,还占用不成功,就选择放弃。
注意: 因为使用trylock有可能成功,有可能失败,所以后面unlock释放锁的时候,需要判断是否占用成功了,如果没占用成功也unlock,就会抛出异常
使用synchronized方式进行线程交互,用到的是同步对象的wait,notify和notifyAll方法
Lock也提供了类似的解决办法,首先通过lock对象得到一个Condition对象,然后分别调用这个Condition对象的:await, signal,signalAll 方法
注意: 不是Condition对象的wait,nofity,notifyAll方法,是await,signal,signalAll
不同点:

  1. Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现,Lock是代码层面的实现。
  2. Lock可以选择性的获取锁,如果一段时间获取不到,可以放弃。synchronized不行,会一根筋一直获取下去。 借助Lock的这个特性,就能够规避死锁,synchronized必须通过谨慎和良好的设计,才能减少死锁的发生。
  3. synchronized在发生异常和同步块结束的时候,会自动释放锁。而Lock必须手动释放, 所以如果忘记了释放锁,一样会造成死锁。


Synchronized 用过吗,其原理是什么?synchronized 的作用?synchronized 和 lock 区别?synchronized是重量级锁还是轻量级锁?是重入锁吗?

Synchronized 用过吗,其原理是什么

Synchronized 是 Java 中用于实现同步的关键字,它可以用于方法和代码块。Synchronized 的原理是,它会使用对象的内置锁(也称为监视器锁)来实现同步,每个对象都有一把内置锁,当一个线程访问一个同步代码块时,它会尝试获取这个锁,如果锁被其他线程持有,则该线程将被阻塞,直到锁被释放。

Synchronized 有以下几个特点:

  1. 互斥性:Synchronized 保证同一时刻只有一个线程可以获取锁,并且只有该线程可以执行同步代码块中的代码。
  2. 可重入性:同一个线程可以多次获取同步锁而不会被阻塞,这样可以避免死锁的发生。
  3. 独占性: 如果一个线程获得了对象的锁,则其他线程必须等待该线程释放锁之后才能获取锁。

4.缺点:非公平锁 ,当锁被释放后,任何一个线程都有机会竞争得到锁,这样做的目的是提高效率,但缺点是可能产生线程饥饿现象。

除了使用 Synchronized 关键字之外,Java 还提供了一些其他的同步机制,例如 ReentrantLock、Semaphore、CountDownLatch 等。在选择同步机制时,需要根据具体的应用场景和需求来选择合适的同步方式。

synchronized 的作用?

在Java中,synchronized 关键字是用来控制线程同步的,

在多线程的环境下,控制synchronized 代码段不被多个线程同时执行

synchronized 既可以加在一段代码上,也可以加在方法上

synchronized 和 lock 区别?

类别synchronizedlock
存在层次Java 关键字,JVM层面是一个类
锁的释放JVM将确保锁会获得自动释放lock必须在finally块中释放
锁状态无法判断可以判断
锁性能可重入,不可中断、非公平可重入、可判断、可公平(两者皆可默认非公平)
性能少量同步大量同步

synchronized是重量级锁还是轻量级锁?

synchronized重量级锁(Heavyweight Lock)。

在Java中,锁有不同的实现方式,包括重量级锁、轻量级锁和偏向锁。synchronized在多线程同步中通常使用的是重量级锁。

重量级锁的实现涉及到操作系统层面的线程调度和内核态与用户态之间的切换,因此它的性能开销相对较大。在多线程竞争激烈的情况下,重量级锁可能导致性能下降,因为线程需要频繁地切换到内核态进行锁的申请和释放。

轻量级锁和偏向锁是Java虚拟机为了提高synchronized性能而引入的优化机制。这些锁会根据竞争情况自动升级或降级为重量级锁。

总之,synchronized 通常是重量级锁,它确保了多线程访问的互斥性,但在高并发情况下,性能开销较大。 Java虚拟机会根据实际情况尝试使用轻量级锁和偏向锁来提高性能。

synchronized是重入锁吗?

是的,synchronized 是一个重入锁(Reentrant Lock)。

重入锁是指允许同一个线程多次获得同一个锁,而不会因为重复获取锁而被阻塞。这种机制被称为锁的重入性

在Java中,synchronized 具有重入性,这意味着如果一个线程已经获得了某个对象上的锁,那么它可以多次进入被锁定的代码块,而不会被阻塞。这是通过在每个锁对象上维护一个锁计数器来实现的。每次线程获取锁时,计数器加一,释放锁时,计数器减一。只有当计数器归零时,锁才会完全释放,其他线程才能获取该锁。

相关文章:

并发编程下的集合:数组寻址、LinkedList、HashMap、ConcurrentHashMap

如果发现hash取模后的数组索引位下无元素则直接新增,若不是空那就说明存在hash冲突,则判断数组索引位链表结构中的第一个元素的key以及hash值是否与新的key一致则直接覆盖,若不一致则判断当前的数组索引下的链表结构是否为红黑树,若为红黑树则走红黑树的新增方法,若不为红黑树则遍历当前链表结构,遍历中发现某个节点元素的next为null是则直接将新元素指针与next进行关联,若在遍历到next为空前判断到,某个节点的key以及key的hash值与新的key与新的keyhash值一致时则走覆盖。

【日常开发之插件篇】IDEA plugins 神器助我!!

今早因为老代码的一些bug让我突然觉得Idea的一些插件特别好用,我准备将我平时所用到的一些插件做个推荐以及记录。

【日常开发之FTP】Windows开启FTP、Java实现FTP文件上传下载

FTP是一个专门进行文件管理的操作服务,一般来讲可以在任意的操作系统之中进行配置,但是如果考虑到简便性,一般来讲可以直接在Linux系统下进行安装。FTP (File Transfer Protocol、文件传输协议)是TCP/IP协议中的一部分,属于应用层协议。使用FTP最主要的功能是对文件进行管理,所以在FTP内部对于文件支持有两种传输模式:文本模式(ASCII、默认)和二进制模式(Binary),通常文本文件使用ASCIl模式,而对于图片、视频、声音、压缩等文件则会使用二进制的方式进行传输。

【Linux之升华篇】Linux内核锁、用户模式与内核模式、用户进程通讯方式

alloc_pages(gfp_mask, order),_ _get_free_pages(gfp_mask, order)等。字符设备描述符 struct cdev,cdev_alloc()用于动态的分配 cdev 描述符,cdev_add()用于注。外,还支持语义符合 Posix.1 标准的信号函数 sigaction(实际上,该函数是基于 BSD 的,BSD。从最初的原子操作,到后来的信号量,从。(2)命名管道(named pipe):命名管道克服了管道没有名字的限制,因此,除具有管道所具有的。

【Mongdb之数据同步篇】什么是Oplog、Mongodb 开启oplog,java监听oplog并写入关系型数据库、Mongodb动态切换数据源

oplog是local库下的一个固定集合,Secondary就是通过查看Primary 的oplog这个集合来进行复制的。每个节点都有oplog,记录这从主节点复制过来的信息,这样每个成员都可以作为同步源给其他节点。Oplog 可以说是Mongodb Replication的纽带了。

【日常开发之Windows共享文件】Java实现Windows共享文件上传下载

下拉框选择你选择的用户点击添加,然后共享确定。创建一个文件夹然后点击属性界面,点击共享。maven版本存在于SMB协议的兼容问题。首先开启服务,打开控制面板点击程序。点击启用或关闭Windows功能。我这边是专门创建了一个用户。SMB1.0选中红框内的。

CXFServlet类的作用

CXFServlet是Apache CXF框架中的一个核心组件,用于处理HTTP请求并将它们转换为Web服务调用。通过配置CXFServlet,你可以轻松地部署和管理SOAP和RESTful Web服务。

@Scheduled注解的scheduler属性什么作用

注解是 Spring Framework 提供的一种机制,用于定义计划任务,即周期性执行的任务。 注解可以应用于方法上,以指示 Spring 容器在特定的时间间隔或按照某种调度规则来调用该方法。 属性是 注解的一个可选属性,它的作用是允许开发者指定一个自定义的 对象来控制任务的调度方式。默认情况下, 注解使用 Spring 内部的 来执行任务,但如果需要更高级的定制化需求,可以通过 属性指定一个自定义的 实现。自定义调度器:共享调度器资源:高级调度需求:假设你想使用 作为调度器,并且希望所有带有

过滤器、拦截器、aop的先后顺序和作用范围&拦截器preHandle(),postHandle(),afterComplation()方法执行顺序

在Spring框架中,过滤器(Filter)、拦截器(Interceptor)和面向切面编程(AOP)都是用于处理请求和处理流程的组件,但它们的作用范围和触发时机有所不同。下面我会解释这三者的先后顺序和作用范围。执行顺序:请注意,这个顺序可能因具体的配置和使用的技术而有所不同。在实际应用中,建议根据项目的具体需求来合理配置和使用这些组件。拦截器执行流程图:实现拦截器需要实现这个接口,这个 接口中有三个默认方法,这三个方法的执行顺序:我们实现接口然后重写这三个方法,就会在对应的时机被自动执行。这里就是调用处理

Zookeeper概要、协议、应用场景

Zoopkeeper提供了一套很好的分布式集群管理的机制,就是它这种基于层次型的目录树的数据结构并对树中的节点进行有效管理,从而可以设计出多种多样的分布式的数据管理模型,作为分布式系统的沟通调度桥梁。

spring.factories文件的作用

即spring.factories文件是帮助spring-boot项目包以外的bean(即在pom文件中添加依赖中的bean)注册到spring-boot项目的spring容器中。在Spring Boot启动时,它会扫描classpath下所有的spring.factories文件,加载其中的自动配置类,并将它们注入到Spring ApplicationContext中,使得项目能够自动运行。spring.factories文件是Spring Boot自动配置的核心文件之一,它的作用是。

Spring事务七大传播机制与五个隔离级别,嵌套事务

如果当前方法正有一个事务在运行中,则该方法应该运行在一个嵌套事务中,被嵌套的事务可以独立于被封装的事务中进行提交或者回滚。如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。当前方法必须在一个具有事务的上下文中运行,如有客户端有事务在进行,那么被调用端将在该事务中运行,否则的话重新开启一个事务。当前方法必须运行在它自己的事务中。一个新的事务将启动,而且如果有一个现有的事务在运行的话,则这个方法将在运行期被挂起,直到新的事务提交或者回滚才恢复执行。

常见的七种加密算法及实现

**数字签名**、**信息加密** 是前后端开发都经常需要使用到的技术,应用场景包括了用户登入、交易、信息通讯、`oauth` 等等,不同的应用场景也会需要使用到不同的签名加密算法,或者需要搭配不一样的 **签名加密算法** 来达到业务目标。这里简单的给大家介绍几种常见的签名加密算法和一些典型场景下的应用。## 正文### 1. 数字签名**数字签名**,简单来说就是通过提供 **可鉴别** 的 **数字信息** 验证 **自身身份** 的一种方式。一套 **数字签名** 通常定义两种 **互补

7min到40s:SpringBoot 启动优化实践

然后重点排查这些阶段的代码。先看下。

SpringBoot系列教程之Bean之指定初始化顺序的若干姿势

之前介绍了@Order注解的常见错误理解,它并不能指定 bean 的加载顺序,那么问题来了,如果我需要指定 bean 的加载顺序,那应该怎么办呢?本文将介绍几种可行的方式来控制 bean 之间的加载顺序。

在Java中使用WebSocket

WebSocket是一种协议,用于在Web应用程序和服务器之间建立实时、双向的通信连接。它通过一个单一的TCP连接提供了持久化连接,这使得Web应用程序可以更加实时地传递数据。WebSocket协议最初由W3C开发,并于2011年成为标准。

3种方案,模拟两个线程抢票

在多线程编程中,资源竞争是一个常见的问题。资源竞争发生在多个线程试图同时访问或修改共享资源时,可能导致数据不一致或其他并发问题。在模拟两个线程抢票的场景中,我们需要考虑如何公平地分配票,并确保每个线程都有机会成功获取票。本篇文章将通过三种方式来模拟两个线程抢票的过程,以展示不同的并发控制策略。使用 Synchronized 来确保一次只有一个线程可以访问票资源。使用 ReentrantLock 来实现线程间的协调。使用 Semaphore 来限制同时访问票的线程数量。

替代Druid,HakariCP 为什么这么快?

这次源码探究,真的感觉看到了无数个小细节,无数个小优化,积少成多。平时开发过程中,一些小的细节也一定要“扣”。

Java中volatile 的使用场景有哪些?

volatile是一种轻量级的同步机制,它能保证共享变量的可见性,同时禁止重排序保证了操作的有序性,但是它无法保证原子性。所以使用volatilevolatile。

JDK22 正式发布了 !

Java 22 除了推出了新的增强功能和特性,也获得 Java Management Service (JMS) 的支持,这是一项新的 Oracle 云基础设施远程软件服务(Oracle Cloud Infrastructure, OCI) 原生服务,提供统一的控制台和仪表盘,帮助企业管理本地或云端的 Java 运行时和应用。使包含运行时计算值的字符串更容易表达,简化 Java 程序的开发工作,同时提高将用户提供的值编写成字符串,并将字符串传递给其他系统的程序的安全性。支持开发人员自由地表达构造器的行为。

Jackson 用起来!

你可以创建自定义序列化器和反序列化器以自定义特定字段或类的序列化和反序列化行为。为此,请创建一个实现或接口的类,并在需要自定义的字段或类上使用和注解。@Override// ...其他代码...优势性能优异:Jackson在序列化和反序列化过程中表现出优秀的性能,通常比其他Java JSON库更快。灵活性:通过注解、自定义序列化器/反序列化器等功能,Jackson提供了丰富的配置选项,允许你根据需求灵活地处理JSON数据。易于使用:Jackson的API设计简洁明了,易于学习和使用。

拜托!别再滥用 ! = null 判空了!!

另外,也许受此习惯影响,他们总潜意识地认为,所有的返回都是不可信任的,为了保护自己程序,就加了大量的判空。如果你养成习惯,都是这样写代码(返回空collections而不返回null),你调用自己写的方法时,就能大胆地忽略判空)这种情况下,null是个”看上去“合理的值,例如,我查询数据库,某个查询条件下,就是没有对应值,此时null算是表达了“空”的概念。最终,项目中会存在大量判空代码,多么丑陋繁冗!,而不要返回null,这样调用侧就能大胆地处理这个返回,例如调用侧拿到返回后,可以直接。

详解Java Math类的toDegrees()方法:将参数从弧度转换为角度

Java Math 类的 toDegrees() 方法是将一个角度的弧度表示转换为其度表示,返回值为double类型,表示从弧度数转换而来的角度数。这就是Java Math 类的 toDegrees() 方法的攻略。我们已经了解了该方法的基本概念、语法、注意事项以及两个示例。希望这篇攻略对你有所帮助。

SpringBoot接口防抖(防重复提交)的一些实现方案

作为一名老码农,在开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,对接过许多开放平台,也搞过消息中心这类较为复杂的应用,但幸运的是,我至今还没有遇到过线上系统由于代码崩溃导致资损的情况。这其中的原因有三点:一是业务系统本身并不复杂;二是我一直遵循某大厂代码规约,在开发过程中尽可能按规约编写代码;三是经过多年的开发经验积累,我成为了一名熟练工,掌握了一些实用的技巧。啥是防抖所谓防抖,一是防用户手抖,二是防网络抖动。

公司新来一个同事:为什么 HashMap 不能一边遍历一边删除?一下子把我问懵了!

前段时间,同事在代码中KW扫描的时候出现这样一条:上面出现这样的原因是在使用foreach对HashMap进行遍历时,同时进行put赋值操作会有问题,异常ConcurrentModificationException。于是帮同简单的看了一下,印象中集合类在进行遍历时同时进行删除或者添加操作时需要谨慎,一般使用迭代器进行操作。于是告诉同事,应该使用迭代器Iterator来对集合元素进行操作。同事问我为什么?这一下子把我问蒙了?对啊,只是记得这样用不可以,但是好像自己从来没有细究过为什么?

每天一个摆脱if-else工程师的技巧——优雅的参数校验

在日常的开发工作中,为了程序的健壮性,大部分方法都需要进行入参数据校验。最直接的当然是在相应方法内对数据进行手动校验,但是这样代码里就会有很多冗余繁琐的if-else。throw new IllegalArgumentException("用户姓名不能为空");throw new IllegalArgumentException("性别不能为空");throw new IllegalArgumentException("性别错误");

SpringBoot请求转发与重定向

但是可能由于B网址相对于A网址过于复杂,这样搜索引擎就会觉得网址A对用户更加友好,因而在重定向之后任然显示旧的网址A,但是显示网址B的内容。在平常使用手机的过程当中,有时候会发现网页上会有浮动的窗口,或者访问的页面不是正常的页面,这就可能是运营商通过某种方式篡改了用户正常访问的页面。重定向,是指在Nginx中,重定向是指通过修改URL地址,将客户端的请求重定向到另一个URL地址的过程,Nginx中实现重定向的方式有多种,比如使用rewrite模块、return指令等。使用场景:在返回视图的前面加上。

SSO 单点登录和 OAuth2.0 有何区别?

此方法的缺点是它依赖于浏览器和会话状态,对于分布式或者微服务系统而言,可能需要在服务端做会话共享,但是服务端会话共享效率比较低,这不是一个好的方案。在单点登录的上下文中,OAuth 可以用作一个中介,用户在一个“授权服务器”上登录,并获得一个访问令牌,该令牌可以用于访问其他“资源服务器”上的资源。首先,SSO 主要关注用户在多个应用程序和服务之间的无缝切换和保持登录状态的问题。这种方法通过将登录认证和业务系统分离,使用独立的登录中心,实现了在登录中心登录后,所有相关的业务系统都能免登录访问资源。

TCP协议-TCP连接管理

TCP协议是 TCP/IP 协议族中一个非常重要的协议。它是一种面向连接、提供可靠服务、面向字节流的传输层通信协议。TCP(Transmission Control Protocol,传输控制协议)。

接口响应慢?那是你没用 CompletableFuture 来优化!

大多数程序员在平时工作中,都是增删改查。这里我跟大家讲解如何利用CompletableFuture优化项目代码,使项目性能更佳!