- JAVAOOP-19线程
-
- 一. 线程相关类Runtime,Timer使用
-
- 二. 线程之间通讯wait,notify
- 三. 线程池
- 四.线程生命周期
- 五.单例模式
-
- 单例模式:
-
- 1.饿汉模式:
- 2. 懒汉模式:
JAVAOOP-19线程
一. 线程相关类Runtime,Timer使用
Runtime类是一个单例类
-
每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。通过 getRuntime 方法获取当前运行时。
-
案例:自动关机
package com.yunhe.thread;import java.io.IOException;public class RunTimeDemo { public static void main(String[] args) throws IOException { Runtime r=Runtime.getRuntime();r.exec("shutdown -s -t 300");//300秒后关机r.exec("shutdown -a");//取消关机} }
Timer:
? 一种工具,用于在后台线程中执行的任务。
? 可安排任务执行一次,
? 或者定期重复执行。
? 定时器在子线程中执行
普通方法 | 说明 |
---|---|
public void schedule(TimerTask task, long delay) | 安排在指定延迟后执行指定的任务。 |
public void schedule(TimerTask task, long delay, long period) | 安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。 |
public void schedule(TimerTask task, Date firstTime, long period) | 安排指定的任务在指定的时间开始进行重复的固定延迟执行。 |
TimerTask:
由 Timer 安排为一次执行或重复执行的任务。
普通方法 | 说明 |
---|---|
abstract void run() | 此计时器任务要执行的操作。 |
boolean cancel() | 取消此计时器任务。 |
?
?
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;public class Demo11 {
public static void main(String[] args){
test3();}// 1 秒后执行任务public static void test1(){
Timer timer = new Timer();timer.schedule(new TimerTask() {
@Overridepublic void run() {
System.out.println("执行任务");}},1000);}// 3 秒后执行任务,每隔 2 秒执行一次public static void test2(){
Timer timer = new Timer();timer.schedule(new TimerTask() {
@Overridepublic void run() {
System.out.println("测试2.."+ new Date());}},3000,2000);}public static void test3(){
Timer timer = new Timer();timer.schedule(new TimerTask() {
int count = 5;@Overridepublic void run() {
System.out.println("任务A: " + count + "..." + Thread.currentThread());count--;if(count==0){
timer.cancel();}}},1000,2000);}}
二. 线程之间通讯wait,notify
Object类的方法:
普通方法 | 说明 |
---|---|
void notify() | 唤醒在此对象监视器上等待的单个线程。 |
void notifyAll() | 唤醒在此对象监视器上等待的所有线程。 |
void wait() | 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。 |
void wait(long timeout) | 在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。 |
//解决死锁的方法:线程间的通信 wait() notify() 等待,唤醒 他们和synchorized搭配使用
public class TestTX {
public static void main(String[] args) throws InterruptedException {
new ThreadE().start(); Thread.sleep(10);new ThreadF().start();}}
class Suo {
public static final Object o=new Object();
}class ThreadE extends Thread{
public void run(){
synchronized (Suo.o) {
System.out.println("1");System.out.println("2");//输出12之后,让他等待,他在等待时,会把当前锁给释放了try {
Suo.o.wait();} catch (InterruptedException e) {
// TODO Auto-generated catch blocke.printStackTrace();}System.out.println("3");System.out.println("4");}}
}class ThreadF extends Thread{
public void run(){
synchronized (Suo.o) {
System.out.println("a");System.out.println("b");System.out.println("c");System.out.println("d");Suo.o.notify();//通知对方线程,你需要的条件满足了,不用等了}}
}
三. 线程池
1. 线程池的作用
线程池作用就是限制系统中执行线程的数量。
根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果。
少了浪费了系统资源,多了造成系统拥挤效率不高。
用线程池控制线程数量,其他线程排 队等候。
一个任务执行完毕,再从队列的中取最前面的任务开始执行。
若队列中没有等待进程,线程池的这一资源处于等待。
当一个新任务需要运行时,如果线程池 中有等待的工作线程,就可以开始运行了;否则进入等待队列。
2. 为什么要用线程池?
1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。
Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。
3. 比较重要的几个类
java.util.concurrent 包
类 | 描述 |
---|---|
ExecutorService | 真正的线程池接口。 |
ScheduledExecutorService | 能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。 |
ThreadPoolExecutor | ExecutorService的默认实现。 |
ScheduledThreadPoolExecutor | 继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。 |
4. 四种线程池
Java通过Executors提供四种线程池,分别为:
1,newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
2,newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
3,newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行。
4,newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
?
Executors:
方法 | 说明 |
---|---|
static ExecutorService newCachedThreadPool() | 创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。 |
static ExecutorService newFixedThreadPool(int nThreads) | 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。 |
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) | 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 |
static ExecutorService newSingleThreadExecutor() | 创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。 |
ExecutorService:
?
方法 | 说明 |
---|---|
void execute(Runnable command) | 在未来某个时间执行给定的命令。 |
示例:
newCachedThreadPool
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 描述: 创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。* 此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。***/
public class TestNewCachedThreadPool {
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();for (int i = 1; i <= 10; i++) {
final int index = i;try {
Thread.sleep(index * 1000);} catch (InterruptedException e) {
e.printStackTrace();}cachedThreadPool.execute(new Runnable() {
@Overridepublic void run() {
String threadName = Thread.currentThread().getName();System.out.println("执行:" + index + ",线程名称:" + threadName);}});}}
}
响应:
执行:1,线程名称:pool-1-thread-1
执行:2,线程名称:pool-1-thread-1
执行:3,线程名称:pool-1-thread-1
执行:4,线程名称:pool-1-thread-1
执行:5,线程名称:pool-1-thread-1
执行:6,线程名称:pool-1-thread-1
执行:7,线程名称:pool-1-thread-1
执行:8,线程名称:pool-1-thread-1
执行:9,线程名称:pool-1-thread-1
执行:10,线程名称:pool-1-thread-1
newFixedThreadPool
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 描述:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。* 线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。***/
public class TestNewFixedThreadPool {
public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);for (int i = 1; i <= 10; i++) {
final int index = i;fixedThreadPool.execute(new Runnable() {
@Overridepublic void run() {
try {
String threadName = Thread.currentThread().getName();System.out.println("执行:" + index + ",线程名称:" + threadName);Thread.sleep(2000);} catch (InterruptedException e) {
e.printStackTrace();}}});}}
}
因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字,和线程名称。
响应:
执行:2,线程名称:pool-1-thread-2
执行:3,线程名称:pool-1-thread-3
执行:1,线程名称:pool-1-thread-1
执行:4,线程名称:pool-1-thread-1
执行:6,线程名称:pool-1-thread-2
执行:5,线程名称:pool-1-thread-3
执行:7,线程名称:pool-1-thread-1
执行:9,线程名称:pool-1-thread-3
执行:8,线程名称:pool-1-thread-2
执行:10,线程名称:pool-1-thread-1
newFixedThreadPool
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 描述:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。* 线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。***/
public class TestNewFixedThreadPool {
public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);for (int i = 1; i <= 10; i++) {
final int index = i;fixedThreadPool.execute(new Runnable() {
@Overridepublic void run() {
try {
String threadName = Thread.currentThread().getName();System.out.println("执行:" + index + ",线程名称:" + threadName);Thread.sleep(2000);} catch (InterruptedException e) {
e.printStackTrace();}}});}}
}
因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字,和线程名称。
响应:
执行:2,线程名称:pool-1-thread-2
执行:3,线程名称:pool-1-thread-3
执行:1,线程名称:pool-1-thread-1
执行:4,线程名称:pool-1-thread-1
执行:6,线程名称:pool-1-thread-2
执行:5,线程名称:pool-1-thread-3
执行:7,线程名称:pool-1-thread-1
执行:9,线程名称:pool-1-thread-3
执行:8,线程名称:pool-1-thread-2
执行:10,线程名称:pool-1-thread-1
newScheduledThreadPool
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;/*** 描述:创建一个定长线程池,支持定时及周期性任务执行。延迟执行***/
public class TestNewScheduledThreadPool {
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);scheduledThreadPool.schedule(new Runnable() {
@Overridepublic void run() {
System.out.println("表示延迟3秒执行。");}}, 3, TimeUnit.SECONDS);scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
@Overridepublic void run() {
System.out.println("表示延迟1秒后每3秒执行一次。");}}, 1, 3, TimeUnit.SECONDS);}}
表示延迟1秒后每3秒执行一次。
表示延迟3秒执行。
表示延迟1秒后每3秒执行一次。
表示延迟1秒后每3秒执行一次。
表示延迟1秒后每3秒执行一次。
表示延迟1秒后每3秒执行一次。
newSingleThreadExecutor
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** 描述:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。***/
public class TestNewSingleThreadExecutor {
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();for (int i = 1; i <= 10; i++) {
final int index = i;singleThreadExecutor.execute(new Runnable() {
@Overridepublic void run() {
try {
String threadName = Thread.currentThread().getName();System.out.println("执行:" + index + ",线程名称:" + threadName);Thread.sleep(2000);} catch (InterruptedException e) {
e.printStackTrace();}}});}}
}
结果依次输出,相当于顺序执行各个任务。
响应:
执行:1,线程名称:pool-1-thread-1
执行:2,线程名称:pool-1-thread-1
执行:3,线程名称:pool-1-thread-1
执行:4,线程名称:pool-1-thread-1
执行:5,线程名称:pool-1-thread-1
执行:6,线程名称:pool-1-thread-1
执行:7,线程名称:pool-1-thread-1
执行:8,线程名称:pool-1-thread-1
执行:9,线程名称:pool-1-thread-1
执行:10,线程名称:pool-1-thread-1
四.线程生命周期
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态。尤其是当线程启动以后,它不可能一直"霸占"着CPU独自运行,所以CPU需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换
\1. 新建状态,当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值
\2. 就绪状态,当线程对象调用了start()方法之后,该线程处于就绪状态。Java虚拟机会为其创建方法调用栈和程序计数器,等待调度运行
\3. 运行状态,如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态
\4. 阻塞状态,当处于运行状态的线程失去所占用资源之后,便进入阻塞状态
\5. 在线程的生命周期当中,线程的各种状态的转换过程
五.单例模式
单例模式:
单例模式是一种对象创建模式,用于生产一个对象的实例,它可以确保系统中一个类只产生一个实例,这样做有两个好处:
1.对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销。
2.由于new操作的次数减少,所以系统内存的使用评率也会降低,这将减少GC压力,缩短GC停顿时间。
由于以上两点可知单例模式的使用对于系统的关键组件和频繁使用的对象来说是可以有效的改善系统的性能的。
单例模式分为饿汉模式和懒汉模式:
1.饿汉模式:
public final class EagerSingleton {
private static EagerSingleton singObj = new EagerSingleton();private EagerSingleton() {
}public static EagerSingleton getSingleInstance() {
return singObj;}}
这种写法就是所谓的饥饿模式,每个对象在没有使用之前就已经初始化了。这就可能带来潜在的性能问题:如果这个对象很大呢?没有使用这个对象之前,就把它加载到了内存中去是一种巨大的浪费。
针对这种情况,我们可以对以上的代码进行改进,使用一种新的设计思想——延迟加载(Lazy-load Singleton)。
2. 懒汉模式:
public final class LazySingleton {
private static LazySingleton singObj = null;private LazySingleton() {
}public static LazySingleton getSingleInstance() {
if (null == singObj ) {
singObj = new LazySingleton();}return singObj;}}
这种写法就是所谓的懒汉模式。它使用了延迟加载来保证对象在没有使用之前,是不会进行初始化的。但是:这种写法线程安全吗?回答必然是:不安全
这是因为在多个线程可能同时运行到“if (null == singObj )”,判断singObj为null,于是同时进行了初始化。所以,这是面临的问题是如何使得这个代码线程安全?很简单,在那个方法前面加一个Synchronized就OK了。
public final class ThreadSafeSingleton {
private static ThreadSafeSingleton singObj = null;private ThreadSafeSingleton() {
}public static Synchronized ThreadSafeSingleton getSingleInstance() {
if (null == singObj ) {
singObj = new ThreadSafeSingleton();}return singObj;}}