线程池优化多线程编程
在Java中的对象是使用new来进行创建的,如果对象的创建量少还好,当创建大量生命周期短的对象时,使用new这种方式效率是比较低下的。使用池技术可以解决这种问题。如数据库连接使用数据库连接池可以大大提高效率,而线程也有线程池。
下面例子中可以看出使用线程池和不使用线程池的效率差别,一种使用普通方式创建5000个生命周期短的线程,另一种是通过线程池技术来创建。
@Test
public void TheandTest() {
Runtime run = Runtime.getRuntime();// 创建Runtime对象
run.gc();// 运行垃圾回收器
long freeMemory = run.freeMemory();// 获得当前虚拟机的空闲内存
long currentTime = System.currentTimeMillis();// 获得当前虚拟机的时间
for (int i = 0; i < 5000; i++) {// 独立运行1000个线程
new Thread(new TempThread()).start();//这里作简单的运算
}
System.out.println("不使用线程池方式创建5000个线程所占用的内存:" + (freeMemory - run.freeMemory()) + "字节");// 查看内存的变化
System.out.println("不使用线程池方式创建5000个线程所消耗的时间:" + (System.currentTimeMillis() - currentTime) + "毫秒");// 查看时间的变化
run.gc();// 运行垃圾回收器
freeMemory = run.freeMemory();// 获得当前虚拟机的空闲内存
currentTime = System.currentTimeMillis();// 获得当前虚拟机的时间
// 创建线程池 3代表线程池的线程数
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5000; i++) {// 使用线程池运行5000个线程
executorService.submit(new TempThread());
}
System.out.println("使用线程池运行5000个线程所占用的内存:" + (freeMemory - run.freeMemory()) + "字节");// 查看内存的变化
System.out.println("使用线程池创建5000个线程所消耗的时间:" + (System.currentTimeMillis() - currentTime) + "毫秒");// 查看时间的变化
}
运行输出
不管是创建占用的内存还是所用的时间,使用线程池创建的效率都是非常高的。
在使用完线程池后,需要调用ExecutorService接口中定义的shutdownNow()方法终止线程池。
Executors类(java.util.concurrent):为Executor、ExectorService、ThreadFactory等类提供工厂和方法。上面例子中使用了newFixedThqreadPool()方法创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。
如果一个线程池中有多个处于可运行的线程,当向线程添加Runnable或Callable接口对象时,会有一个线程执行run或call方法,方法执行后,此时线程并没有终止,而是可运行状态,等待新的任务。
线程插入(加入)
在某些需求中,存在线程A,但业务需要插入线程B,并要线程B先执行完成,再执行线程A,此时可以使用Thread类的join()方法来完成。
Java虚拟机允许程序并发地运行多个执行线程。 join方法是Thqead类的一个静态方法,有3种形式
join()等待调用方法的线程终止
join(long mills)等待该调用方法的线程终止时间最长为mills毫秒
join(long mills,int nanos)等待调用该方法的线程终止时间最长为mills毫秒加nanos纳秒
注:如果有线程中断了运行中的join()方法线程,则会抛出interruptedException异常
下面这个类实现了Runnable接口,并在run()方法中每隔0.5秒输出一句语句。
public class ThreanJoinDemo implements Runnable {
@Override
public void run() {
for (int i = 1; i < 10; i++) {
try {
//线程休眠0.5秒
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是插队:" + i + "号");
}
}
}
再新建一个类,编写main方法,使用上面的ThreanJoinDemo类创建一个新的线程,并使用join()方法中途加入线程。
public static void main(String[] args) {
//创建新的线程
Thread thread = new Thread(new ThreanJoinDemo());
thread.start();//启动线程
for (int i = 1; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( "正常排队"+ i + "号");
try {
thread.join();//加入线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行效果
实现线程同步
多线程开发中,同步方法是比较常用的。同步方法就是有synchronized关键字修饰的方法,
用synchronized修饰的方法就是同步方法,被线程调用时锁住该方法,只有这个线程执行完这个方法才能解锁,被其他线程调用,执行完后再开锁。synchronized可以修饰方法,修饰代码块,但是不能修饰构造器、成员变量等。synchronized修饰静态方法的话,如果调用该静态方法会锁住整个类