- public static void main(String[] args) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
- for (int i = 0; i < 10; i++) {
- new Thread(() -> {
- try {
- log.debug("{}", sdf.parse("1951-04-21"));
- } catch (Exception e) {
- log.error("{}", e);
- }
- }).start();
- }
- }
有很大几率出现 java.lang.NumberFormatException 或者出现不正确的日期解析结果
- public static void main(String[] args) {
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
- for (int i = 0; i < 10; i++) {
- new Thread(() -> {
- synchronized (sdf) {
- try {
- log.debug("{}", sdf.parse("1951-04-21"));
- } catch (Exception e) {
- log.error("{}", e);
- }
- }
- }).start();
- }
- }
这样虽能解决问题,但带来的是性能上的损失,并不算很好
DateTimeFormatter是Java 8引入的新的日期时间格式化类,具有更好的线程安全性和更强大的解析能力,而SimpleDateFormat是早期版本中的日期时间格式化类,使用起来相对简单。
- public static void main(String[] args) {
- DateTimeFormatter sdf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
- for (int i = 0; i < 10; i++) {
- new Thread(() -> {
- try {
- log.debug("{}", sdf.parse("1951-04-21"));
- } catch (Exception e) {
- log.error("{}", e);
- }
- }).start();
- }
- }
以String来举例
- public final class String
- implements java.io.Serializable, Comparable
, CharSequence { - /** The value is used for character storage. */
- private final char value[];
- /** Cache the hash code for the string */
- private int hash; // Default to 0
-
- // ...
-
- }
- 属性用 final 修饰保证了该属性是只读的,不能修改
- 类用 final 修饰保证了该类中的方法不能被覆盖,防止子类无意间破坏不可变性
构造新字符串对象时,会生成新的 char[] value,对内容进行复制 。这种通过创建副本对象来避 免共享的手段称之为【保护性拷贝(defensive copy)】
设置的原理
final变量之前会有一个写屏障,也就是前面的赋值操作不可能走到后面保证了a的值是最新的值。如果不加上a,那么就有可能在多线程调用的时候,a初始值是0,但是还需要赋值一个20,但是由于线程切换,而且a已经算是赋值成功,那么就会直接被使用。如果加上final,那么就会直接给变量赋值20而不是先赋值0再赋值20。
优化原理
如果是final被线程方法调用,那么就会直接把final的值复制一份给这个线程的栈。如果是值太大,那么就会通过字节码指令ldc来存入串池中。如果没有final那么是直接从堆内存中拿,很容易出现线程的问题
享元模式(Flyweight Pattern)是一种结构型设计模式,它通过共享对象来减少内存使用和提高性能。在享元模式中,对象被细粒度地分解为可共享的和不可共享的部分。
通常情况下,一个应用程序中可能会有大量相似的对象,这些对象的大部分属性都是相同的,只有少数属性的值不同。创建和使用这些对象会消耗很多内存,为了优化性能和减少内存占用,可以使用享元模式。
享元模式的核心思想是共享对象。它将对象的状态(即不可变的属性)和可变的状态分离,并通过共享不可变的状态来减少内存使用。可变状态则由外部对象管理。
在享元模式中,共享对象被存储在一个池中(即享元池),需要创建对象时,首先在享元池中查找是否已存在相同状态的对象,如果存在,则直接返回共享的对象;如果不存在,则创建新的对象并将其添加到享元池中。
享元模式的优点包括:
- 减少内存使用:通过共享相同状态的对象,可以减少内存使用,特别是当有大量相似对象时。
- 提高性能:共享对象的重复利用可以提高程序的执行效率。
- 隔离外部状态:将对象的可变状态分离到外部,使得对象在共享的同时,对外部状态的变化是相互独立的。
然而,享元模式也有限制和注意事项:
- 对象的内部状态和外部状态需要分离,外部状态不能影响对象的行为。
- 共享对象需要是不可变的,否则会导致对象状态的不一致。
- 对象的共享和创建逻辑需要细心设计,避免并发问题和错误的共享对象。
在JDK中 Boolean,Byte,Short,Integer,Long,Character 等包装类提供了 valueOf 方法,例如 Long 的 valueOf 会缓存 -128~127 之间的 Long 对象,在这个范围之间会重用对象,大于这个范围,才会新建 Long 对 象:
- public static Long valueOf(long l) {
- final int offset = 128;
- if (l >= -128 && l <= 127) { // will cache
- return LongCache.cache[(int)l + offset];
- }
- return new Long(l);
- }
- @Slf4j
- public class MyPool {
- public static void main(String[] args) {
- Pool pool = new Pool(2);
-
- for(int i=0;i<5;i++){
- new Thread(()->{
- Connection conn = pool.borrow();
- try {
- Thread.sleep(new Random().nextInt(1000));
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- pool.free(conn);
- },"t"+i).start();
- }
-
- }
- }
- @Slf4j(topic = "c.Pool")
- class Pool{
- private final int poolsize;
-
- private Connection[] connections;
-
- private AtomicIntegerArray states;
-
- public Pool(int size){
- this.poolsize=size;
- connections=new MyConnection[size];
- int[] ints = new int[size];
- Arrays.fill(ints,0);
- states=new AtomicIntegerArray(ints);
- for(int i=0;i
- connections[i]=new MyConnection("连接" &