Java多线程实现方式主要有四种:继承Thread类、实现Runnable接口、实现Callable接口通过FutureTask包装器来创建Thread线程、使用ExecutorService、Callable、Future实现有返回结果的多线程。
其中前两种方式线程执行完后都没有返回值,后两种是带返回值的。
使用Thread对象的interrupt()方法,打断线程睡眠则会抛出interrupted异常
System.in.read();读取键盘输入
方式一:继承Thread类(线程没有返回值,在java.lang包中)
重写run()方法,调用start()方法执行。
需要注意的是:为什么多线程的启动不是调用run()方法,而是调用start()方法?
在Java开发中有一门JNI(Java Native Interface)技术,这门技术的特点,使用Java调用本机操作系统的函数,但是这个技术不能离开特定的操作系统。
如果线程想要执行,需要操作系统分配资源。所以此操作严格来讲需要JVM根据不同的操作系统来实现的。
start()方法中,使用了native关键字修饰了方法,而native关键字时根据不同的操作系统分配不同的资源。
start()方法,不仅仅要启动多线程执行的代码,还需要根据不同的操作系统来分配资源
1 package test; 2 3 public class MyThread { 4 public static void main(String[] args) { 5 Test t1 = new Test("one"); 6 Test t2 = new Test("two"); 7 Test t3 = new Test("three"); 8 /** 9 * 调用Thread类中的start方法,才能进行多线程的操作,而不是run方法 10 * start()方法不仅仅启动多线程代码的执行,还需要根据不同的操作系统分配资源 11 */ 12 try {//调用Thread类中的sleep方法,让线程等待指定的一段时间 13 Thread.sleep(1000); 14 } catch (InterruptedException e) { 15 e.printStackTrace(); 16 } 17 t1.start(); 18 t2.start(); 19 t3.start(); 20 } 21 } 22 23 /** 24 * 继承线程类Thread,重写run()方法,在run方法中实现需要进行的数据操作 25 * @author Administrator 26 * 27 */ 28 class Test extends Thread{ 29 30 private String name; 31 32 public Test(String name) { 33 this.name=name; 34 } 35 @Override 36 public void run() {//需要进行多进程的操作 37 for (int i = 0; i < 100; i++) { 38 System.out.println(name+"===>"+i); 39 } 40 } 41 }
方式二:实现Runnable接口(线程没有返回值,在java.lang包中)
该接口标注为:@Java.lang.FactionInterface
实现run方法,将Runnable对象作为参数放入到Thread构造方法中,在调用start()方法。
1 package test; 2 /** 3 * 测试Runnable接口 4 * @author Administrator 5 * 6 */ 7 public class MyRunnable { 8 public static void main(String[] args) { 9 Demo mr1 = new Demo("A"); 10 Demo mr2 = new Demo("B"); 11 Demo mr3 = new Demo("C"); 12 //将该对象作为参数,传入到Thread类中,在调用start()方法。 13 new Thread(mr1).start(); 14 new Thread(mr2).start(); 15 new Thread(mr3).start(); 16 } 17 } 18 19 /** 20 * 实现Runnable接口,实现多线程 21 * @author Administrator 22 * 23 */ 24 class Demo implements Runnable { 25 26 private String name; 27 28 public Demo(String name) { 29 this.name = name; 30 } 31 32 @Override 33 public void run() { 34 for (int i = 0; i < 300; i++) { 35 System.out.println(name + "==>" + i); 36 } 37 } 38 39 }
问题一:Thread与Runnable有什么不同?
1、Thread实现了Runnable接口,使用Runnable接口可以避免单根继承的问题。
2、Runnable接口实现的多线程可以比Thread类实现的多线程更加清楚的描述数据共享的概念。
问题二:写出两种多线程实现操作?
1 package test; 2 /** 3 * Runnable接口比Thread类更加清楚的描述了数据共享的概念 4 * @author Administrator 5 * 6 */ 7 public class ThreadAndRunnable { 8 9 public static void main(String [] args){ 10 TestDemo1 t = new TestDemo1(); 11 new Thread(t).start(); 12 new Thread(t).start(); 13 new Thread(t).start(); 14 try { 15 Thread.sleep(2000); 16 } catch (InterruptedException e) { 17 // TODO Auto-generated catch block 18 e.printStackTrace(); 19 } 20 System.out.println("=======================================我是分割线======================================="); 21 /** 22 * 这里虽然也可以数据共享, 23 * 但是,TestDemo2已经继承了Thread类,而这里又要new三个Thread类出来, 24 * 区调用start方法,显得多此一举 25 */ 26 TestDemo2 d1 = new TestDemo2(); 27 new Thread(d1).start(); 28 new Thread(d1).start(); 29 new Thread(d1).start(); 30 } 31 32 } 33 34 /** 35 * 实现了Runnable接口的类,可以明显的表示出数据共享的概念 36 * @author Administrator 37 * 38 */ 39 class TestDemo1 implements Runnable{ 40 /** 41 * 买票 42 */ 43 private int numb = 20; 44 45 @Override 46 public void run() { 47 for (int i = 0; i < 200; i++) { 48 if (this.numb > 0) { 49 System.out.println("买票;numb = "+this.numb--); 50 } 51 } 52 } 53 } 54 55 /** 56 * Thread类实现数据共享 57 * @author Administrator 58 * 59 */ 60 class TestDemo2 extends Thread{ 61 private int num = 20; 62 @Override 63 public void run() { 64 for (int i = 0; i < 200; i++) { 65 if(this.num > 0) 66 System.out.println("买票;num = "+this.num--); 67 } 68 } 69 70 }
方式三:实现Callable接口通过FutureTask包装器来创建Thread线程(线程有返回值,该接口在java.util.concurrent包中)
1 @java.lang.FactionalInterface 2 public interface Callable<V>{ 3 public V call() throws Exception; 4 }
从JDK1.5开始提供一个类java.util.concurrentFutureTask<V>,这个类主要负责Callable接口对象操作的,该类的定义结构为:
1 public class FutureTask<V> extends Object implements RunnableFuture<V> 2 3 //然而RunnableFuture有是什么? 4 public interface RunnableFuture<V> extends Runnable,Future<V> 5 6 //然而FutureTask<V>接口的构造方法内,接受了Callable接口对象 7 public FutureTask(Callable<V> callable) 8 //接受callable对象的目的:取得call()方法的结果,只有通过FutureTask类才能得到返回的 //结果
1 package test; 2 3 import java.util.concurrent.Callable; 4 import java.util.concurrent.FutureTask; 5 /** 6 * Callable接口实现多线程实例,可以得到返回的数据 7 * @author Administrator 8 * 9 */ 10 public class MyCllable { 11 12 public static void main(String[] args) throws Exception{ 13 TestDemo t1 = new TestDemo(); 14 TestDemo t2 = new TestDemo(); 15 FutureTask<String> task1 =new FutureTask<String>(t1); 16 FutureTask<String> task2 =new FutureTask<String>(t2); 17 /** 18 * FutureTask类是Runnable接口子类,所以可以作为构造参数调用start()方法 19 */ 20 new Thread(task1).start();//启动多线程 21 new Thread(task2).start();//启动多线程 22 System.out.println("task1===>"+task1.get());//得到返回的结果 23 System.out.println("task2===>"+task2.get());//得到返回的结果 24 } 25 26 } 27 28 class TestDemo implements Callable<String>{ 29 30 private int num = 20; 31 32 @Override 33 public String call() throws Exception { 34 for (int i = 0; i < 200; i++) { 35 if(this.num > 0){ 36 System.out.println("使用Callable接口实现多线程:"+num--); 37 } 38 } 39 return "数据归零"; 40 } 41 42 }
示例:写一个线程类输出001—999启动10条线程同时执行该类,每个线程要都有机会,而且不能重复而其有序
1 package com.zelin.lesson.test; 2 3 import java.text.NumberFormat; 4 5 /** 6 * 编写一个线程类,启动10条线程同时执行该类,要求输出效果如下所示 7 * 001 8 * 002 9 * 003 10 * …. 11 * 999 12 * 输出001—999 每个线程要都有机会,而且不能重复而其有序 13 */ 14 public class TestThread { 15 public static void main(String[] args) { 16 Thread0 t = new Thread0(); 17 for (int i = 0; i < 10; i++) { 18 new Thread(t).start(); 19 } 20 } 21 } 22 23 class Thread0 implements Runnable{ 24 //1.定义变量 25 private int tickets = 1; 26 @Override 27 public void run() { 28 while(true){ 29 synchronized (this){ 30 if(tickets == 1000) break; 31 //2.将变量进行格式化处理 32 NumberFormat numberFormat = NumberFormat.getInstance(); 33 //3.设置输出的数字的最小整数位数 34 numberFormat.setMinimumIntegerDigits(3); 35 //4.对数字进行格式化处理 36 String format = numberFormat.format(tickets++); 37 //5.输出 38 System.out.println(format); 39 } 40 } 41 } 42 }