java多线程编程核心技术——第一章总结 1.5sleep()方法

1.1进程、多线程的概念及线程的优点

进程的概念:

百度百科讲解:
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。着重点的话,就是最后一句进程是程序的实体,个人理解的话,也可以认为是进程就是程序的表现,你所看到的每一个进程,其实质都是一个程序,或者说是在运行的程序。

线程的概念:

  线程可以理解成在进程中独立运行的子任务,如在qq运行中,好友视频线程,文件下载线程,传输数据线程等,这些不同的独立的任务都在同时的进行,其中每一项任务都可以理解成是"线程"在工作。

  线程就是子任务。

1.2多线程的使用

想要实现线程,有两种方式(目前):

  • 继承Thread类
  • 实现Runnable接口

注:Thread类也实现了Runnable接口。

若想实现“多继承”只能通过实现Runnable接口来实现。继承Thread类与实现Runnable接口,创建的线程工作时的性质都是一样的。

通过重写run()方法,将需要线程开启后执行的任务放入其中就可以通过调用线程对象的start()或者run()方法来实现开启线程。

注:start()方法与run()方法的区别:

  start()方法是另外开启一个线程来执行run()里面的内容,run()方法则是当前线程获取run()里面的执行权。一个是开启新的线程执行任务,一个是当前线程执行任务。

  书中的话是:Thread.start()方法是告诉“线程规划器”此线程已经准备就绪,等待调用线程对象的run()方法,这个过程就是让系统安排一个时间来调用Thread中的run()方法。从而使线程运行,启动线程,具有异步的效果。

线程的调用具有随机性,因为多线程的实现是在极短的时间内cpu在不同线程间跳动来实现的(对于线程来说就是获取了执行权)。因为执行权的获取、失去都具有随机性,所以导致了线程的调用也具有随机性。

Thread的构造方法:

  由于Thread类实现了Runnable接口。所以构造方法中Thread(Runnable targer)不仅可以传入Runnable接口对象,还可以传入Thread对象(java三大特性:多态),这样可以将当前线程的任务交由其他线程来执行。

线程的安全性问题:

  当多线程操作共享数据时,如果不进行处理(加锁),就会出现数据不同步的现象,也就是说所谓的线程不安全问题。

synchronized关键字解决线程不安全问题:可以在线程的run()上加上关键字synchronized来为线程加锁,当第一个线程执行到此处时,会进行加锁处理,在其运行完之前不会放开锁,此时其他线程要想获得执行权,只能排队等到当前线程运行完run()代码(放开锁)后才能获得线程执行权并重新加锁。

  注:synchronized关键字可以在任意对象和方法上加锁,而这种加锁的代码称为:“互斥区”或“临界区”。

  注:非线程安全:多个线程对同一个对象中的同一个实例变量进行操作时出现值被更改、值不同步的情况,进而影响程序的执行流程。

i--与System.out.println()的异常:

  println()源码:

  若自增自减操作是在println()操作外进行的,那么就有可能会出现非线程安全问题。

  注:

  在某些JVM中,i--的操作要经历下面三个步骤:

  1. 取得原有i值
  2. 计算i-1
  3. 对i进行赋值

  若上述任何一步中,有多个线程同时访问,那么就可能会出现多线程问题。

1.3CurrentThread()方法

  currentThread()方法可以返回正在执行的线程的调用信息。

  注:

Thread.currentThread()获得的始终是执行start()方法的线程(或者说当前运行的线程)。

    而this获得的都是run()方法所在线程对象的名称。

    Thread.currentThread().getName()和this.getName()方法都可以获取线程的名字。

1.4isAlive()方法

 方法isAlive()是判断当前线程是否处于活动状态。

  活跃状态:线程已经启动且尚未终止。线程处于正在运行或准备开始运行的状态,就认为线程是“存活的”。

1.5sleep()方法

    方法sleep()的作用是在指定的毫秒数内让当前"正在执行的线程"休眠(暂停执行)。这个“正在执行的线程”是指this.currentThread()返回的线程。

1.6getId()方法

getId()方法的作用是获取线程的唯一标识。

1.7停止线程

  线程停止:在线程处理完任务之前,停掉正在做的操作,也就是放弃当前操作。

  在java中有三种方法可以实现线程的停止:

    1. 使用退出标志,使线程正常退出,也就是当run方法执行完后线程终止。
    2. 使用stop()强行终止线程。但是不推荐这个方法,因为stop()与suspend()以及resume()一样,都是作废过期的方法,使它们产生不可预期的结果。
    3. 使用interrupt()方法中断线程。该方法不会终止一个线程,还需要加入一个判断才能够完成线程的终止。

  停不了的线程:

    使用interrupt()方法停止线程,但是interrupt()方法并不像循环中的break关键字一样可以立即起效,interrupt()方法仅仅是在当前线程中打了一个停止的标记,并没有真正停止线程。

  判断线程是否停止:

    在Java的SDK中,Thread.java类里提供了两种方法判断线程是否停止

  根据JDK来看interrupted()方法会清除停止状态,也就是说当前如果出现暂停,若调用多次后第二次之后一定返回false。 

  isInterrupted()方法不会清除停止状态。

2017/12/15补充:

      interrupt()仅仅是为当前线程添加一个状态,并不影响线程的执行。     即,线程此时的状态为打断状态,但是线程是正常执行run()里面的内容的。     若想实现真正的打断,可以通过interrupted()或者isInterrupted()与判断语句实现打断。

  能停止的线程——异常法:

在线程执行的run()中直接抛出一个异常就可以将当前在运行的线程停止。

  在沉睡中停止:

    若某一线程处于sleep状态,此时将该线程停止的话就会抛出异常java.lang.InterruptedException:sleep interrupted进而停止线程。

    注:若先进入interrupt中,在进行睡眠还是会爆出该异常。

    2017/12/15补充:

    注:实际上是,只要线程在进入睡眠状态时,打断状态是true,就会报出异常。

  能停止的线程——暴力停止:

    stop()方法停止线程是暴力的,该方法会直接停止线程。

  方法stop()与java.lang.ThreadDead异常:

    调用stop()方法时,会抛出java.lang.ThreadDeath异常,但在通常情况下,此异常不需要显示地捕捉。

      注:如果不特地注明ThreadDeath方法时,是不会爆出异常进入catch中的。

    stop()方法已经作废,因为如果强制停止线程会导致一些清理的工作无法完成,另外一种情况就是对锁定的对象进行了“解锁”,导致数据得不到同步的处理,出现数据不一致的问题。

      注:stop()方法会杀死线程,然后将锁释放,此时若run()中的共享数据修改没有完成,可能会出现数据不同步的现象。

  使用return()停止线程:

可以结合interrupt()与判断线程是否停止的两个方法,通过判断实现是否通过return()停止线程。

    注:仅仅使用return;即可。return;可以结束方法的执行。

1.8暂停线程

  suspend()与resume()的使用:

    在java中,使用suspend()方法暂停线程,使用resume()方法恢复线程的执行。

  suspend()与resume()的缺点——独占:

    在使用suspend与resume方法时,可能会导致公共的同步对象的独占发生,使得其他线程无法访问公共同步对象。

    即若在临界区(互斥区)中停止了线程,那么其他线程在resume()前将永远无法获得锁。

    注:println()方法内部使用了synchronized关键字,这可能导致在一些测试中出现异常。

  suspend()与resume()的缺点——不同步:

若方法使用不当,可能会导致出现数据更新的不同步现象。

1.9yield方法

  yield()方法的解释:放弃当前cpu资源,将它让给其他的任务去占用CPU执行时间。但放弃的时间不能确定,有可能刚刚放弃,但马上又获得CPU时间片。

1.10线程的优先级

    在操作系统中,线程是有优先级划分的,优先级较高的线程会得到相对较多的资源。也就是说CPU会优先执行优先级较高的线程对象中的任务。

    设置线程优先级有助于帮“线程规划器”确定下次选择哪一个线程来优先执行。

    设置线程的优先级使用setPriority()方法,此方法在JDK的源代码如下:

    public final void setPriority(int newPriority) {
        ThreadGroup g;
        checkAccess();
        if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
            throw new IllegalArgumentException();
        }
        if((g = getThreadGroup()) != null) {
            if (newPriority > g.getMaxPriority()) {
                newPriority = g.getMaxPriority();
            }
            setPriority0(priority = newPriority);
        }
    }

    在java中,线程的优先级分为1~10这10个优先级,如果小于1或者大于10,则JDK抛出异常IllegalArgumentException()。

    JDK常用下面三个量来预置定义优先级的值。

    线程优先级的继承特性:

      在java中线程的优先级具有继承性,比如A线程启动B线程,则B线程的优先级与A是一样的。

优先级具有规则性:

       线程的执行顺序与线程代码的执行顺序无关,与线程的优先级有关,优先级越高越先执行。

优先级具有随机性:

      随机性意味着优先级高的线程不一定总是能优先执行完。

优先级越高的线程执行速度越快。

1.11守护线程

  在Java中有两种线程,一种为用户线程,一种为守护线程。

  守护线程是一种特殊的线程,它具有“陪伴”的含义,当进程中不存在非守护线程时,则守护线程自动销毁。

  典型的守护线程就是垃圾回收线程。

  当进程中没有线程了,则垃圾回收线程也就没有存在的必要了,自动销毁。

  任何一个守护线程,都是JVM中所有的非守护线程的保姆,只要当前JVM实例中存在任何一个非守护线程,且没有结束,守护线程就在工作,只有当最后一个非守护线程结束时,守护线程才随着JVM一同结束工作。

  通过setDaemon(Boolean)方法来设置守护线程。

    注:该方法一定要在start()方法前调用,不然会抛出异常。

本文内容是书中内容兼具自己的个人看法所成。可能在个人看法上会有诸多问题(毕竟知识量有限,导致认知也有限),如果读者觉得有问题请大胆提出,我们可以相互交流、相互学习,欢迎你们的到来,心成意足,等待您的评价。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Vamei实验室

Linux并发与同步

典型的UNIX系统都支持一个进程创建多个线程(thread)。在Linux进程基础中提到,Linux以进程为单位组织操作,Linux中的线程也都基于进程。尽管实...

2189
来自专栏PHP在线

认识webservice

1.Web services 使用 XML 来编解码数据,并使用 SOAP 来传输数据。 基础的 Web Services 平台是 XML + HTTP。 HT...

34410
来自专栏逆向技术

16位汇编第第四讲常用的7种寻址方式

常用的7中寻址方式 昨天稍微讲了一下,立即数寻址,今天继续讲解寻址方式. (注意,这个属于简陋版的,写了4个小时的博客,也就是第一版,保存了一下,但是博客出问题...

1895
来自专栏腾讯IVWEB团队的专栏

nodejs 中错误捕获的一些最佳实践

本文为翻译文章,原文比较长,感觉也有点啰嗦,所以根据个人理解猜测梳理出本文。

4710
来自专栏架构说

c++在编译中遇到符合不存在如何解决?

今日问题:symbol 不存在 : symbol lookup error: ./libinterface.so: undefined symbol: _ZN...

33815
来自专栏小狼的世界

PHP手册阅读笔记

学习PHP以来一直希望有时间能够有时间通读PHP手册,最近终于强迫自己划出一些时间,完成了对PHP手册的通读。除了函数参考部分没有每个都看,其他的章节基本上都看...

1254
来自专栏人人都是极客

汇编语言入门教程

但是,计算机不理解高级语言,必须通过编译器转成二进制代码,才能运行。学会高级语言,并不等于理解计算机实际的运行步骤。

1372
来自专栏技术专栏

慕课网Flask高级编程实战-2.搜索书籍路由编写

将上一小节的判断语句全都写到search函数中的几个缺点: 1.使得代码非常的臃肿,现在只有两个判断就占用了6行,如果有多个判断,search方法就要爆炸了 ...

1773
来自专栏逸鹏说道

Python3 与 C# 并发编程之~ 进程实战篇

之前说过 Queue:在 Process之间使用没问题,用到 Pool,就使用 Manager().xxx, Value和 Array,就不太一样了:

1264
来自专栏我有一个梦想

C++服务器开发之笔记三

为什么需要原子性操作? 我们考虑一个例子: (1)x++这个常见的运算符在内存中是怎样操作的? 从内存中读x的值到寄存器中,对寄存器加1,再把新值写回x所处的内...

1837

扫码关注云+社区