这是我参与8月更文挑战的第22天,活动详情查看:8月更文挑战
前言
在拥有共享数据的多条线程并行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。针对线程安全问题,
jdk1.5推出了java.util.concurrent并发包来解决这些问题。
new Thread的弊端
- 新建对象,性能差
- 线程缺乏统一管理,可能无限制的新建线程,相互竞争,严重时会占用过多的系统资源,导致死机或
OOM
ThreadPool - 线程池
- 重用存在的线程,减少对象新建,销毁的开销。
- 线程总数可控,控制资源的利用率。
- 避免过多线程资源竞争,避免阻塞。
- 提供额外功能,定时执行,定期执行,监控等。
线程池的种类
在JUC包中提供了工具类Executors(调度器)对象来创建线程池,可以创建的线程池有四种:
- CachedThreadPool - 可缓存线程池
- FixedThreadPool - 定长线程池
- SingleThreadExecutor - 单线程池
- ScheduledThreadPool - 调度线程池
CachedThreadPool
public class ThreadPoolSample1 {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newCachedThreadPool();
for(int i = 1 ; i <= 1000 ; i++) {
final int index = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":" + index);
}
});
}
try {
// 给线程足够的运行时间
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadPool.shutdown();
}
}
Executors.newCachedThreadPool()创建一个可缓存线程池Executors调度器对象ExecutorService用于管理线程池shutdown()代表关闭线程池(等待所有线程完成)shutdownNow()代表立即终止线程池的运行,不等待线程,不推荐使用 特点 可缓存线程池的特点是,无限大,如果线程池中没有可用的线程则创建,有空闲线程则利用起来
FixedThreadPool
public class ThreadPoolSample2 {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(10);
for(int i = 1 ; i <= 1000 ; i++) {
final int index = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":" + index);
}
});
}
//给线程足够的运行时间
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadPool.shutdown();
}
}
Executors.newFixedThreadPool(10)创建一个可创建一个定长线程池 特点 定长线程池的特点是固定线程总数,空闲的线程用于执行任务,如果线程都在使用,后续任务则处于等待状态,在线程池中的线程执行完任务后再执行后续的任务。如果任务处于等待的状态,备选的等待算法默认为FIFO(先进先出),LIFO(后进先出)
SingleThreadExecutor
public class ThreadPoolSample3 {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor();
for(int i = 1 ; i <= 1000 ; i++) {
final int index = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":" + index);
}
});
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadPool.shutdown();
}
}
Executors.newSingleThreadExecutor()创建一个单线程线程池 特点 利用线程池便于管理
ScheduledThreadPool
public class ThreadPoolSample4 {
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
/*延迟三秒执行一次Run方法
scheduledThreadPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println("延迟3秒执行");
}
} , 3 , TimeUnit.SECONDS);*/
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(new Date() + "延迟1秒执行,每三秒执行一次");
}
}, 1, 3, TimeUnit.SECONDS);
}
}
Executors.newScheduledThreadPool(5)创建一个可调度线程池schedule()延迟执行scheduleAtFixedRate()间隔多久执行一次 特点 可以实现定时调度
经典应用
数据库连接池
在系统启动时在线程池中创建好数据库连接,使用时拿来即用,减少用户连接等待时间,将连接的耗时提前到项目启动阶段,提高用户体验。
总结
线程安全与线程不安全区别
- 线程安全
- 优点:可靠
- 缺点:执行速度慢
- 使用建议:需要线程共享时使用
- 线程不安全
- 优点:速度快
- 缺点:结果可能与预期不符
- 使用建议:在线程内部使用,无需线程间共享
常见的线程安全/不安全的类
| 线程安全 | 线程不安全 |
|---|---|
| Vector | ArrayList、LinkedList |
| Properties | HashSet、TreeSet |
| StringBuffer | StringBuilder |
| HashTable | HashMap |