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