String和StringBuffer和StringBuilder

String、StringBuffer、StringBuilder

String
Object a = null;
//空指针
String q = a.toString();
//字符串 “null”
String w = String.valueOf(a);
//null
String e = (String) a;
//空指针
String r = String.valueOf(null);
//null
String t = (String) null;

String的值是不可变的,这就导致每次对String对象进行操作时都会生成新的String对象,不仅效率低而且大量浪费有限的内存空间
例如:String a = “a”; //假设a指向地址0x0001
a = “b”; //重新赋值的a指向地址0x0002,但是地址0x0001的“a”依然存在。因此String的操作都是改变赋值地址而不是改变值。

StringBuffer

StringBuffer是可变类和线程安全操作类,任何对它指向的字符串的操作都不会生成新的对象,每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量。当字符串大小超过容量时,会自动增加容量。

StringBuilder

StringBuilder和StringBuffer功能基本相似,主要区别为StringBuffer类的方法是多线程、安全的。StringBuilder不是线程安全的。但StringBuilder速度要快于StringBuffer,若不考虑线程安全的情况下推荐使用StringBuilder。

测试StringBuffer和StringBuilder线程安全

@Test
    public static void main(String[] args) throws InterruptedException {
    
    
        StringBuffer stringBuffer = new StringBuffer();
        StringBuilder stringBuilder = new StringBuilder();
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(200);
        final CountDownLatch countDownLatch = new CountDownLatch(5000);
        for (int i = 0; i < 5000 ; i++) {
    
    
            executorService.execute(() -> {
    
    
                try {
    
    
                    semaphore.acquire();
                    update(stringBuffer);
                    update2(stringBuilder);
                    semaphore.release();
                } catch (Exception e) {
    
    
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        System.out.println("stringBufferSize:"+stringBuffer.length());
        System.out.println("stringBuilderSize:"+stringBuilder.length());
    }
    private static void update(StringBuffer stringBuffer){
    
    
        stringBuffer.append("1");
    }

    private static void update2(StringBuilder stringBuilder){
    
    
        stringBuilder.append("1");
    }

ps:
1. ExecutorService
我们之前使用线程的时候都是使用new Thread来进行线程的创建,但是这样会有一些问题。如:
a. 每次new Thread新建对象性能差。
b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
c. 缺乏更多功能,如定时执行、定期执行、线程中断。
相比new Thread,Java提供的四种线程池的好处在于:
a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。
而我们今天来学习和掌握另外一个新的技能,特别像一个线程池的一个接口类ExecutorService,下面我们来了解下java中Executors的线程池

Java通过Executors提供四种线程池,分别为:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
2. Semaphore
Semaphore也是一个线程同步的辅助类,可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。
Semaphore的主要方法摘要:

void acquire():从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。
void release():释放一个许可,将其返回给信号量。
int availablePermits():返回此信号量中当前可用的许可数。
boolean hasQueuedThreads():查询是否有线程正在等待获取。
3. CountDownLatch
CountDownLatch是一个同步工具类,用来协调多个线程之间的同步,或者说起到线程之间的通信(而不是用作互斥的作用)。

CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成一些任务,然后在CountDownLatch上等待的线程就可以恢复执行接下来的任务。

场景一:让单线程进行等待,多线程完成后,进行汇总合并
countDownLatch.await(); //让main线程进行等待
countDownLatch.countDown(); //计数器每次减1,当计数器减到0时,main线程运行。
场景二:
多线程等待,并发线程一起执行。例如:秒杀。
countDownLatch.await(); //多线程进行等待
countDownLatch.countDown(); //时间到后,开始秒杀

Guess you like

Origin blog.csdn.net/Caozefei_2018/article/details/113940080