Java线程和多线程(一)
2016-06-21 10:27
218 查看
Java 线程是一个轻量级执行任务的处理单元。Java提供了
在应用中存在两种类型的线程,用户线程和守护线程。当我们启动应用的时候,main函数是第一个启动的用户线程,开发者可以在之后自由的创建用户线程。当所有的线程执行完毕,JVM会终结掉程序。
开发者也可以配置不同线程的优先级,但是这并不代表更高优先级的线程会比低优先级的线程先执行。因为线程的执行取决于线程调度器,而线程调度器的实现是基于操作系统的。当一个线程启动后,它的执行便交给了操作系统的线程调度器,JVM不在拥有其控制的权限。
开发者可以通过实现
上面的代码就是最简单的用来创建线程的方式,其中,我们使用到了
多线程指的是2个或者更多的线程来在一个程序中并发的执行任务。单处理的电脑只能在同一时间执行一个线程,时间分片是操作系统给不同的进程线程用来共享处理器时间的。
线程共享其父进程的数据和代码。
线程间的上下文切换的代价比进程间上下文的切换代价小很多。
线程之间的交互相对于进程来说更为简单。
Java提供了两种方式来创建线程。
实现
继承
因为Java为单继承模式,实现
当然,还有
下面的例子是调用
当然,如果读者运行程序的话,会发现实际运行的时间是多余2000毫秒的,这都是基于操作系统的线程调度器。
实际的休眠时间取决于操作系统的定时器以及调度器,对于一个空闲的系统来说,休眠的时间很接近实际的执行时间,但是对于一个繁忙的系统来说,休眠时间会多一些。
线程休眠不会丢失信息,或者锁定当前线程获得的信息。
其他的线程都可以打断当前线程的sleep,那样的话会抛出
下面的例子展示了
输出的结果为:
上面的例子大体可以看出
![](http://img.blog.csdn.net/20160621102524141)
Thread类来支持多线程,开发者在应用中可以创建多个线程来支持并发执行任务。
在应用中存在两种类型的线程,用户线程和守护线程。当我们启动应用的时候,main函数是第一个启动的用户线程,开发者可以在之后自由的创建用户线程。当所有的线程执行完毕,JVM会终结掉程序。
开发者也可以配置不同线程的优先级,但是这并不代表更高优先级的线程会比低优先级的线程先执行。因为线程的执行取决于线程调度器,而线程调度器的实现是基于操作系统的。当一个线程启动后,它的执行便交给了操作系统的线程调度器,JVM不在拥有其控制的权限。
开发者可以通过实现
Runnable接口,或者是继承
Thread类来实现多线程。
Thread t = new Thread(new Runnable(){ @Override public void run() { } });
上面的代码就是最简单的用来创建线程的方式,其中,我们使用到了
Runnable的匿名类。
Java线程
进程和线程是执行的基本单元。Java的并发编程更为关注的是线程。进程
进程是一个包含了所有执行环境的单元,可以将其看作为一个程序或者应用。然而程序本身内部也可能包含多个进程。Java的运行环境是作为一个单独的线程来工作的,但其内部包含了不同的类以及程序。线程
线程也被称作轻量级的进程。线程要求的资源更少,并且存在于进程之中,所有的线程共享进程的资源。Java多线程
每一个Java的应用都至少包含一个线程——主线程。尽管后台也会存在一些其他的线程,例如内存管理,系统管理,限号处理等等,但是从应用来看,主函数是第一个线程,并且我们可以从其中创建多个线程。多线程指的是2个或者更多的线程来在一个程序中并发的执行任务。单处理的电脑只能在同一时间执行一个线程,时间分片是操作系统给不同的进程线程用来共享处理器时间的。
线程的优点
线程与进程相比更为轻量级,创建线程的资源远远少于进程。线程共享其父进程的数据和代码。
线程间的上下文切换的代价比进程间上下文的切换代价小很多。
线程之间的交互相对于进程来说更为简单。
Java提供了两种方式来创建线程。
实现
java.lang.Runnable接口
继承
java.lang.Thread类
Thread
VS Runnable
因为Java为单继承模式,实现Runnable接口更为实用,因为Java支持实现多个接口,但是如果继承了Thread,就无法继承其他的类了。
Java线程休眠
java.lang.Thread提供了一个
sleep()方法可以用来暂停线程指定的毫秒数。其参数不能为负值,否则会抛出
IllegalArgumentException。
当然,还有
sleep(long millis, int nanos)是
sleep()函数的一个重载函数,这个函数可以指定精确到纳秒级,其中,纳秒参数值在0~999999之间。
下面的例子是调用
Thread.sleep()的一个例子,等待时间为2秒。
public class ThreadSleep { public static void main(String[] args) throws InterruptedException { long start = System.currentTimeMillis(); Thread.sleep(2000); System.out.println("Sleep time in ms = "+(System.currentTimeMillis()-start)); } }
当然,如果读者运行程序的话,会发现实际运行的时间是多余2000毫秒的,这都是基于操作系统的线程调度器。
线程休眠关键点
sleep()会暂停当前的线程执行。
实际的休眠时间取决于操作系统的定时器以及调度器,对于一个空闲的系统来说,休眠的时间很接近实际的执行时间,但是对于一个繁忙的系统来说,休眠时间会多一些。
线程休眠不会丢失信息,或者锁定当前线程获得的信息。
其他的线程都可以打断当前线程的sleep,那样的话会抛出
InterruptedException。
线程休眠的原理
Thread.sleep()是和线程调度器来进行交互实现的,该函数会将当前的线程状态置为等待状态指定的时间。一旦等待的时间结束了,线程的状态会重新变为Runnable状态,等待CPU的执行。所以时间线程休眠的时间是跟操作系统的线程调度器相关的。
Java线程的Join
Java Thread Join:该方法用来暂停其他进程的执行,直到当前的线程死亡。一下有3个重载的方法。public final void join():这个方法会将当前执行的线程置于等待状态,直到调用
join方法的线程的执行结束。如果线程被中断,会抛出
InterruptedException.
public final synchronized void join(long millis):这个
join方法是用来等待调用的线程死亡或者是指定的毫秒数。因为线程的执行是依赖于操作系统的实现的,这个操作也不能保证配置的时间为当前线程等待的精确时间。
public final synchronized void join(long millis, int nanos):该
join方法和上面的方法类似,不过可配参数为毫秒和纳秒的组合。
下面的例子展示了
Thread.join方法的一些用法。程序的目标是为了确保主线程是最后一个结束的线程,第三个线程要在第一个线程死亡后才开始执行。
package com.journaldev.threads; public class ThreadJoinExample { public static void main(String[] args) { Thread t1 = new Thread(new MyRunnable(), "t1"); Thread t2 = new Thread(new MyRunnable(), "t2"); Thread t3 = new Thread(new MyRunnable(), "t3"); t1.start(); //start second thread after waiting for 2 seconds or if it's dead try { t1.join(2000); } catch (InterruptedException e) { e.printStackTrace(); } t2.start(); //start third thread only when first thread is dead try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } t3.start(); //let all threads finish execution before finishing main thread try { t1.join(); t2.join(); t3.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("All threads are dead, exiting main thread"); } } class MyRunnable implements Runnable{ @Override public void run() { System.out.println("Thread started:::"+Thread.currentThread().getName()); try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread ended:::"+Thread.currentThread().getName()); } }
输出的结果为:
Thread started:::t1 Thread started:::t2 Thread ended:::t1 Thread started:::t3 Thread ended:::t2 Thread ended:::t3 All threads are dead, exiting main thread
上面的例子大体可以看出
join方法的作用,自定义的线程执行时间为4000ms,因为线程1
join了2秒,所以线程1跟2均启动了,然后线程1重新join,造成了线程3是在线程1执行之后才启动的。之后因为线程2和线程3都进行了
join,所以主线程在最后才结束。
线程状态
下图中展示了Java线程中不同的状态,需要注意的是,当开发者可以创建线程并启动,但是线程的状态如何从Runnable到
Running到
Blocked等状态,是取决于OS中的线程调度算法的,Java本身并不能完全控制。
New
当开发者使用new操作符来创建一个
Thread的时候,线程本身的状态就是
New的状态。在这个时候,线程还没有开始存活,这个状态属于Java编程范围内的一个内部状态。
Runnable
当开发者调用Thread的实例方法
start()以后,线程的状态就从
New状态变成了
Runnable,这个时候,线程的控制权就交给了操作系统的线程调度器来继续线程的执行。是立刻执行该线程,还是将其扔到线程池等待运行就取决于操作系统的线程调度算法了。
Running
当线程正在执行时,其状态就变成了Running状态。线程调度器从线程池中会挑选一个
Runnable状态的线程,将其状态改为
Running然后由CPU开始执行该线程。线程根据时间的分片情况,比如完成线程方法或者等待资源等,可以将其状态改为
Runnable,
Dead或者
Blocked等。
Blocked/Waiting
线程可以处于Waiting状态直到其他线程调用
Thread.join()或者直到一些资源有效。比如,生产者消费者问题或者是等待唤醒或者是IO资源等,这个时候线程的状态是
Waiting状态。一旦线程的等待状态结束了,其状态会重新变成
Runnable,回到
Runnable的线程池之中。
Dead
一旦线程完成了执行,其状态会变成Dead并且认为线程死亡。
相关文章推荐
- Spring 的AOP--spring框架动态实现AOP
- SpringMVC前端调度器 DispatcherServlet web.xml配置
- ECLIPSE配置OSGI服务器
- SpringMVC接收页面表单参数
- C中的无符号整数在java中的处理
- Java反射机制
- IT十八掌掌第十三天课程总结
- eclipse安装配置
- Spring 请求参数(包装类)非法情况下的转换
- spring3.2 @Scheduled注解 定时任务
- 三分钟了解Activity工作流
- Java进阶学习第24天——动态代理与类加载器
- struts2的标签在JS中的使用 中文乱码
- java 利用spring JavaMailSenderImpl发送邮件,支持普通文本、附件、html、velocity模板
- Spring中Quartz的最精简的配置
- java Map 按值排序
- 关于eclipse 打包Strings文件引用错误 解决方法
- myeclipse文件解决乱码问题
- SpringMVC 实例 --非注解
- Java导出pdf表格