多线程主要有两种实现方法,分别是继承Thread类与实现Runnable接口。
继承Thread类以后无法再继承其他类,但实现Runnable接口的方式解决了Java单继承的局限;此外,Runnable接口实现多线程可以实现数据共享(传递给Thread的参数均为实现Runnable接口的类的同一个对象)。
启动一个线程是调用start()方法,它将启动一个新线程,使线程就绪状态,以后可以被调度为运行状态,同时在其中调用了native的方法(与操作系统有关);一个线程必须关联一些具体的执行代码,
run()方法是该线程所关联的执行代码。
一、继承Thread类
重写Thread类的run方法即可,那么当线程启动的时候,就会执行run方法体的内容。代码如下:
public class ThreadDemo extends Thread { @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " is running ... "); // 打印当前线程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { ThreadDemo td = new ThreadDemo(); td.start(); // 启动线程 while (true) { System.out.println(Thread.currentThread().getName() + " is running ... "); // 打印当前线程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } } }
运行结果如下:
main is running ... Thread-0 is running ... main is running ... Thread-0 is running ... Thread-0 is running ... main is running ... Thread-0 is running ... main is running ...
我们发现这里有个问题,多个线程的名字都是系统定义好的,就是Thread-开头,后面跟数字,如果我们每个线程处理不同的任务,那么我们能不能给线程起上不同的名字,方便我们排查问题呢?答案是可以的。只要在创建线程实例的时候,在构造方法中传入指定的线程名称即可。如:
public ThreadDemo(String name) { super(name); }
二、实现Runnable接口
实现Runnable接口也是一种常见的创建线程的方式。使用接口的方式可以让我们的程序降低耦合度。
Runnable接口中仅仅定义了一个方法,就是run。我们来看一下Runnable接口的代码。
package java.lang; @FunctionalInterface public interface Runnable { public abstract void run(); }
其实Runnable就是一个线程任务,线程任务和线程的控制分离,这也就是上面所说的解耦。
1、线程任务类
public class ThreadTarget implements Runnable { @Override public void run() { while(true) { System.out.println(Thread.currentThread().getName() + " is running .. "); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }
2、可运行的线程类
public class Main { public static void main(String[] args) { ThreadTarget tt = new ThreadTarget(); // 实例化线程任务类 Thread t = new Thread(tt); // 创建线程对象,并将线程任务类作为构造方法参数传入 t.start(); // 启动线程 // 主线程的任务,为了演示多个线程一起执行 while(true) { System.out.println(Thread.currentThread().getName() + " is running .. "); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }
三、使用内部类的方式(另一种写法)
public class DemoThread { public static void main(String[] args) { // 基于子类的实现 new Thread() { public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " is running ... "); // 打印当前线程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } }; }.start(); // 基于接口的实现 new Thread(new Runnable() { @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " is running ... "); // 打印当前线程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); // 主线程的方法 while (true) { System.out.println(Thread.currentThread().getName() + " is running ... "); // 打印当前线程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } } }
四、定时器
- 例1:在2017年10月11日晚上10点执行任务。
import java.text.SimpleDateFormat; import java.util.Timer; import java.util.TimerTask; /** * 定时器举例 * */ public class TimerDemo { private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); public static void main(String[] args) throws Exception {
Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { System.out.println("定时任务执行了...."); } }, format.parse("2017-10-11 22:00:00")); }
}
- 例2: 每隔5s执行一次
import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TimerDemo2 { public static void main(String[] args) {
Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { System.out.println("Hello"); } }, new Date(), 5000); } }
五、带返回值的线程实现方式
1. 创建一个类实现Callable接口,实现call方法。这个接口类似于Runnable接口,但比Runnable接口更加强大,增加了异常和返回值。
public interface Callable<V> { V call() throws Exception; }
2. 创建一个FutureTask,指定Callable对象,作为线程任务。
3. 创建线程,指定线程任务FutureTask。
4. 启动线程。
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; public class CallableTest { public static void main(String[] args) throws Exception {
Callable<Integer> call = new Callable<Integer>() { @Override public Integer call() throws Exception { System.out.println("thread start .. "); Thread.sleep(2000); return 1;//可以返回一个Integer值 } }; FutureTask<Integer> task = new FutureTask<>(call);
Thread t = new Thread(task); t.start();
System.out.println("do other thing .. "); System.out.println("拿到线程的执行结果 : " + task.get()); //可以通过FutureTask的get()方法获得返回值,注意:get方法是阻塞的,线程无返回结果,get方法会一直等待。
}
}
运行结果如下:
do other thing .. thread start .. 拿到线程的执行结果 : 1
六、基于线程池的方式
那么每次需要的时候创建,不需要的时候销毁,是非常浪费资源的。那么我们就可以使用缓存的策略,也就是使用线程池。
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolDemo { public static void main(String[] args) { // 创建线程池 ExecutorService threadPool = Executors.newFixedThreadPool(10); while(true) { threadPool.execute(new Runnable() { // 提交多个线程任务,并执行 @Override public void run() { System.out.println(Thread.currentThread().getName() + " is running .."); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }); } } }
运行结果如下:
pool-1-thread-4 is running .. pool-1-thread-1 is running .. pool-1-thread-6 is running .. pool-1-thread-2 is running .. pool-1-thread-8 is running .. pool-1-thread-3 is running .. pool-1-thread-5 is running .. pool-1-thread-9 is running .. pool-1-thread-10 is running .. pool-1-thread-7 is running ..