Java多线程学习笔记(三)

一、背景

这篇博客想要讲解的是线程的暂停与停止。暂停与停止的方法各有其特点与优缺点,我会在这里逐一进行分析,并且大都是用举例子的方式来进行阐述。

二、线程的暂停

suspend和resume

线程的暂停使用的是suspend和resume方法,我写了程序进行验证。
这里写图片描述
这里写图片描述

这段代码运行出来的结果如下:
这里写图片描述
可见suspend和resume的功能:suspend可以使一个线程暂时停止运行,resume可以恢复被暂停的线程。

suspend和resume“独占”的缺点

就是说,如果被挂起的线程正在访问临界资源,那么这个时候其它线程也无法对该临界资源进行访问,也就使“独占”一词的来源。
下面举一个有趣的例子:
先来看这段程序:
这里写图片描述
这里写图片描述
这段程序十分简单,很明显,最后输出的结果应该使MAIN END!!!!!
没错,输出结果如下:
这里写图片描述
那么,我们再来看这段程序:
这里写图片描述
这里写图片描述
可以发现,这段程序与上一段程序唯一的不同在于我再MyThread类run方法中添加了一个将i值打印出来的语句。按道理讲,这段代码的输出应该是将i的值不断的输出,知道该线程被挂起,然后输出MAIN END!!!!!
但是事实上,输出结果的结果并没有MAIN END!!!!!
结果如下:
这里写图片描述
这是为什么呢?
我们来看一下println的源码:
这里写图片描述
这下你或许明白了,因为由关键字synchronized,所以这段代码是临界区,在MyThread线程在对其进行访问的时候被暂停了,那么,其它线程当然也无法访问这段代码,所以main中的println迟迟无法打印出结果来。这就是suspend与resume的“独占”缺点。

三、线程的停止

interrupt、interrupted、isInterrupted

interrupt

要介绍这三种方法,先来看一段代码:
这里写图片描述
这里写图片描述
这段代码的运行结果如下:
这里写图片描述
也就是说,从1一直输出到500000,而interrupt方法好像没有起什么作用,
事实上并不是的,interrupt方法只是给该线程发出了一个停止的信号,至于怎么使用这个信号,就要看interrupted和isInterrupted了。因为interrupted和isInterrupted可以用来判断一个线程是否有处于停止状态,判断出来停止状态就可以使用if语句来进行适当的操作。

interrupted

这里写图片描述
这里写图片描述
运行结果如下:
这里写图片描述
为什么测试出来的是否由停止信号还是false呢?
原因在于interrupted测试的是当前线程,也就是这段程序中的main线程是否停止,当然main线程没有停止,所以interrupted返回的是false。
那么我再将源程序改一下,将main也用interrupt来停止一下:
程序如下:
这里写图片描述
这里写图片描述
这时的运行结果如下:
这里写图片描述
如结果所示,返回的第一个值是true,也就是说检测到了main有停止信号,但是为什么第二个值又是false呢?
原因在于interrupted这个方法在输出true之后会将停止状态清除掉,那么第一个方法将停止状态清除掉了,自然而然第二个interrupted返回的是false。

isInterrupted

接下来我们来看一下isInterrupted方法。
isInterrupted的方法返回值不同于interrupted。其可以判断其它线程的停止状态,而且它不会清除停止状态。
示例程序如下:
这里写图片描述
这里写图片描述
运行结果如下:
这里写图片描述

break,return,throw new InterruptedException()

break、return和throw new InterruptedException()可以与interrupted或isInterrupted的if语句搭配来使用
我这里只演示一下break的用法,return、throw new InterruptedException()类似
这里写图片描述
这里写图片描述
运行结果如下:
这里写图片描述
这里需要注意的是,虽然break和return、throw new InterruptedException()的用法类似,但是实际上break只是跳出了循环而已,并没有真正的结束线程,而return、throw new InterruptedException()可以真正的结束线程,另外throw new InterruptedException()会将程序跳转至run方法中的catch。这样可以对异常信息进行相关处理,而且不至于使程序中出现很多break和return造成污染。

沉睡中停止线程

如果在沉睡中停止线程会出现什么情况呢?
看如下程序:
这里写图片描述
这里写图片描述
结果如下:
这里写图片描述
看到这个结果我们可以得出来两个信息:
1.线程在沉睡中如果被停止就会直接进入catch中
2.进入catch语句之前会清除停止状态值

那么,如果是先停止线程,然后线程要进入睡眠会出现什么情况呢?
示例程序如下:
这里写图片描述
这里写图片描述
结果如下:
这里写图片描述
可以看到,与上面类似,仍旧是进入catch,并且会清除停止状态值。

stop

stop这个方法十分暴力,用的时候需谨慎。
我这里写了一个示例程序如下:
这里写图片描述
这里写图片描述
输出结果如下:
这里写图片描述
可以看到,正在运行着的线程直接就被停止掉,stop方法看起来十分简单易用,实际上则会出现一些问题。比如stop会释放锁,这会造成数据不同步的不良后果。而且stop会让线程停止,则有可能一些清理工作得不到完成.

四、总结

这篇博客主要讲解了Java多线程中一些在暂停与停止线程中常用的方法还有它们的用途以及优缺点。其中,stop、suspend、resume在使用时需慎重,因为它们有可能会引起某些麻烦。

Java多线程学习笔记(二)

this和Thread.currentThread()的区别

我们先来看一下我写的一个试验程序:

这里写图片描述

这两段代码的运行结果如下:
这里写图片描述
在c.start();之前,只有一个main线程在运行。我们先运行的CountOperate的构造函数。那么为什么this.getName和Thread.currentThread().getName的运行结果不同呢,分别是Thread-0和main,很显然,现在只有一个线程main在运行,所以解释是,this是继承的子类的线程对象,Thread.currentThread()是当前正在运行的线程对象。也正因如此,this.isAlive的结果是false,Thread.currentThread().isAlive的结果是true,原因是此时Thread-0线程没有在运行中,而main线程在运行中。

Java多线程学习笔记(一)

一、什么是多线程技术?

    线程也可以叫做轻量级进程。是一个程序执行流的最小单元。或许这么讲还不够好理解。打个比方:我们在电脑上登陆了QQ,那么QQ是一个进程单元。线程是什么呢?线程就是我们跟别人聊天、下载别人传送来的文件或者跟好友视频着。大家都知道,我们是可以用QQ一边跟好友视频、一边打字聊天、一边下载文件的,这就是多线程技术。这样我们就节省了很多等待的时间。这就是多线程技术的好处。

二、一个简单的多线程程序

    看下面的这个程序我们可以看到,类MyThread继承Thread,那么类MyThread也可以用Thread的方法,在这里我们用了start方法。

    Thread类中的start方法就是通知“线程规划器”,告诉它这个线程已经准备好了,等待调用线程对象的run()方法。

    两个线程并发执行,所以输出的结果的先后顺序是不一定的。结果如下:

    那么如果我们不使用Thread中的start方法,而是使用run方法呢?肯定还是调用run方法无疑,结果会是如何呢?​

    我们可以看到,这是顺序执行的,由run=main可以看到,实际上这还是一个线程main。

    这就能看出来调用start和调用run的区别在于:

    调用run就是像一般的调用类的方法那样,等它顺序执行完了才会执行下个方法。

    而调用start实际上是开始一个新线程,将此线程交给“线程规划器”来处理了。所以是两个线程并发执行。

三、共享数据的问题

    共享数据的情况就是多个线程访问同一个变量。这种情况会经常常见的,比如说投票机制,需要对同一个变量进行加一。

    但是共享数据的时候是会出现一些问题的。如下例:​

结果如下:​

要解决这个问题只需要在被调用的方法前加关键字:synchronized

这就给该段代码加锁了,被加锁的区域叫做临界区,一次只能有一个线程访问。

结果如下:​

看到这个结果可能有人会有问题,为什么不是按照ABCDE的顺序输出呢?

实际上执行start方法的顺序不代表线程启动的顺序。

四、实现Runnable接口

我们知道,java是单根继承的,所以如果一个类已经有了父类了就不能在继承Thread了,

这种情况下就需要使用Runnable接口来实现多线程。程序如下:

五、总结

    这篇博客写了一些简单的多线程程序,分析了多线程共享资源时可能出现的问题,然后讲了Runnable接口,当类已经有父类时可以使用Runnable接口来开始一个新线程。