如何使用 synchronized?
Synchronized本质上都是依赖对象来锁,根据不同的对象类型,可以分为三种锁粒度: this锁:当前实例锁 class锁:类对象锁 Object锁:对象实例锁 synchronized 关键字的使用方式主要有下面 3 种: 修饰实例成员方法:使用this锁,线程想要执行被Synchronized关键字修饰的普通方法,必须先获取当前实例对象的锁资源; 修饰静态成员方法:使用class锁,线程想要执行被Synchronized关键字修饰的静态方法,必须先获取当前类对象的锁资源; 修饰代码块:使用Object锁,使用给定的对象实现锁功能,线程想要执行被Synchronized关键字修饰的代码块,必须先获取当前给定对象的锁资源。 1、修饰实例方法 (锁当前对象实例)给当前对象实例加锁,进入同步代码前要获得 当前对象实例的锁 。 123synchronized void method()...
乐观锁和悲观锁
什么是悲观锁?悲观锁总是假设最坏的情况,认为共享资源每次被访问的时候就会出现问题(比如共享数据被修改),所以每次在获取资源操作的时候都会上锁,这样其他线程想拿到这个资源就会阻塞直到锁被上一个持有者释放。也就是说,共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。 像 Java 中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。 高并发的场景下,激烈的锁竞争会造成线程阻塞,大量阻塞线程会导致系统的上下文切换,增加系统的性能开销。并且,悲观锁还可能会存在死锁问题,影响代码的正常运行 什么是乐观锁?乐观锁总是假设最好的情况,认为共享资源每次被访问的时候不会出现问题,线程可以不停地执行,无需加锁也无需等待,只是在提交修改的时候去验证对应的资源(也就是数据)是否被其它线程修改了(具体方法可以使用版本号机制或 CAS 算法)。 高并发的场景下,乐观锁相比悲观锁来说,不存在锁竞争造成线程阻塞,也不会有死锁的问题,在性能上往往会更胜一筹。但是,如果冲突频繁发生(写占比非常多的情况),会频繁失败和重试,这样同样会非常影响性能,导致 CPU...
String、StringBuffer、StringBuilder 的区别
可变性String 是不可变的(后面会详细分析原因)。 StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串,不过没有使用 final 和 private 关键字修饰,最关键的是这个 AbstractStringBuilder 类还提供了很多修改字符串的方法比如 append 方法。 12345678910111213abstract class AbstractStringBuilder implements Appendable, CharSequence { char[] value; public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0,...
接口和抽象类的区别
接口和抽象类的共同点 实例化:接口和抽象类都不能直接实例化,只能被实现(接口)或继承(抽象类)后才能创建具体的对象。 抽象方法:接口和抽象类都可以包含抽象方法。抽象方法没有方法体,必须在子类或实现类中实现。 接口和抽象类的区别 设计目的:接口主要用于对类的行为进行约束,你实现了某个接口就具有了对应的行为。抽象类主要用于代码复用,强调的是所属关系。 继承和实现:一个类只能继承一个类(包括抽象类),因为 Java 不支持多继承。但一个类可以实现多个接口,一个接口也可以继承多个其他接口。 成员变量:接口中的成员变量只能是 public static final 类型的,不能被修改且必须有初始值。抽象类的成员变量可以有任何修饰符(private, protected, public),可以在子类中被重新定义或赋值。 方法: Java 8 之前,接口中的方法默认是 public abstract ,也就是只能有方法声明。自 Java 8 起,可以在接口中定义 default(默认) 方法和 static (静态)方法。 自 Java 9 起,接口可以包含 private...
深拷贝和浅拷贝
关于深拷贝和浅拷贝区别: 浅拷贝:浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),不过,如果原对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象。 深拷贝:深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。 上面的结论没有完全理解的话也没关系,我们来看一个具体的案例! 浅拷贝浅拷贝的示例代码如下,我们这里实现了 Cloneable 接口,并重写了 clone() 方法。 clone() 方法的实现很简单,直接调用的是父类 Object 的 clone() 方法。 123456789101112131415161718192021222324252627282930313233343536373839404142434445public class ShallowCopy { public static void main(String[] args) throws CloneNotSupportedException { Teacher teacher = new...
MyISAM 和 InnoDB 有什么区别?
MySQL 5.5 之前,MyISAM 引擎是 MySQL 的默认存储引擎,可谓是风光一时。 虽然,MyISAM 的性能还行,各种特性也还不错(比如全文索引、压缩、空间函数等)。但是,MyISAM 不支持事务和行级锁,而且最大的缺陷就是崩溃后无法安全恢复。 MySQL 5.5 版本之后,InnoDB 是 MySQL 的默认存储引擎。 从锁粒度、事务、外键、性能、安全方面考虑 1、是否支持行级锁MyISAM 只有表级锁(table-level locking),而 InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁。 也就说,MyISAM 一锁就是锁住了整张表,这在并发写的情况下是多么滴憨憨啊!这也是为什么 InnoDB 在并发写的时候,性能更牛皮了! 2、是否支持事务MyISAM 不提供事务支持。 InnoDB 提供事务支持,实现了 SQL 标准定义了四个隔离级别,具有提交(commit)和回滚(rollback)事务的能力。并且,InnoDB 默认使用的 REPEATABLE-READ(可重读)隔离级别是可以解决幻读问题发生的(基于 MVCC...
JVM vs JDK vs JRE
JVMJava 虚拟机(Java Virtual Machine, JVM)是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现(Windows,Linux,macOS),目的是使用相同的字节码,它们都会给出相同的结果。字节码和不同系统的 JVM 实现是 Java 语言“一次编译,随处可以运行”的关键所在 JVM 并不是只有一种!只要满足 JVM 规范,每个公司、组织或者个人都可以开发自己的专属 JVM。 也就是说我们平时接触到的 HotSpot VM 仅仅是是 JVM 规范的一种实现而已。 JDK 和 JREJDK(Java Development Kit),它是功能齐全的 Java SDK,是提供给开发者使用,能够创建和编译 Java 程序的开发套件。它包含了 JRE,同时还包含了编译 java 源码的编译器 javac 以及一些其他工具比如 javadoc(文档注释工具)、jdb(调试器)、jconsole(基于 JMX 的可视化监控⼯具)、javap(反编译工具)等等。 JRE(Java Runtime Environment) 是 Java...
String 为什么是不可变的?
String 为什么是不可变的?String 类中使用 final 关键字修饰字符数组来保存字符串,所以String 对象是不可变的。 1234public final class String implements java.io.Serializable, Comparable<String>, CharSequence { private final char[] value;//...} 🐛 修正:我们知道被 final 关键字修饰的类不能被继承,修饰的方法不能被重写,修饰的变量是基本数据类型则值不能改变,修饰的变量是引用类型则不能再指向其他对象。因此,final 关键字修饰的数组保存字符串并不是 String 不可变的根本原因,因为这个数组保存的字符串是可变的(final 修饰引用类型变量的情况)。 String 真正不可变有下面几点原因: 1. 保存字符串的数组被 final 修饰且为私有的,并且String 类没有提供/暴露修改这个字符串的方法。2. String 类被 final...
== 和 equals() 的区别
== 对于基本类型和引用类型的作用效果是不同的: 对于基本数据类型来说,== 比较的是值。 对于引用数据类型来说,== 比较的是对象的内存地址。 因为 Java 只有值传递,所以,对于 == 来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址。 equals() 不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等。equals()方法存在于Object类中,而Object类是所有类的直接或间接父类,因此所有的类都有equals()方法。 Object 类 equals() 方法: 123public boolean equals(Object obj) {return (this == obj);} equals() 方法存在两种使用情况: 类没有重写 equals()方法:通过equals()比较该类的两个对象时,等价于通过“ == ”比较这两个对象,使用的默认是 Object类equals()方法。 类重写了...