前言
前面一章,我们了解了线程的基本概念,从这一章,我们开始学习如何创建一个线程。总的说来,在java
中,有四种创建线程的方式:
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口
- 使用线程池
上述四种创建线程的方式中,我们着重介绍 1,2 ,对于 3,4,小伙伴们作为了解即可。下面用代码来实现这几种创建方式。
继承Thread类
下面的代码中使用了sleep()
方法,不太理解的同学可以先放一放,后面我们会详细介绍这个方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
public class Thread1 extends Thread { private String name; public Thread1(String name){ this.name = name; } @Override public void run() { for(int i = 0; i < 5; i++){ System.out.println(name+"运行:"+i); try { sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Thread1 thread1_1 = new Thread1("thread1_1"); Thread1 thread1_2 = new Thread1("thread1_2"); thread1_1.start(); thread1_2.start(); } }
|
上一章的学习我们知道,调用线程的start()
方法后,上述的两个线程就都进入了可运行态
,注意此时两个线程均还没有运行。
只有当CPU调度
之后,才能开始执行。至于两个线程到底谁先运行,完全取决于某一刻谁获得了CPU的使用权
。因此,程序的运行结果是相对随机的,每一次运行的结果都有可能不同。比如,第一次运行结果:
第二次运行结果:

实现Runnable接口
下面的代码中,Thread.currentThread().getName()
可以获得当前线程的名字,此外注意导入sleep()方法
所在的包:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import static java.lang.Thread.sleep;
public class Thread2 implements Runnable { @Override public void run() { for(int i = 0; i < 5; i++){ System.out.println(Thread.currentThread().getName()+"运行:"+i); try { sleep( 100); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Thread2 thread = new Thread2(); new Thread(thread,"线程A").start(); new Thread(thread,"线程B").start(); } }
|
上述创建线程的方式使用到了一种设计模式-----静态代理模式来创建线程。不太清楚的可以去参考一下 JAVA
tag 下的相关文章。
同样的,由于CPU何时调度哪个线程
是不确定的,上述方式得到的运行结果也不尽相同,比如第一次运行结果:
第二次运行结果:

实现Callable接口
实现Callable接口
和实现Runnable接口
的区别在于,实现Callable接口
允许用户自定义返回值
和抛出异常
,而实现Runnable接口
无返回值且不能抛出异常:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask;
public class Thread3 implements Callable<Integer> { private int count3 = 10; @Override public Integer call() throws Exception{ for(int i = 0; i < 5; i++){ System.out.println(Thread.currentThread().getName()+"运行:" + count3--); } return 200; } public static void main(String[] args) throws InterruptedException, ExecutionException { Callable<Integer> callable = new Thread3(); FutureTask<Integer> futureTask = new FutureTask<Integer>(callable); new Thread(futureTask).start(); System.out.println(futureTask.get()); } }
|
上述实现Callable接口
来创建线程的方式只需了解即可。此外Callable接口还可通过线程池来创建线程,与线程池有关的内容,参见同tag下的其他文章。
现阶段我们只需掌握前两种创建方式(继承Thread类
,实现Runnable接口
)即可。
1,2方式的对比
最后,我们来对比一下继承Thread类
,实现Runnable接口
两种创建线程的方式:
实现Runnable接口
可以实现资源共享
,而继承Thread类
不行。此外,实现Runnable接口还可以避免Java单继承带来的局限性。
因此,一般采用实现Runnable接口来实现多线程。最后,关于实现Runnable接口
可以实现资源共享的说法,大家可以参见下面一段代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import static java.lang.Thread.sleep; public class Thread2 implements Runnable{ private int count = 15; @Override public void run() { for(int i = 0; i < count; i++){ System.out.println(Thread.currentThread().getName()+"抢到了第:" + i++ +"张票"); try { sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { new Thread(new Thread2(),"A").start(); new Thread(new Thread2(),"B").start(); }
}
|
上面代码中的票数count
便是一个公共资源,实现Runnable接口
可以很轻松地实现资源共享。
下一章我们将会介绍多线程里必须要解决的一个问题-----线程安全
。