创建方法总结
- 继承Thread类创建线程
- 实现Runnable创建线程
- 使用匿名内部类的形式创建线程
- 使用lambda表达式创建线程
- 使用Callable和Future创建线程
- 使用线程池例如用Executor框架创建线程
- spring@Async异步注解创建线程
方法一: 继承Thread类
public class Demo01 {
public static void main(String[] args) { //main()是主线程
MyThread myThread = new MyThread();//创建线程MyThread的线程对象
//启动线程调用的是start()方法,而不是run()方法
myThread.start();//开启线程
while (true) {//通过死循环语句打印输出
System.out.println("main()方法在运行");
}
}
}
class MyThread extends Thread {
//线程执行的程序就是在run()方法中
public void run() {
while (true) {//通过死循环语句打印输出
System.out.println("MyThread()类的run()方法在运行");
}
}
}
运行结果
利用两个while来模拟多线程环境,从运行结果,可以看到两个循环中的语句都有输出,说明该文件实现了多线程。
通过继承Thread类可以实现多线程,但是这种方式有一定的局限性。因为java只支持单继承,一个类一旦继承了某个父类就无法继承Thread类。为了克服这种弊端,Thread类提供了另外一个构造方法Thread(Runnable target),其中runnable是一个接口,它只有一个run()方法。
方法二:实现Runnable
public class Demo02 {
public static void main(String[] args) {
MyThread02 myThread = new MyThread02();//建线程MyThread的实例对象
Thread thread=new Thread(myThread);//创建线程对象
thread.start();//开启线程,执行线程中的run()方法
while (true) {//通过死循环语句打印输出
System.out.println("main()方法在运行");
}
}
}
class MyThread02 implements Runnable {
public void run() {//线程的代码段,当调用start()方法时,线程从此处开始执行
while (true) {//通过死循环语句打印输出
System.out.println("MyThread()类的run()方法在运行");
}
}
}
运行结果
MyThread02类中重写了Runnable接口中的run()方法。main()方法中通过Thread类的构造方法将MyThread02类的实例对象作为参数传入。从运行结果,可以看到两个循环中的语句都有输出,说明该文件实现了多线程。
直接继承Thread类和实现Runnable接口都能实现多线程,那么这两种实现多线程的方式在实际应用中有什么区别?
假设售票厅有四个窗口可发售某日某次列车的100张车票,这时,100张车票可以看做共享资源,四个窗口可以创建四个线程。为了更直观显示窗口情况,可以通过Thread的currenthread()方法得到当前线程的实例对象,然后调用gwtName()方法获取现成的名称。
案例方法一:通过继承Thread类创建多线程
public class Demo03 {
public static void main(String[] args) {
new TicketWindowThread().start();//创建第一个线程对象TicketWindow并开启
new TicketWindowThread().start();//创建第二个线程对象TicketWindow并开启
new TicketWindowThread().start();//创建第三个线程对象TicketWindow并开启
new TicketWindowThread().start();//创建第四个线程对象TicketWindow并开启
}
}
class TicketWindowThread extends Thread{
private int tickets=100;
public void run(){
while (true) {//通过死循环打印语句
if (tickets > 0) {
Thread th = Thread.currentThread();//获取当前线程
String th_name = th.getName();//获取当前线程的名字
System.out.println(th_name + "正在发售第" + tickets-- + "张票");
}
}
}
}
运行结果
结果分析
- 从运行结果可以看出,每张票都被打印了四次。出现这样现象的原因是四个线程没有共享100张票,而是各自出售了100张票。
- 在程序中创建了四个ThreadWindow对象,就等于创建了四个售票程序,每个程序中都有100张票,每个线程在独立的处理各自的资源。
- 注意:每个线程都有自己的名字,主线程默认的名字是“main”,用户创建的第一个线程的名字默认为“Thread_0”,第二个默认为“Thread_1”,以此类推。
- 如果希望指定现成的名称,可以通过调用setName(String name)方法为线程设置名字。
- 由于现实中铁路系统的票资源是共享的,一次上面的运行结果是不合理的。
- 为了保证资源共享,在线程中只能创建一个售票对象,然后开启多个线程去运行同一个售票对象的售票方法。简单来说,就是四个线程运行同一个售票程序,就需要创建现成的第二种实现方法。
案例方法二:通过实现Runnable接口创建多线程
public class Demo04 {
public static void main(String[] args) {
TicketWindowRunnable tw = new TicketWindowRunnable();//创建TicketWindow实例对象tw
new Thread(tw, "窗口1").start();//创建线程对象并命名为“窗口1”,开启线程
new Thread(tw, "窗口2").start();//创建线程对象并命名为“窗口2”,开启线程
new Thread(tw, "窗口3").start();//创建线程对象并命名为“窗口3”,开启线程
new Thread(tw, "窗口4").start();//创建线程对象并命名为“窗口4”,开启线程
}
}
class TicketWindowRunnable implements Runnable{
private int tickets=100;
public void run(){
while(true){
if(tickets>0){
Thread th = Thread.currentThread();//获取当前线程
String th_name = th.getName();//获取当前线程的名字
System.out.println(th_name + "正在发售第" + tickets-- + "张票");
}
}
}
}
运行结果
结果分析
上述代码创建了TicketWindowRunnable对象并实现了Runnable接口,然后再main方法中创建了四个线程,在每个线程上都去调用这个TicketWindowRunnable对象中的run()方法,这样就可以确保四个线程访问的是同一个tickets变量,共享100张车票。
实现Runnable接口相对于继承Thread类来说的优势
- 适合多个相同程序代码的线程去处理同一个资源的情况,把线程同程序代码,数据有效地分离,很好的体现了面向对象的设计思想。
- 可以避免由于Java的单继承带来的局限性,在开发中经常碰到这样的一种情况。及使用一个已经继承了某个类的子类来创建线程,由于一个类不能同时有两个父类,因此就不能使用继承Thread类的方式。
提示:JDK8简化了多线程的创建方法,在创建线程时指定线程要调用的方法。
格式如下:
Thread t=new Thread(() ->{
//main方法代码
});
public class Demo05 {
public static void main(String[] args) {
Thread t=new Thread(() ->{
while(true){
System.out.println("start new thread!");
}
});
t.start();//启动新线程
}
}
class MyThread05 extends Thread{
public void run() {
while (true) {
System.out.println("MyThread()类的run()方法在运行");
}
}
}