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( ...
随机推荐
- 图解PCB布线数字地、模拟地、电源地,单点接地抗干扰!
我们在进行pcb布线时总会面临一块板上有两种.三种地的情况,傻瓜式的做法当然是不管三七二十一,只要是地 就整块敷铜了.这种对于低速板或者对干扰不敏感的板子来讲还是没问题的,否则可能导致板子就没法正常工 ...
- USB Mass Storage大容量存储 The Thirteen Class章节的理解
http://blog.csdn.net/xgbing/article/details/7002558 USB Mass Storage 6.7 The Thirteen Class章节的理解 Cas ...
- 求平方根C++
求平方根,正根.曾经都不会.昨天看数学,看到了,写了出来.自己又小优化了一下,非常不错. // squareRoot.cpp -- 2011-08-29-01.04 #include "st ...
- SEO从理论到实践
GITHUB:http://www.liu12fei08fei.top/blog/12seo.html 明白seo是什么 知道怎么做 SEO从理论到实践 什么是SEO? SEO和SEM的区别 SEO和 ...
- C#调用 Oracle 存储过程样例代码
-- 建表 CREATE TABLE sale_report ( sale_date DATE NOT NULL , sale_item VARCHAR(2) NOT NULL , ...
- 【从零学习openCV】IOS7人脸识别实战
前言 接着上篇<IOS7下的人脸检測>,我们顺藤摸瓜的学习怎样在IOS7下用openCV的进行人脸识别,实际上非常easy,因为人脸检測部分已经完毕,剩下的无非调用openCV的方法对採集 ...
- Major GC 是清理老年代。 Full GC 是清理整个堆空间—包括年轻代和老年代。
Major GC 是清理老年代. Full GC 是清理整个堆空间—包括年轻代和老年代.
- sql把一个表的某几列的数据存到另一个表里
一.如何用slq语句把一个表中的某几个字段的数据插入到另一个新表中,就要用下面这条slq语句: insert into 表名1(字段1,字段2) select 字段1,字段2 from 表名2 ...
- iOS7以下设备获取mac地址
注意,是iOS7以下的设备,直接上源码,获取mac地址都是为了唯一标识一个设备的,但iOS7设备的mac地址为 020000000000 MacAddress.h #include <sys/s ...
- navicat for mysql中添加注释
mysql; # 这注释持续到行尾 mysql; -- 这注释持续到行尾 mysql ; mysql+ /* 这是 多行注释 */