在进行讲解线程的创建方式之前,首先了解下什么是进程,什么是线程,进程与线程之间的关系等
什么是进程?
其实当一个程序进入内存运行时,就是一个进程,进程是处于运行中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位,具有独立性,动态性,并发性,这里的独立性指的是在系统中独立存在,有独立资源,有独立地址空间,没有进程允许,不会跟别的进程交互;动态性指的是进程在系统中有生命周期以及各种不同的状态,这也是跟程序的区别,进程加入了时间的概念;并发性指的是进程间可以在单处理器上并发执行,独立互不影响
那什么是线程呢?
多线程其实就是扩展了多进程的概念,使一个进程可以同时并发处理多个任务,可以看成是轻量级的进程;线程是进程的组成部分,一个进程可以有多个线程,线程可以有自己的堆栈,程序计数器,局部变量,但是没有系统资源,线程是必须有一个父进程的,他与父进程的其他线程是共享全部资源,线程的调度与管理是由父进程负责为完成
简单来说就是,操作系统可以同时执行多个任务,每个任务就是进程,进程可以同时执行多个任务,每个任务就是线程
如何创建多线程?
创建多线程的方式可以概括为四种:
1,继承Thread类,重写run()方法
2,实现Runnable接口,重写run()方法
3,实现Callable接口, 重写call()方法,借助Future执行
4,借助Executor框架使用线程池创建线程
具体线程创建方式如下:
一:继承Thread类创建线程
class MyThead extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName() + ": 继承Thread线程啦"); } }
调用线层的start()方法启动线程
new MyThead().start();
执行结果如下:
注:Thread其实也是实现了Runnable接口
二:实现Runnable接口创建线程
class MyRunnable implements Runnable { public void run() { System.out.println(Thread.currentThread().getName() + ": 实现Runnable线程啦"); } }
借助Thread类调用线层的start()方法启动线程
new Thread(new MyRunnable()) .start();
执行结果如下:
三:使用Callable和Future接口创建线程
Java5开始提供Callable接口,提供call方法作为线程的执行体,可以看成是Runnable接口的增强版本,增强点在于call()方法可以有返回值,并且可以抛出异常,由于Callable是新增的接口,不能作为Thread的target使用,所以Java5里提供了Future接口,该接口实现了Runnable,Future的实现类FutureTask类用来包装Callable对象,那么该怎么调用并获取返回值呢?下面用代码进行展示用法:
创建Callable对象
class MyCallable implements Callable<Map<String, String>> { public Map<String, String> call() throws Exception { HashMap<String, String> map = new HashMap<String, String>(); map.put("returnCode", "000000"); map.put("messgae", Thread.currentThread().getName() + ":Callable创建线程成功"); return map; } }
启动线程
public static void main(String[] args) { FutureTask<Map<String, String>> future = new FutureTask(new MyCallable()); new Thread(future).start(); try { /** * get()返回Callable任务里的call()返回值 * get方法是一个阻塞方法,对于task内置了一些任务状态,当任务状态为新建0或者初始化完成的时候1的时候会阻塞 * 需要根据设置的时间阻塞,没有设置时会一直进行阻塞,一直到有结果返回 */ Map<String, String> resultMap = future.get(); System.out.println(resultMap); } catch (Exception e) { e.printStackTrace(); } }
执行结果如下:
四:借助Executor框架使用线程池创建线程
- Executors提供了一系列工厂方法用于创先线程池,创建的线程池都实现了ExecutorService接口,下面为常用的线程池:
创建固定数目线程的线程池,操作一个共享的无边界队列,当所有线程都处于活动状态时,额外的任务被提交它们将在队列中等待,直到线程可用。当有线程池挂掉会重新创建一个新的
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
- 创建一个可缓存的线程池,可以创建的范围是0-Integer.MAX_VALUE,当有可用线程时直接使用,当没有时创建新的线程并添加到缓存中,提供使用,这种类型的线程池,适合执行许多短期的异步任务的程序,是在执行方法之前创建线程,60秒内未使用的线程会被终止并删除缓存,
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
- 创建一个单线程化的Executor,只会有一个线程池,当这个线程池挂掉会自动创建一个新的
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
- 创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); }一般来说,CachedTheadPool在程序执行过程中通常会创建与所需数量相同的线程,然后在它回收旧线程时停止创建新线程,因此它是合理的Executor的首选,只有当这种方式会引发问题时(比如需要大量长时间面向连接的线程时),才需要考虑用FixedThreadPool 下面提供一个固定大小的线程池的使用案例:
public static void main(String[] args) { try { ExecutorService threadPool = Executors.newFixedThreadPool(10); for (int i = 0; i <15; i++) {执行结果如下:
//主要通过submit方法执行调用,可以接收Runnable,Callable Future<Map<String, String>> future = threadPool.submit(new MyCallable()); Map<String, String> resultMap = future.get(); System.out.println(resultMap); } threadPool.shutdown(); } catch (Exception e) { e.printStackTrace(); } }
下一篇再介绍相关线程的其他知识,欢迎讨论,一起学习