一、创建多线程的方式
Java多线程很常见,如何使用多线程,如何创建线程,java中有两种方式,第一种是让自己的类实现Runnable接口,第二种是让自己的类继承Thread类。其实Thread类自己也是实现了Runnable接口。具体使用实例如下:
1、通过实现Runnable接口方式
public class MyThread1 implements Runnable//通过实现Runnable接口方式
{
? ? String sign = "thread#1@";
?
? ? @Override
? ? public void run()
? ? {
? ? ? ? for (int i = 0; i < 30; i++)
? ? ? ? {
? ? ? ? ? ? System.out.println(sign + "->" + i);
? ? ? ? ? ? try
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Thread.sleep(100L);
? ? ? ? ? ? } catch (InterruptedException e)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
? ? }
?
}
2、通过继承Thread类的方式
public class MyThread2 extends Thread//通过继承Thread类的方式
{
? ? String sign = "thread#2@";
?
? ? @Override
? ? public void run()
? ? {
? ? ? ? for (int i = 0; i < 30; i++)
? ? ? ? {
? ? ? ? ? ? System.out.println(sign + "->" + i);
? ? ? ? ? ? try
? ? ? ? ? ? {
? ? ? ? ? ? ? ? Thread.sleep(100L);
? ? ? ? ? ? } catch (InterruptedException e)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? }
? ? }
?
}
再启用上面创建的两种线程,调运代码如下:
public static void main(String[] args) throws InterruptedException
{
? ? long startTime = System.currentTimeMillis();
? ? // 通过主线程启动自己的线程
?
? ? // 通过实现runnable接口
? ? Runnable myThread1 = new MyThread1();
? ? Thread thread1 = new Thread(myThread1);
? ? thread1.start();
?
? ? // 通过继承thread类
? ? Thread thread2 = new Thread(new MyThread2());
? ? thread2.start();
?
? ? // 注意这里不是调运run()方法,而是调运线程类Thread的start方法,在Thread方法内部,会调运本地系统方法,最终会自动调运自己线程类的run方法
?
? ? // 让主线程睡眠
? ? Thread.sleep(1000L);
? ? System.out.println("主线程结束!用时:"
? ? ? ? ? ? + (System.currentTimeMillis() - startTime));
? ? // System.exit(0);
}
? ? 输入结果(每次输入可能不同)不再详细列出。对于上面的两种方式,更推荐使用实现Runnable接口的方式实现多线程。一方面是可以防止java单继承的顾虑,另一方面Runnable是面向接口编程,扩展性比起继承Thread更好。所以尽量使用implement Runnable的方式。
二、java线程类型说明
? ? 上面是将多线程跑起来了,但是有个问题,如果不让主线程睡眠,当主线程(比如main线程)结束以后,如果子线程还没结束,那么子线程是否还会执行呢?答案是会继续执行,为了说明这个问题,就又涉及到java中线程的类型。java中线程一共有两种类型:守护线程(daemon thread)和用户线程(user thread)又叫非守护线程。可以通过thread.setDaemon()方法设置线程是否为守护线程,默认是设置非守护线程(false)。java虚拟机停止运行的时间是虚拟机中运行的所有线程都是守护线程的时候,也就是说如果jvm中没有user thread的时候,jvm就停止运行。或者说jvm在最后一个非守护线程结束的时候,将停止所有的守护进程,然后退出jvm。
? ? 当使用main方法开启线程时,主线程默认是非守护进程,而用户自己开的进程也是非守护进程。当主线程结束,但是子线程(默认是非守护线程)还没结束,所以虚拟机是不停止运行的,当子线程运行完以后,如果主线程也运行完毕,jvm发现没有非守护线程,就将jvm关闭,所以当main方法的主线程执行完毕以后,子线程是会继续执行的。当然我们也可以让在main主线程执行完毕以后,子线程不再执行,方法就是将所有的子线程设置为守护进程setDaemon(true)即可。但是需要注意的是这个设置需要在线程运行之前设置,不能在线程运行的过程中修改线程类型。
? ? 更直白点说如果用户将线程设置为守护进程,那实际的意思就是告诉jvm你不用搭理我这个线程,jvm想停止的时候,不用考虑我这个线程是否执行结束。这种线程具体的使用比如在垃圾回收机制的线程,就是一个守护线程。
Runnable myThread1 = new MyThread1();
Thread thread1 = new Thread(myThread1);
thread1.setDaemon(true);
thread1.start();
?
Thread thread2 = new Thread(new MyThread2());
thread2.setDaemon(false);
thread2.start();
?
System.out.println("mainThread isDaemon:"
? ? ? ? + Thread.currentThread().isDaemon());
System.out.println("thread1 isDaemon:" + thread1.isDaemon());
System.out.println("thread2 isDaemon:" + thread2.isDaemon());
三、线程池的使用
? ? 上面已经看过了可以在代码中直接新起线程,如果我们在主线程中新起一百个线程,让这一百个线程同时工作,逻辑上是没有任何问题的,但是这样做对系统资源的开销很大,这样会在短时间内处理很多的任务,当然包括新起线程等等。基于这样的考虑,我们是有必要引入线程池这个东西的。线程池就是一个池子,池子里有很多可用线程资源,如果需要就直接从这个池子里拿就是。当不用的时候,放入池子中,线程池会自动帮我们管理。所以使用线程池主要有以下两个好处:1、减少在创建和销毁线程上所花的时间以及系统资源的开销 2、如不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存 。
? ? 如果我们想要使用线程池,就需要先定义这个线程池。定义线程池的时候,其中的几个主要参数说明如下:
-corePoolSize(int):线程池中保持的线程数量,包括空闲线程在内。也就是线程池释放的最小线程数量界限。
-maximumPoolSize(int):线程池中嫩容纳最大线程数量。
-keepAliveTime(long):空闲线程保持在线程池中的时间,