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); } } }
结果: