一、进程、线程和任务
进程是程序的运行实例,进程是程序向操作系统申请资源的最小单位(比如申请内存空间和文件句柄)。
线程是进程中可独立执行的最小单位,一个进程可能包括多个线程,同一个进程中的线程共享该进程中的资源。
线程所要完成的计算被称为任务,特定的线程总是在执行特定的任务。
public class SimpleJavaApp {
public static void main(String[] args) throws InterruptedException {
while (true){
System.out.println(new Date());
Thread.sleep(1000);
}
}
}
二、多线程编程简介
略,就是编写多线程的程序。
三、Java线程API简介
1.线程的创、启动与运行
其实创建线程就是创建一个Thread类或其子类的实例;处理线程的任务就是执行它的run方法,而Thread类的start方法就是去启动线程。
Thread类的源码,可以发现有两种构造方式,其一Thread()
无参构造,其二Thread(Runnable target)
添加一个runnable对象到Thread的构造器中。
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, null, gname)}, where {@code gname} is a newly generated
* name. Automatically generated names are of the form
* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
*/
public Thread() {
this(null, null, "Thread-" + nextThreadNum(), 0);
}
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, target, gname)}, where {@code gname} is a newly generated
* name. Automatically generated names are of the form
* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
*
* @param target
* the object whose {@code run} method is invoked when this thread
* is started. If {@code null}, this classes {@code run} method does
* nothing.
*/
public Thread(Runnable target) {
this(null, target, "Thread-" + nextThreadNum(), 0);
}
①Thread子类的形式创建线程:
public class WelcomeApp {
public static void main(String[] args) {
WelcomeThread welcomeThread = new WelcomeThread();
welcomeThread.start();
System.out.printf("1.Welcome! I'm %s.%n",Thread.currentThread().getName());
}
}
class WelcomeThread extends Thread{
@Override
public void run() {
System.out.printf("2.Welcome! I'm %s.%n",Thread.currentThread().getName());
}
}
//...........result............
1.Welcome! I'm main.
2.Welcome! I'm Thread-0.
②创建Runnable接口实例的方式创建线程:
public class WelcomeApp1 {
public static void main(String[] args) {
Thread welcomeThread = new Thread(new WelcomeTask());
welcomeThread.start();
System.out.printf("1.Welcome! I'm %s.%n",Thread.currentThread().getName());
}
}
class WelcomeTask implements Runnable{
@Override
public void run() {
System.out.printf("2.Welcome! I'm %s.%n",Thread.currentThread().getName());
}
}
//..........result.........
1.Welcome! I'm main.
2.Welcome! I'm Thread-0.
线程运行结束后,所占用的资源会被Java虚拟机的垃圾回收器回收。
对于线程,我们只能执行一次start()方法,如果执行多次会报IllgelThreadStateException,但是Java虚拟机并不阻止我们直接调用run方法。
public class ExcepApp {
public static void main(String[] args) {
ExcepThread excepThread = new ExcepThread();
excepThread.start();
excepThread.start();
}
}
class ExcepThread extends Thread{
@Override
public void run() {
System.out.printf("1.Welcome! I'm %s.%n",Thread.currentThread().getName());
}
}
异常的screenshot:
2.Runnable接口
Runnable接口可以看作是对任务的抽象,Thread类也是Runnable接口的一个对象,如下代码。
public class Thread implements Runnable {}
Thread类的run方法源码:
private Runnable target;
@Override
public void run() {
if (target != null) {
target.run();
}
}
二者的区别:
public class ThreadCreationCmp {
public static void main(String[] args) {
Thread t;
CountingTask ct = new CountingTask();
final int numberOfProcessor = Runtime.getRuntime().availableProcessors();
for (int i = 0; i < 2 * numberOfProcessor; i++) {
t = new Thread(ct);
t.start();
}
for (int j = 0; j < 2 * numberOfProcessor; j++) {
t = new CountingThread();
t.start();
}
}
static class Counter{
private int count = 0;
public void increment(){
count++;
}
public int value(){
return count;
}
}
static class CountingTask implements Runnable{
private Counter counter = new Counter();
@Override
public void run() {
for (int i = 0; i < 100; i++) {
counter.increment();
}
System.out.println("CountingTask: " + counter.value());
}
}
static class CountingThread extends Thread{
//需要static才能让每次创建Thread的时候传递counter值
private static Counter counter = new Counter();
@Override
public void run() {
for (int i = 0; i < 100; i++) {
counter.increment();
}
System.out.println("CountingThread: " + counter.value());
}
}
}
3.线程属性(看书中13页)
4.Thread类的常用方法
几个方法的说明:
join方法:当前线程结束,其他线程才能执行,需要等待当前线程结束
yield方法:释放处理器资源,也就是当前线程不忙,释放资源,如果有其他线程需要则其他线程执行,没有需要则自己也可以竞争继续执行
sleep方法:当前线程停一段时间,不释放资源
简易倒计时器
public class SimpleTimer {
private static int count;
public static void main(String[] args) {
count = args.length >= 1 ? Integer.valueOf(args[0]) : 60;
int remaining;
while (true){
remaining = countDown();
if (0 == remaining){
break;
}else{
System.out.println("倒计时秒数:" + count);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Done...");
}
private static int countDown(){
return count--;
}
}
四、无处不在的线程
比如servlet的doGet和doPost都会用到,另外还有web容器都需要使用到线程执行。
另外垃圾回收也会使用到线程…
五、线程的层次关系
如果线程A创建了线程B,那么线程A是线程B的父线程,线程B是线程A的子线程。
六、线程的生命周期
Java线程的全部状态类型都在Thread.State枚举类里,可以使用Thread.getState()
获取。
线程状态:
NEW
:一个已经创建而未启动的线程处于该状态,由于一个线程实例只能启动一次,所以一个线程只会处于NEW状态一次RUNNABLE
:包括两个状态:READY和RUNNING状态,READY表示处于该状态的线程可以被线程调度器(Scheduler)进行调度而使之处于RUNNING状态,RUNNING状态表示处于该状态正在运行,也就是执行run方法,执行Thread.yield()的线程,可以将RUNNING变为READY,处于READY状态的线程也被称为活跃线程BLOCKED
:一个线程发起一个阻塞式I/O操作后,或者申请一个由其他线程持有的独占资源(比如锁)时,相应的线程会处于这个状态,该状态线程不会占用处理器资源,当阻塞式I/O操作完成后,或者线程申请到资源后,会转为RUNNABLEWAITING
:一个线程执行了特定方法后就会处于这种等待其他线程执行另外一些特定操作的状态,能够使其执行线程变更为WAITING状态的方法包括:Object.wait()、Thread.join()、LockSupport.park(),能够使相应的线程从WAITING变更为RUNNABLE的方法包括:Object.notify()/notifyAll()和LockSupport.unpark(Object)TIMED_WAITING
:状态和WAITING很相似,差别在于处于该状态的线程不是无休止的等待其他线程执行特定操作,当其他线程没有在特定时间内执行该线程特定操作时,会自动转回RUNNABLETERMINATED
:已经执行结束的线程处于该状态,并且只能有一次运行到这个状态,而不管程序运行完毕还是抛出异常提前终止都会进入这个状态