Java使用Thread代表线程,所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用就是执行一段程序流(完成一定的任务)。
Java使用线程执行体来代表这段程序流。
1. 继承Thread类创建线程类
package org.apache;
public class ThreadDemo1 extends Thread {
private int i;
/*
* 重写run方法,也就是线程执行体
* 当线程类继承Thread类时,直接使用this即可取得当前线程
* 直接调用getName()方法即可返回当前线程的名字
*/
public void run(){
for ( ; i<100;i++){
System.out.println(getName() + " " + i);
}
}
public static void main(String[] args)
{
for (int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()
+ " "+i);
if(i==20){
//创建并启动第一个线程
new ThreadDemo1().start();
//创建并启动第二个线程
new ThreadDemo1().start();
}
}
}
}
执行结果:
注意:实际上该程序有三个线程,main()方法是主线程,显示的创建了两个子线程。
Thread-0和Thread-1 i的值是不连续的,所以Thread-0和Thread-1不能共享Thread类的实例变量。
2. 实现Runnable接口创建线程类
package org.apache;
public class ThreadDemo2 implements Runnable {
private int i;
//当实现Runnable接口时必须用Thread.currentThread().getName()获取当前线程
public void run() {
for ( ; i<100;i++){
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
public static void main(String[] args) {
for (int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName() + " "+i);
if (i==20){
ThreadDemo2 td = new ThreadDemo2();
//通过new Thread(target,name)方法创建线程
new Thread(td,"线程1").start();
new Thread(td,"线程2").start();
}
}
}
}
结果:
i的值是连续的,共享同一个线程类的实例变量(其实是线程的target类)
3.使用Callable和Future创建线程
FutureTask同时实现
Future接口--->接收call()方法的返回值
和Runnable接口--->可以作为Thread的target
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/*
* 创建并启动有返回值的线程步骤:
* 1. 创建Callable接口的实现类,并实现call()方法,作为线程执行体
* 2. 使用FutureTask类包装Callable对象,该FutureTask对象封装了Callable对象的call()方法
* 3. 使用FutureTask对象作为Thread对象的target创建并启动新线程
* 4. 调用FutureTask对象的get()方法获得子线程的执行结束的返回值
*/
public class ThreadDemo3 implements Callable{
private int i;
public Object call() throws Exception {
for ( ; i<100;i++){
System.out.println(Thread.currentThread().getName() + " " + i);
}
return i;
}
public static void main(String[] args) {
ThreadDemo3 td = new ThreadDemo3();
//通过new Thread(target,name)方法创建线程
FutureTask<Integer> task = new FutureTask<Integer>(td);
for (int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName() + " "+i);
if (i==20){
new Thread(task,"有返回值的线程").start();
}
}
try {
//获取线程返回值
System.out.println(task.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
结果:
4. 控制线程
4.1 join线程
当程序程序执行流中调用其它线程的join()方法时,调用线程将会被阻塞。直到被join的线程执行完成。
package org.apache;
public class ThreadDemo1 extends Thread {
public ThreadDemo1 (String name){
super(name);
}
private int i;
/*
* 重写run方法,也就是线程执行体
* 当线程类继承Thread类时,直接使用this即可取得当前线程
* 直接调用getName()方法即可返回当前线程的名字
*/
public void run(){
for ( ; i<100;i++){
System.out.println(getName() + " " + i);
}
}
public static void main(String[] args) throws InterruptedException
{
new ThreadDemo1("新线程").start();
for (int i=0;i<100;i++){
Thread task = new ThreadDemo1("被join的线程");
System.out.println(Thread.currentThread().getName()
+ " "+i);
if(i==20){
//创建并启动第一个线程
task.start();
//main调用了join()方法,必须等task执行结束后才会向下执行
task.join();
}
}
}
}
执行结果:
分析可知main方法(主线程)被阻塞,只有新线程和join线程并发执行。
4.2 守护线程
有一种线程是为其它线程提供服务的(典型的有GC),被称为后台线程或者守护线程。
package org.apache;
public class ThreadDemo4 extends Thread{
private int i;
public void run(){
for (;i<1000;i++){
System.out.println(getName() + " "+i);
}
}
public static void main(String[] args) {
ThreadDemo4 task = new ThreadDemo4();
//设置为守护进程
task.setDaemon(true);
task.start();
for( int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
}
//程序执行到此处就main进程就结束了,JVM将会主动退出,守护线程也将被结束,无法执行到=999
}
}
执行结果:
4.3 线程睡眠:sleep
package org.apache;
import java.util.Date;
public class ThreadDemo5 {
//正在执行的线程将会暂停一段时间,进入阻塞状态
public static void main(String[] args) throws InterruptedException {
for (int i=0;i<10;i++){
System.out.println("当前时间: "+new Date());
Thread.sleep(1000);
}
}
}
结果: