Java多线程(1) 创建
一.线程的生命周期及五种基本状态
关于Java中线程的生命周期,首先看一下以下这张较为经典的图:
Java线程具有五中基本状态
新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread();
就绪状态(Runnable):当调用线程对象的start()方法(t.start();)。线程即进入就绪状态。处于就绪状态的线程,仅仅是说明此线程已经做好了准备。随时等待CPU调度运行。并非说运行了t.start()此线程马上就会运行;
执行状态(Running):当CPU開始调度处于就绪状态的线程时。此时线程才得以真正执行,即进入到执行状态。注:就绪状态是进入到执行状态的唯一入口,也就是说,线程要想进入执行状态执行。首先必须处于就绪状态中;
堵塞状态(Blocked):处于执行状态中的线程因为某种原因,临时放弃对CPU的使用权。停止执行。此时进入堵塞状态。直到其进入到就绪状态,才 有机会再次被CPU调用以进入到执行状态。
依据堵塞产生的原因不同。堵塞状态又能够分为三种:
1.等待堵塞:执行状态中的线程执行wait()方法。使本线程进入到等待堵塞状态。
2.同步堵塞 -- 线程在获取synchronized同步锁失败(由于锁被其他线程所占用),它会进入同步堵塞状态;
3.其它堵塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到堵塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完成时。线程又一次转入就绪状态。
【疑问】:锁的持有问题
死亡状态(Dead):线程运行完了或者因异常退出了run()方法,该线程结束生命周期。
二. Java多线程的创建及启动
Java中线程的创建常见有如三种基本形式
1.继承Thread类。重写该类的run()方法。

1 class MyThread extends Thread {
2
3 private int i = 0;
4
5 @Override
6 public void run() {
7 for (i = 0; i < 100; i++) {
8 System.out.println(Thread.currentThread().getName() + " " + i);
9 }
10 }
11 }

1 public class ThreadTest {
2
3 public static void main(String[] args) {
4 for (int i = 0; i < 100; i++) {
5 System.out.println(Thread.currentThread().getName() + " " + i);
6 if (i == 30) {
7 Thread myThread1 = new MyThread(); // 创建一个新的线程 myThread1 此线程进入新建状态
8 Thread myThread2 = new MyThread(); // 创建一个新的线程 myThread2 此线程进入新建状态
9 myThread1.start(); // 调用start()方法使得线程进入就绪状态
10 myThread2.start(); // 调用start()方法使得线程进入就绪状态
11 }
12 }
13 }
14 }

如上所看到的,继承Thread类,通过重写run()方法定义了一个新的线程类MyThread,当中run()方法的方法体代表了线程须要完毕的任务。称之为线程运行体。当创建此线程类对象时一个新的线程得以创建。并进入到线程新建状态。
通过调用线程对象引用的start()方法,使得该线程进入到就绪状态,此时此线程并不一定会立即得以运行,这取决于CPU调度时机。
2.实现Runnable接口,并重写该接口的run()方法。该run()方法相同是线程运行体,创建Runnable实现类的实例,并以此实例作为Thread类的target来创建Thread对象,该Thread对象才是真正的线程对象。

1 class MyRunnable implements Runnable {
2 private int i = 0;
3
4 @Override
5 public void run() {
6 for (i = 0; i < 100; i++) {
7 System.out.println(Thread.currentThread().getName() + " " + i);
8 }
9 }
10 }


1 public class ThreadTest {
2
3 public static void main(String[] args) {
4 for (int i = 0; i < 100; i++) {
5 System.out.println(Thread.currentThread().getName() + " " + i);
6 if (i == 30) {
7 Runnable myRunnable = new MyRunnable(); // 创建一个Runnable实现类的对象
8 Thread thread1 = new Thread(myRunnable); // 将myRunnable作为Thread target创建新的线程
9 Thread thread2 = new Thread(myRunnable);
10 thread1.start(); // 调用start()方法使得线程进入就绪状态
11 thread2.start();
12 }
13 }
14 }
15 }

相信以上两种创建新线程的方式大家都非常熟悉了。那么Thread和Runnable之间究竟是什么关系呢?我们首先来看一下以下这个样例。
1 public interface Runnable {
2
3 public abstract void run();
4
5 }
我们看一下Thread类中对Runnable接口中run()方法的实现:

@Override
public void run() {
if (target != null) {
target.run();
}
}

也就是说,当运行到Thread类中的run()方法时,会首先推断target是否存在。存在则运行target中的run()方法,也就是实现了Runnable接口并重写了run()方法的类中的run()方法。可是上述给到的列子中,因为多态的存在。根本就没有运行到Thread类中的run()方法,而是直接先运行了运行时类型即MyThread类中的run()方法。
3.使用Callable和Future接口创建线程。详细是创建Callable接口的实现类,并实现clall()方法。
并使用FutureTask类来包装Callable实现类的对象。且以此FutureTask对象作为Thread对象的target来创建线程。
看着好像有点复杂,直接来看一个样例就清晰了。

1 public class ThreadTest {
2
3 public static void main(String[] args) {
4
5 Callable<Integer> myCallable = new MyCallable(); // 创建MyCallable对象
6 FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); //使用FutureTask来包装MyCallable对象
7
8 for (int i = 0; i < 100; i++) {
9 System.out.println(Thread.currentThread().getName() + " " + i);
10 if (i == 30) {
11 Thread thread = new Thread(ft); //FutureTask对象作为Thread对象的target创建新的线程
12 thread.start(); //线程进入到就绪状态
13 }
14 }
15
16 System.out.println("主线程for循环运行完成..");
17
18 try {
19 int sum = ft.get(); //取得新创建的新线程中的call()方法返回的结果
20 System.out.println("sum = " + sum);
21 } catch (InterruptedException e) {
22 e.printStackTrace();
23 } catch (ExecutionException e) {
24 e.printStackTrace();
25 }
26
27 }
28 }
29
30
31 class MyCallable implements Callable<Integer> {
32 private int i = 0;
33
34 // 与run()方法不同的是。call()方法具有返回值
35 @Override
36 public Integer call() {
37 int sum = 0;
38 for (; i < 100; i++) {
39 System.out.println(Thread.currentThread().getName() + " " + i);
40 sum += i;
41 }
42 return sum;
43 }
44
45 }

首先,我们发现。在实现Callable接口中。此时不再是run()方法了,而是call()方法,此call()方法作为线程运行体,同一时候还具有返回值。在创建新的线程时,是通过FutureTask来包装MyCallable对象,同一时候作为了Thread对象的target。
那么看下FutureTask类的定义:
1 public class FutureTask<V> implements RunnableFuture<V> {
2
3 //....
4
5 }
1 public interface RunnableFuture<V> extends Runnable, Future<V> {
2
3 void run();
4
5 }
于是。我们发现FutureTask类实际上是同一时候实现了Runnable和Future接口,由此才使得其具有Future和Runnable双重特性。通过Runnable特性,能够作为Thread对象的target。而Future特性,使得其能够取得新创建线程中的call()方法的返回值。
运行下此程序。我们发现sum = 4950永远都是最后输出的。
而“主线程for循环运行完成..”则非常可能是在子线程循环中间输出。由CPU的线程调度机制,我们知道,“主线程for循环运行完成..”的输出时机是没有不论什么问题的,那么为什么sum =4950会永远最后输出呢?
原因在于通过ft.get()方法获取子线程call()方法的返回值时。当子线程此方法还未运行完成,ft.get()方法会一直堵塞,直到call()方法运行完成才干取到返回值。
上述主要解说了三种常见的线程创建方式,对于线程的启动而言。都是调用线程对象的start()方法。须要特别注意的是:不能对同一线程对象两次调用start()方法
Java多线程(1) 创建的更多相关文章
- java多线程-概念&创建启动&中断&守护线程&优先级&线程状态(多线程编程之一)
今天开始就来总结一下Java多线程的基础知识点,下面是本篇的主要内容(大部分知识点参考java核心技术卷1): 1.什么是线程以及多线程与进程的区别 2.多线程的创建与启动 3.中断线程和守护线程以及 ...
- Java多线程的创建(一)
方法一:继承Thread类实现 1.创建一个类A,并继承Thread类 2.重写A的run()方法 3.创建A的实例对象b,即创建了线程对象 4.使用b调用start()方法:启动线程(会自动调用ru ...
- Java多线程的创建与简单使用
一.线程的基本概念 什么是线程:Thread 进程内部的一个执行单元,它是程序中一个单一的顺序控制流程. 线程又被称为轻量级进程(lightweight process) 如果在一个进程中同时运行了多 ...
- java多线程之创建线程的4种方式及Future
Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例.Java可以用四种方式来创建线程: 继承Thread创建线程 实现Runnable接口创建线程 实现callab ...
- Java多线程的创建(二)
前言: 虽然java的API中说创建多线程的方式只有两种(There are two ways to create a new thread of execution),分别是继承Thread类创建和 ...
- java多线程-线程创建
Java 线程类也是一个 object 类,它的实例都继承自 java.lang.Thread 或其子类. 可以用如下方式用 java 中创建一个线程,执行该线程可以调用该线程的 start()方法: ...
- Java多线程-----匿名内部类创建线程
1.继承Thread类创建线程 package com.practise.createthread; public class AnonymousThread { public static v ...
- Java多线程——之一创建线程的四种方法
1.实现Runnable接口,重载run(),无返回值 package thread; public class ThreadRunnable implements Runnable { public ...
- Java多线程之创建线程的三种方式比较
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6560057.html 一:继承Thread类创建线程 1:继承Thread类定义线程子类: 2:重写run( ...
随机推荐
- Date与String间的转换
一.Date-->String :格式化过程 1. DateFormat :String format(Date d) 2.SimpleDateFormat是继承DateFormat(抽象类)的 ...
- 使用Chrome快速实现数据的抓取(三)——JQuery
使用Chrome抓取页面一个非常方便的地方就是它可以执行JS,也就是说我们可以通过JS函数获取我们想要的数据.一个非常强大易用的库就是Jquery,本文就简单的介绍一下使用Chrome获取数据时Jqu ...
- IIS 未能从程序集“System.ServiceModel, Version=3.0.0.0, Culture=neutral,
在Windows Server 2008中的IIS服务器中部署WCF服务程序时,通过浏览器访问报出如下错误: 未能从程序集“System.ServiceModel, Version=3.0.0.0, ...
- HTML5 vs FLASH vs SILVERLIGHT
Introduction HTML5 kills off flash; HTML5 kills off Silverlight; HTML5 makes the dinner and does the ...
- Material Design(原质化设计)视觉设计语言规范 踏得网镜像
Android 5.0 Lollipop(棒棒糖,也就是之前的代称Android L)全面实践了谷歌最新研发的 Material Design 设计语言规范,只是该设计规范并不是仅针对移动平台. 我们 ...
- Windows系统虚拟内存文件和休眠缓存大小优化
虚拟内存的大小设置 虚拟内存的文件 pagefile.sys 一般在系统盘的根目录下,默认情况下会比较大.下面给出缩小设置方式. 我的电脑(鼠标右键)--属性--高级系统设置--切换到“高级”选项卡- ...
- java基础知识概要图
- 【转】利用HTML5开发Android
● Android设备多分辨率的问题 Android浏览器默认预览模式浏览 会缩小页面 WebView中则会以原始大小显示 Android浏览器和WebView默认为mdpi.hdpi相当于mdpi的 ...
- tomcat 的缓存机制
事出做项目时一个jsp页面修改后一直没有读出来,后来仔细研究了下tomcat才发现 当请求jsp页面时,Tomcat会分派给JspServlet来处理,在jspServlet的方法 service() ...
- 【BZOJ】【2738】&【Tsinsen】【A1333】矩阵乘法
整体二分+树状数组 过了[BZOJ][2527][POI2011]Meteors以后这题就没那么难啦~ 关键是[从小到大]依次插入数字,然后整体二分每个查询的第k大是在第几次插入中被插入的……嗯大概就 ...