并发编程 -- 多线程(一)
作者 : Stanley 罗昊
进程
在理解多线程之前, 我们先需要了解什么是进程?
进程说白了就是在你的内存空间中开辟出的一个独立的空间;
如果还不理解的话, 我再解释一下;
想必各位之前都安装过软件吧, 你安装完软件之后, 在你的软件安装包里面是不是有一个. exe 文件, 那你双击 exe 文件的时候, 在你的任务管理器, 在里面就有一个进程选项卡, 就是说, 每当你打开一个 exe 文件的时候, 它都会显示在任务管理器的进程当中, 所以就可以把运行中的任意一款软件, 都可以把它看做一个进程;
当然, 以上的操作方式是在 Windows 系统的操作的, 也就是说, 想查看 Windows 的进程, 只需要在任务管理器中查看即可;
在 Linux 下使用命令 ps 或 pstree,ps -eflgrep;
如果想在 Linux 系统下查看 java 的相关进程, 命令为: jps;
那么问题就来了, 当你打开 QQ 的时候, 是不是就是开启了一个进程, 当你开始使用它并且聊天的时候, 比如你是 a, 你现在要跟 b 聊天然后再去跟 c 聊天, 那么这样的操作是不是相互独立的呢? 也就是说, 你现在要跟 b 发送你的游戏密码, 这个时候 c 问你晚上吃啥饭, 你发的密码 c 知道吗? 肯定不会啦, 所以你跟 b 聊天的时候, 是不会影响你跟 c 聊天的, 因为你跟 b c 是相互独立的;
那么, 在这个里面, 你跟他们每个人产生的通话底层是怎么实现的呢?
底层就是靠线程去实现的;
线程
什么是线程呢?
线程是指程序在执行过程中, 能够执行程序代码的一个执行单元, 在 Java 语言中, 线程有四种状态: 运行, 就绪, 挂起, 结束. 一般情况下, 一个操作系统是有多个进程, 那么每个进程都要对应多个线程;
线程与进程有区别吗?
有, 进程是一段正在运行的程序, 而线程有时也被称为轻量级进程, 它是进程的执行单元, 一个进程可以拥有多个线程, 各个线程之间共享程序的内存空间, 但是, 各个线程拥有自己的栈空间.
一个进程可以有很多线程, 每条线程并行执行不同的任务;
实现单个线程
在之前的一些简单的 java 练习中, 我们运行的时候, 是不是都是在 main 方法中测试运行啊, 那么, 在这之前, 我仅仅编写了一些非常简单的 java 代码, 甚至就在 main 方法中输出了一句话, 就可以直接完成运行, 在这期间, 我并没有创建有关线程方面的方法以及程序, 那么就怎么实现运行了呢?
其实很简单, Main 方法既然能运行你的程序, 那么必然就会有一个线程, 那么这个线程就是单线程, 那, 我们如何查看本次运行线程的线程名呢?
我们仅需在 main 方法里面输出以下即可:
System.out.println(Thread.currentThread());
打印出来后, 我们就可以看到线程名师 Main, 因为就一个线程, 所以 main 就是主线程;
那么就一个线程, 就表明, 在这之前, 我们所做的一些练习程序都是单线程的;
线程的创建方式
两种:
1. 继承(extends) Thread
继承完 Thread 类之后需要重写 run 方法;
2. 实现(implem) Runnable 接口
也许需要重写 run 方法;
继承 Thread 类创建线程
讲了那么多, 那么就开始上手实操一下, 我们将要练习的是继承 Thread 来创建一个线程;
首先, 我们创建一个类 (MyThread) 然后继承 Thread 类;
继承完 Thread 后, 实现 run 方法;
run 方法的作用就是, 相当于这个线程对用户提供的一个接口;
所以, 用户有什么业务逻辑, 都需要写在 run 方法里面:
- public class MyThread extends Thread{
- public void run(){
- // 作用: 相当于这个线程向用户提供的接口, 用户有什么样的业务逻辑, 写在这个方法中
- }
- }
那我们现在就让这个 run 方法就实现一个简单的打印;
这个就是我使用了第一种方式来创建了一个线程;
那么, 你这个线程创建完成后, 如果你想调用它怎么办?
调用线程
我再建一个类(TestThread);
然后提供一个 Main 方法;
在 main 方法中创建一个线程, 语法:
- // 创建一个线程
- MyThread mt = new MyThred();
这个就是创建线程, 创建完后, 下一步我们需要调用 start 方法;
- public class TestThread{
- public static void main (String [] args){
- // 创建线程
- MyThread mt = new MyThred();
- // 启动线程
- mt.start();
- }
- }
为什么要调用 start, 而不是 run?
其实很简单, mt.start 就是启动你的线程, 那么启动后, 它底层就会去调用你的 run 方法;
这个就是线程的启动式 start 方法;
我们运行一下后, 看一下控制台打印:
就是一些输出, 感觉就跟调用方法一样, 对吧;
那么, 接下来, 我们看一下第二种创建方式
实现(implem) Runnable 接口创建线程
这种方法跟上面的那种, 大同小异, 只不过上面那个是继承, 这个是实现接口;
实现 Runnable 接口后, 需要实现它的 run 方法;
- public class MyRunnableThread implements Runable{
- public void run(){
- // 作用: 相当于这个线程向用户提供的接口, 用户有什么样的业务逻辑, 写在这个方法中
- }
- }
现在, 我们用第二种方式创建了一个线程, 当然, 业务逻辑跟上面的那个相同, 因为举例, 所以没有深究别的;
第二种方式调用线程就有所不同
因为运行线程永远需要 Thread 里面的 start 方法来启动线程, 所以需要把 Thread 创建出来, 再将创建出来的线程放进去;
所以打印出来的结果是跟上面的结果是一样的, 这里就不再放图上去了;
线程关键字分析
start, 是线程启动的方法;
run 方法是线程执行过程中调用的方法(默认调用), 在上面的例子我们也看到了, 你并没有手动去调用 run 方法, 是他自动调用的, 就跟你创建对象的时候, 默认调用构造方法一样;
深究 run 与 start
那, 启动线程一定是要用 staet 方法启动, 我试试不用它, 我直接调用 Thread 中的 run 方法可行吗?
可行, 因为抛开线程, 你本身就是实例化了 Thread 这个类, 并调用该类中的 run 方法是没有问题的, 但是, 不纳入线程中!!
我们直接调用 run 方法后, 发现, 方法可以正常打印, 因为, 仅仅完成了普通方法的调用, 实际上并没有启动线程;
深入理解并发编程 -- 多线程(一)
来源: http://www.bubuko.com/infodetail-3065099.html