创建多线程的五种方式
1.继承Thread类
Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。
启动线程的唯一方法就是通过Thread类的start()实例方法。
start()方法是Java的native方法,它将启动一个新线程,并执行run()方法。
这种方法实现多线程比较简单,通过自己创建的类直接继承Thread,并重写run()方法,就可以启动新线程并执行自己定义的run()方法。
优点:代码比较简单
缺点:该类无法继承其他类
1 public class ThreadDemo1 { 2 3 public static void main(String[] args) { 4 ThreadDemo t1 = new ThreadDemo(); 5 t1.setName("线程1"); 6 t1.start(); 7 ThreadDemo t2 = new ThreadDemo(); 8 t2.setName("线程2"); 9 t2.start(); 10 } 11 12 } 13 14 class ThreadDemo extends Thread{ 15 @Override 16 public void run() { 17 for (int i = 1; i <= 10; i++) { 18 System.out.println(Thread.currentThread().getName()+":"+i); 19 } 20 } 21 }
2.实现Runnable接口
Java的继承属于单继承,如果一个类继承了其他的一个类就无法继承Thread类。但是一个类继承一个类的同时,可以实现多个接口。
优点:继承其他类,统一实现该接口的实例可以共享资源。
缺点:代码比较复杂。
1 public class ThreadDemo2 { 2 3 public static void main(String[] args) { 4 MyThread myThread= new MyThread(); 5 Thread t1 = new Thread(myThread, "线程1"); 6 t1.start(); 7 Thread t2 = new Thread(myThread, "线程2"); 8 t2.start(); 9 } 10 11 } 12 13 class MyThread implements Runnable{ 14 @Override 15 public void run() { 16 for (int i = 1; i <= 10; i++) { 17 System.out.println(Thread.currentThread().getName()+":"+i); 18 } 19 } 20 }
3.实现Callable接口
实现Callable接口和实现Runnable接口的方法基本相同,不过Callable接口中的call()方法有返回值,Runnable接口中的call()方法没有返回值。
1 public class ThreadDemo3 { 2 3 public static void main(String[] args) throws ExecutionException, InterruptedException { 4 Callable<Object> myCallable = new MyCallable<Object>(); 5 //使用FutureTask类包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值 6 FutureTask<Object> futureTask = new FutureTask<Object>(myCallable); 7 Thread t1 = new Thread(futureTask, "线程1"); 8 t1.start(); 9 System.out.println(futureTask.get()); 10 } 11 } 12 13 14 class MyCallable<String> implements Callable<Object>{ 15 16 @Override 17 public Object call() throws Exception { 18 for (int i = 1; i <= 10; i++) { 19 System.out.println(i); 20 } 21 return Thread.currentThread().getName()+"执行完毕"; 22 } 23 }
4.线程池
线程池,其实就是一个容纳多个线程的容器,其中的线程可以重复使用,省去了频繁创建线程对象的操作,避免了频繁创建线程的资源消耗。
Executor类:提供了一系列工厂方法用于创建线程池,返回的线程池都实现了ExecutorService接口。
public static ExecutorService newFixedThreadPool(int nThreads):创建固定数目线程的线程池。
public static ExecutorService newSingleThreadExecutor():创建一个单线程化的Executor。
ExecutorService提供了sumbit()方法,传递一个Callable或Runnable,将线程放进线程池中,并执行。
优点:实现自动化装配,易于管理,循环利用资源。
1 public class ThreadDemo4 { 2 3 public static void main(String[] args) { 4 ExecutorService pool = Executors.newFixedThreadPool(2); 5 //将线程放入线程池中并执行 6 Future<?> submit = pool.submit(new MyRunnableThread()); 7 pool.submit(new MyRunnableThread()); 8 //关闭线程池 9 pool.shutdown(); 10 } 11 12 } 13 14 class MyRunnableThread implements Runnable { 15 @Override 16 public void run() { 17 for (int i = 1; i <=10 ; i++) { 18 System.out.println(Thread.currentThread().getName()+":"+i); 19 } 20 } 21 }
5.Timer类和TimerTask类
在这种实现方式中,Timer类实现的是类似定时任务的功能,也就是定时或者每隔一定时间触发一次线程。
Timer类本身实现的就是一个线程,只是这个线程是用来调用其他线程。
TimerTask类实现了Runnable接口,具备了多线程的能力。
在这种实现方式中,通过继承TimerTask使该类获得多线程的能力,重写run()方法,然后通过Timer类启动线程。
在实际使用时,一个Timer可以启动任意多个TimerTask实现的线程,但是多个线程之间会存在阻塞。所以如果多个线程之间如果需要完全独立运行的话,
最好还是一个Timer启动一个TimerTask实现。
1 public class ThreadDemo5 { 2 3 public static void main(String[] args) { 4 Timer timer = new Timer(); 5 MyTimerTask myTimerTask1 = new MyTimerTask("线程1"); 6 MyTimerTask myTimerTask2 = new MyTimerTask("线程2"); 7 //启动线程 8 timer.schedule(myTimerTask1,0); 9 timer.schedule(myTimerTask2,0); 10 } 11 12 } 13 14 class MyTimerTask extends TimerTask{ 15 private String TimerTaskName; 16 17 public MyTimerTask(String TimerTaskName) { 18 this.TimerTaskName=TimerTaskName; 19 } 20 21 @Override 22 public void run() { 23 for (int i = 1; i <=10 ; i++) { 24 System.out.println(TimerTaskName+":"+i); 25 } 26 } 27 }