java多线程学习(一)
来源:互联网 发布:booking.it 编辑:程序博客网 时间:2024/06/05 21:11
一 并发的优点
并发的优点大致可以分为两个方面:“速度”和“代码设计”。
1 速度的提升
首先,我们需要知道并发与并行的区别,具体可以看这篇博客:http://blog.csdn.net/yy_james/article/details/71481467
现在的计算机都是多处理器,将一个程序的不同模块分布在不同的CPU上执行,显然可以提高整个程序的运行效率。但是这里需要注意的是,“并发”通常是提高运行在单个处理器上的程序的性能。
这听起来不太对,在同一个CPU上,顺序运行一个程序相比运行一个包含多个部分的并发程序,少了很多任务切换的代价,因为对于并发程序,CPU需要在不通的任务之间切换。所以直观感觉起来,对于单CPU来讲,顺序程序的执行效率更高。
但是有一个问题就是:“阻塞”。对于顺序执行的程序,如果由于某些原因,使程序进入了阻塞状态,那么整个程序都会停止不前;并发程序则不一样,如果一个任务被阻塞了,程序的其他任务还可以继续执行。所以,并发可以大幅度的提高单个CPU的使用效率。
Java中实现并发的方式是线程。与进程相比,线程的创建、管理消耗的资源更少,所以更适合并发程序。但线程也有问题,由于同一个进程的线程共享内存、IO这些资源,所以需要程序员控制这些资源不会同时被多个线程访问。
2 改进代码设计
线程可以使程序员创建更加松散耦合的设计。通俗点说,针对某些复杂的需求,可以将多个任务分配给多个单独的线程来执行;而不需要在一个线程中处理不同的任务(无论是在逻辑上还是代码编写上,这种方法都有问题)。
二 java线程的实现
1 定义一个任务
定义任务的方法有两种,一是实现Runnable接口,二是继承Thread类。无论使用哪种方法,核心都是要重写run()方法。run()方法中的代码,将会在线程开始的时候运行。
(1)实现Runnable接口
public class LiftOffS implements Runnable {@Overridepublic void run() {while(true ){System.out.print("sleep");;try {TimeUnit.MILLISECONDS.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}}
(2)继承Thread类
public class SimpleThread extends Thread {@Overridepublic void run() {while(true ){System.out.print("sleep");;try {TimeUnit.MILLISECONDS.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}}
2 使用线程驱动任务
使用线程驱动任务的方法有两种,一种是使用Thread类,一种是使用Executor,推荐使用第二种,因为Executor便于对线程的管理,而且更加安全(防止任务访问处于不稳定状态的对象)。
(1)Thread驱动(驱动上面的LiftOffS 类的对象)
pulbic static void main(String[] args){Thread t = new Thread(new LiftOffS());t.start();}
(2)Executor驱动
如下,Executor可以一次驱动多个线程(一个线程池的概念),Executor可以有多种实现方式,常用的有以下几种。
下面代码中使用的CachedThreadPool,当一个新的任务被提交,线程池中没有可用的线程时,线程池就会新建一个线程,使用完的线程会被回收到线程池;
FixedThreadPool是固定大小的线程池,线程池在创建时,通过构造函数的参数指定线程池中线程的个数,当新任务得不到线程时,它将会被阻塞,直到线程池中有可用的线程;
SingleThreadPool可以认为是线程个数为1的FixedThreadPool。
ExecutorService executor = Executors.newCachedThreadPool();for(int i=0; i<5; i++){executor.execute(new LiftOffR());}executor.shutdown();
3 创建有返回值的线程
上面的线程是一个独立的任务,不具有返回值,如果希望一个线程具有返回值,那么定义任务时需要实现Cllable接口,而不是Runnable接口。
(1)定义任务,Callable接口具有类型参数的泛型,表示返回值的类型
public class TaskWithResult implements Callable<String> {private int id;public TaskWithResult(int id) {this.id = id;}@Overridepublic String call() throws Exception {return "Task result from task" + id;}}
(2)驱动任务
public static void callbackTest(){ArrayList<Future<String>> result = new ArrayList<Future<String>>();ExecutorService executor = Executors.newCachedThreadPool();for(int i=0; i<5; i++){result.add(executor.submit(new TaskWithResult(i)));}executor.shutdown();for(Future<String> fs : result){try {System.out.println(fs.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}}
在驱动任务时,Executor使用的是submit()方法,而不是execute()方法。
该方法返回一个Future<?>对象,可以通过Future的isDone()方法检查线程是否已经产生返回值,如果产生返回值,则使用Future的get()方法得到返回值;
也可以不使用isDone()方法,直接调用Future的get()方法,当线程还未产生返回值时,get()方法会被阻塞。
4 线程休眠
线程休眠所使用的是sleep()方法,该方法中断线程执行给定的时间。
//旧方法Thread.sleep(1000); //毫秒//新方法TimeUnit.MILLISECONDS.sleep(1000); //毫秒
5 让步
线程的让步使用yield()方法实现,这个方法给线程调度机制一个暗示:我的工作已经做的差不多了,可以让别的线程使用CPU了。
但是这只是一个暗示,没有机制保证其必须被采纳。
Thread.yield();
6 优先级
线程中可以使用setPriority()方法设置优先级,也可以使用getPriority()方法得到优先级。
java中常用的优先级有三种(int型):Thread.MIN_PRIORITY,Thread.MAX_PRIORITY,Thread.NORM_PRIORITY。
调用方法:
Thread.setPriority(Thread.MIN_PRIORITY);
7 后台线程(daemon)
后台线程是指在程序运行的时候在后台提供一种通用服务线程,并且不是程序中不可或缺的部分。因此,当所有非后台进程结束的时候,整个程序也就结束了,同时会杀死进程中的所有后台线程(即使任务的run()方法中有finally块,也不会执行)。
此外,如果一个线程是后台线程,那么它创建的任何线程都是后台线程。
//设置后台线程Thread t = new Thread(new Simple()); //Simple是一个实现了Runnable接口的类t.setDaemon(true);t.start();if(t.isDaemon()){ //判断线程是否是后台线程System.out.println("后台线程");}
8 join
一个线程可以在另一个线程上调用join()方法,效果是等待一段时间,知道第二个线程执行结束后,第一个线程才继续执行。
join()也可以带上一个超时参数,这样,如果目标线程在超时时间内还没有结束,join()也可以继续返回。
public class Joiner extends Thread{private Simple s;public Joiner(Simple s){this.s = s;}public void run(){s.join();doOther();}public static void main(String[] args){Simple s = new Simple();Thread t1 = new Thread(s); //Simple是一个实现了Runnable接口的类Thread t2 = new Thread(new Joiner(s));t1.start();t2.start();}}
9 捕获异常
由于异常是无法在线程之间传递的,所以,由main()方法创建的线程,在出现异常时,无法在main()中捕获,异常信息会直接被打印在控制台中,只有在线程内部才能捕获。
不过java提供了方法,它允许在每个Thread对象上增加一个异常处理器,处理那些未被捕获的异常。
(1)首先,需要自己定义一个处理异常的类,这个类需要实现UncaughtExceptionHandler接口的方法。
public class MyUncaughtExceptionHandler implements UncaughtExceptionHandler {@Overridepublic void uncaughtException(Thread t, Throwable e) {System.out.println("caught "+e);}}
(2)继承线程工厂,配合Executor为每个线程增加异常处理器
public class HandlerThreadFactory implements ThreadFactory {@Overridepublic Thread newThread(Runnable r) {System.out.println(this+" creating new thread");Thread t = new Thread(r);System.out.println("created "+t);t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());System.out.println("en="+t.getUncaughtExceptionHandler());return t;}}
(3)使用线程驱动任务
public class ExceptionThread implements Runnable {@Overridepublic void run() {Thread t = Thread.currentThread();System.out.println("en="+t.getUncaughtExceptionHandler());throw new RuntimeException();}public static void main(String[] args){/* 可以通过这种方式,设置默认的UncaughtExceptionHandlerThread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());ExecutorService executor = Executors.newCachedThreadPool();*/ExecutorService executor = Executors.newCachedThreadPool(new HandlerThreadFactory());executor.execute(new ExceptionThread());executor.shutdown();}}
- java多线程学习(一)
- JAVA多线程学习(一)
- java多线程学习(一)
- JAVA多线程学习(一)
- JAVA多线程学习---(一)
- JAVA多线程学习(一)
- Java多线程学习(一)
- java多线程学习(一)
- java多线程学习(一)
- java多线程学习(一)
- Java学习-多线程(一)
- java多线程学习一
- 学习JAVA多线程程序设计(一)
- Java多线程学习总结(一)
- java多线程学习总结(一)
- Java学习经验(一)多线程初识
- Java多线程学习总结(一)
- Java中多线程学习总结(一)
- jquery的输入框自动补全功能+ajax
- Put Delete 请求报错Not Allow Method的解决方案
- spring activiti整合记录
- 源码阅读--xutil3
- CentOS NTP服务搭建
- java多线程学习(一)
- 最大连续和(分治)
- java.lang.NumberFormatException: Invalid double: ""
- asp.net 2.0 分析器错误消息: 文件.aspx.cs”不存在错误
- web服务器响应码及解释
- 【数据结构--经典算法】PHP实现各种经典算法
- Gamma函数(伽玛函数)的一阶导数、二阶导数公式推导及java程序
- 解决Minimum supported Gradle version is 3.3. Current version is 2.14.1问题
- 机器学习