java多线程实现的三种方式学习总结

线程概念和两种实现方法

一、线程的概念

现代的操作系统都是多用户多进程分时操作系统,所以我们在使用操作系统时,可以一边听歌,一边下载,还可以聊天等等,事实上我们的操作系统同时还运行着很多后台进程,你可以打开window系统的任务管理器就可以看到很多进程在运行。
这里写图片描述
在往下学习之前,让我们先弄清楚什么是程序,进程和线程这几个概念。

1、程序:
利用编程语言开发的一个工具软件, 静态的,在没有启动运行之前只是磁盘中的一个普通文件。

2、进程:
程序启动之后就变成了进程,进程是在内存中运行的,具有一定的生命周期,如果程序运行解析,进程在内存中就会回收,生命周期也就结束了,当然程序是可以再次运行,重写变成进程。

3、线程:
进程在运行过程中的执行走向,线索。线程是比进程更小的一个单位,一个进程可以有一个或多个线程的。
java程序启动运行时,就自动产生了一个线程,主函数main就是在这个线程上运行的,当不再产生新的线程时,程序就是单线程。到目前为止我们编写的所有的java程序都是单线程的,接下来我们要学习如何实现多线程的java程序。


二、线程的实现方法

1、单线程。

我们新建一个类Stu1实现一个简单的循环。

public class Stu1 {

    public static void main(String[] args) {
        //主线程
        for(int i=0;i<10;i++){
            System.out.println("主线程:"+i);
        }
        System.out.println("主线程:死亡");
    }

}

这里写图片描述
运行上面代码,程序只有一个线程,运行完毕程序也就结束了。


2、通过继承Thread类实现第一个线程。

新建一个MyThread类继承Thread类,并重写run方法,run方法就是线程的执行内容。实现代码如下:

package lisy;
class MyThread extends Thread{
    private String name;
    public MyThread(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        for(int i = 0;i <10;i++) {
            System.out.println(this.name +", i = "+i);
        }
    }
}
public class TestDemo {
public static void main(String[] args) {
        MyThread mt1 = new MyThread("thread1");
        MyThread mt2 = new MyThread("thread2");
        MyThread mt3 = new MyThread("thread3");
        mt1.run();
        mt2.run();
        mt3.run();
    }
}

以上代码运行结果如图
这里写图片描述


以上的代码只是进行了一个顺序打印和多线没有关系。

正确启动多线程的方式不是调用run()方法,而是使用start()方法启动线程,线程启动后会自动执行线程的run()方法。请看一下代码

package lisy;
class MyThread extends Thread{
    private String name;
    public MyThread(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        for(int i = 0;i <10;i++) {
            System.out.println(this.name +", i = "+i);
        }
    }
}
public class TestDemo {
public static void main(String[] args) {
        MyThread mt1 = new MyThread("thread1");
        MyThread mt2 = new MyThread("thread2");
        MyThread mt3 = new MyThread("thread3");
        mt1.start();
        mt2.start();
        mt3.start();
    }
}

这里写图片描述
现在你可以再次运行这个段代码,查看后台的输出。你可以多运行几次可以看出来都是交替执行的。

由输出可见,主线程和子线程之间是独立运行的,它们将会轮流的占用CPU,而那个线程会占有CPU是由操作系统决定的。所以我们看到多次运行这个程序时,每一次的输出可能都不一样。

每一个线程的对象只能够启动一次


3、通过实现Runnable接口实现线程。

(1)观察Runnable接口

@FunctionalInterface
public interface Runnable{
    public abstract void run();
}

注意:接口中定义的抽象方法是没有default权限的。

如果要想启动多线程依靠的只能够是Thread类中的start()方法,在之前继承Thread类的时候可以直接将start()方法继承下来继续使用,但是现在实现的是Runnable接口,没有此方法。

那么此时就需要关注Thread类提供的构造方法。

public Thread (Runnable target)

使用Runnable实现多线程代码如下:

package lisy;
class MyThread implements Runnable{
    private String name;
    public MyThread(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        for(int i = 0;i <10;i++) {
            System.out.println(this.name +", i = "+i);
        }
    }
}
public class TestDemo {
public static void main(String[] args) {
        MyThread mt1 = new MyThread("thread1");
        MyThread mt2 = new MyThread("thread2");
        MyThread mt3 = new MyThread("thread3");
        new Thread(mt1).start();
        new Thread(mt2).start();
        new Thread(mt3).start();
    }
}

打印结果如图:
这里写图片描述


Runnable接口对象还可以使用匿名内部类或者Lambda表达式来实现。

采用匿名内部类实现代码如下:

package lisy;
class MyThread implements Runnable{
    private String name;
    public MyThread(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        for(int i = 0;i <10;i++) {
            System.out.println(this.name +", i = "+i);
        }
    }
}
public class TestDemo {
public static void main(String[] args) {
        String name = "线程对象";
        new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                for(int i = 0;i <10;i++) {
                    System.out.println(name +", i = "+i);
                }
            }
        }).start();
    }
}

打印结果如图:
这里写图片描述

采用Lambda表达式实现代码如下:

package lisy;
class MyThread implements Runnable{
    private String name;
    public MyThread(String name) {
        this.name = name;
    }
    @Override
    public void run() {
        for(int i = 0;i <10;i++) {
            System.out.println(this.name +", i = "+i);
        }
    }
}
public class TestDemo {
public static void main(String[] args) {
        String name = "线程对象";
        new Thread(()->{
            for(int i = 0;i <10;i++) {
                System.out.println(name +", i = "+i);
            }
        }
        ).start(); 
    }
}

打印结果如图:
这里写图片描述


4、通过实现Callable接口实现线程。

Runnable中的run()方法没有返回值,如果某些线程执行完成后可能带来一些返回结果,这种情况下我们就只能利用Callable接口来实现多线程。此接口定义在java.uti.concurrent这个包中。jdk1.5之后有的。

@FunctionalInterface
public interface Callable<V>{
    public V call() throws Exception;
}

这个泛型表示的是返回值的类型。call()方法就相当于run()方法。但是Thread类中没有提供接受Callable接口的对象。

类图:实现为实线,虚线为使用
这里写图片描述

实现代码:

package lisy;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class MyThread implements Callable<String>{
    private int ticket = 5;
    @Override
    public String call() {
        while(this.ticket > 0 ) {
            System.out.println("剩余票数:"+this.ticket--);
        }
    return "飘卖完了!";
    }
}
public class TestDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {

    FutureTask<String> task = new FutureTask<>(new MyThread());//取得实现结果
    new Thread(task).start();
    new Thread(task).start();
    System.out.println(task.get());

    }
}

代码实现结果图:

这里写图片描述


三、继承Thread类、实现Runnable接口这两种实现方式的区别(面试题)

对于多线程的两种实现模式:继承Thread类、实现Runnable接口,这两种模式本质上来讲,一定使用Runnable接口实现,这样可以避免单继承局限。这两种实现方式之间的关系。

  • Thread类的定义结构:
    Thread类实现了Runnable接口。
    这里写图片描述
  • Runnable接口实现的多线程要比Thread类实现的多线程更方便的表示出数据共享的概念。

(1)请看继承Thread类实现共享数据的代码:

package lisy;
class MyThread extends Thread{
    private int ticket = 5;
    @Override
    public void run() {
        while(this.ticket > 0 ) {
            System.out.println("剩余票数:"+this.ticket--);
        }
    }
}
public class TestDemo {
public static void main(String[] args) {
        MyThread mt1 = new MyThread();
        MyThread mt2 = new MyThread();
        MyThread mt3 = new MyThread();
        mt1.start();
        mt2.start();
        mt3.start();
    }
}

打印结果如图:
这里写图片描述

分析打印结果的图:
这里写图片描述
(2)使用Runnable接口来实现数据共享代码:

package lisy;
class MyThread implements Runnable{
    private int ticket = 5;
    @Override
    public void run() {
        while(this.ticket > 0 ) {
            System.out.println("剩余票数:"+this.ticket--);
        }
    }
}
public class TestDemo {
public static void main(String[] args) {
        MyThread mt = new MyThread();
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();
    }
}

打印结果如图:
这里写图片描述

分析打印结果的图:
这里写图片描述


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值