Java多线程读书笔记(一)

参考的代码来自:git@github.com:hedanning/JavaThreadExe.git

一、基本概念

进程:正在运行的程序实例。是系统进行资源分配和调度的一个独立单元。

线程:进程中独立运行的子任务。

例子:QQ.exe就是一个进程;QQ.exe在运行的时候就有很多子任务在同时运行,这每个子任务就是一个线程。

 

多线程的优点:在同一时间内运行更多不同种类的任务,系统的运行效率大大得到提升。

单任务的特点:排队执行,也就是同步。大幅降低了CPU的利用率。

单线程是同步的,多线程是异步的。

 

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

解决非线程安全的方法:使用synchronized关键字

 

一个进程在运行时,至少会有1个线程在运行。

例子:Java中的main方法就是由叫做main的线程在执行。

二、实现多线程的两种方式

  • 继承Thread类
  • 实现Runnable接口

      Thread类实际上也是实现了Runnable接口,它们之间具有多态关系。使用这两种方式创建的线程在工作时的性质时一样的,没有本质的区别。只是因为Java中不支持多继承,所以为了支持多继承,完全可以实现Runnable接口的方式,一边是新啊一边继承。

run方法:写线程要执行的代码。

注意:

  • 在使用多线程技术时,代码的运行结果与代码执行顺序或调用顺序是无关的(线程调用的随机性)。参考:com.thread1.t2

三、Thread中的start()方法和run()方法

线程对象.start()方法:通知“线程规划器”此线程已经准备就绪,等待调用线程对象的run()方法。这个过程其实就是让系统安排一个时间来调用Thread中的run()方法,也就是使线程的到运行,启动线程,具有异步执行的效果。参考:com.thread1.t3

线程对象.run()方法:这个使同步执行,那么此线程对象就不交给“线程规划期”来进行处理,而是由main主线程来调用run()方法,也就是必须等run()方法中的代码执行完之后才可以执行后面的代码。参考:com.thread1.t3

注意:执行start()方法的顺序不代表线程启动的顺序。参考:com.thread1.t4

四、实现Runnable接口

      如果要创建的线程已经有了一个父类,这是就要使用Runnable接口。

      在Thread类中,有两个构造函数Thread(Runnable target)和Thread(Runnable target,String name)可以传递Runnable接口。参考:com.thread1.t5

      因为Thread类也是实现了Runnable接口,所以以上两个构造函数不仅可以传入Runnable接口对象,还可以传入一个Thread类的对象,这样做完全可以将一个Thread对象中的run()方法交由其他的线程进行调用。

五、实例变量与线程安全

      自定义线程类中的实例变量针对其他线程可以有共享和不共享之分。

共享:多个线程对同一个对象中的同一个实例变量进行操作   参考:com.thread1.t7

MyThread myThread = new MyThread();
Thread t1 = new Thread(myThread,"a");
Thread t2 = new Thread(myThread,"b");
Thread t3 = new Thread(myThread,"c");
Thread t4 = new Thread(myThread,"d");
Thread t5 = new Thread(myThread,"e");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();

不共享:多个线程对不同对象中的实例变量进行操作   参考:com.thread1.t6

MyThread t1 = new MyThread("a");
MyThread t2 = new MyThread("b");
MyThread t3 = new MyThread("c");
t1.start();
t2.start();
t3.start();

六、synchronized关键字

      synchronized可以在任意方法或代码块上加锁,而加锁的这段代码称为“互斥区”或“临界区”。这样,线程就排队来执行加锁的代码。

      当一个线程想要执行同步方法里面的代码时,线程首先尝试去拿这把锁,如果能够拿到这把锁,那么这个线程就可以执行synchronized里面的代码。如果拿不到这个锁,那么这个线程就会不断地尝试拿这把锁,直到能够拿到为止,而且是有多个线程同时不争抢这把锁。

七、currentThread()方法:

作用:返回代码段正在被哪个线程调用的信息。参考:com.thread1.t8、com.thread1.t9

      在main()方法中调用start(),那么run()方法是由Thread-0执行的;如果在main()方法中直接调用run()方法,此时run()方法是由main线程执行的。

Thread.currentThread().getName():静态方法(实体) ---- 得到的是调用这个方法的线程的名字

this.getName():实例方法(实例) ---- 这个类的对象,如果是‘死的线程’(没有调用start()方法),则默认赋值:Thread-0

这里先总结几点:引用来自https://www.cnblogs.com/xujintao/p/7380269.html

  • 一个进程里main线程是一开始就活着的,但是,它跟main方法一点关系也没有,仅仅同名而已。
  • 谁调用了start()方法,谁才把自己交给“线程规划器”,谁才是活着的线程,否则,不管是继承Thread还是实现了Runnable,都是一个普通的对象。
  • 继承Thread类或者实现了Runnable接口的对象具有运行线程的资格,但是,只要它没有调用start()方法,它就是一个普通的对象,有资格,并不代表就是线程。线程是活着的,对象是死的。
  • 线程可以级级包裹,使用Thread(Runnable targer)或者Thread(Runnable targrt,String name)构造方法。
  • 如果在main方法中调用run()方法,仅仅就是一个函数的调用;只有让线程规划器去调用,才是一个线程。

八、isAlive()方法

作用:判断当前的线程是否处于活动状态  参考:com.thread1.t10

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

      在使用isAlive()方法时,如果将线程以构造参数的方式传递给Thread对象进行start()启动时,运行的结果是有差异的,主要是Thread.currentThread()和this的差异。参考:com.thread1.t11

九、sleep()方法

作用:在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行),这个“正在执行的线程”是指Thread.currentThread()返回的线程。参考:com.thread1.t12

十、getId()方法

作用:取得线程的唯一标识   参考:com.thread1.t13

十一、停止线程

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

      在Java中由3中方法可以终止正在运行的线程:

  1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
  2. 使用stop方法强行终止线程,但是不推荐使用这个方法,因为stop和suspend及resume一样,都是作废过期的方法,使用它们可能产生不可预料的结果。
  3. 使用interrupt方法中断线程。

(一)、interrupt方法

1.1 interrupt()方法概念

       调用interrupt()方法仅仅是在当前线程中大了一个停止的标记,并不是真的停止线程。参考:com.thread1.t14

1.2 判断线程的状态是不是停止的

  • this.interrupted():测试当前线程(当前线程:指的是运行this.interrupted()方法的线程)是否已经中断。如果连续两次调用interrupted()方法,则第二次调用将返回false,因为调用一次之后进行了清除。参考:com.thread1.t15

  • this.isInterrupted():测试线程对象是否已经中断,不清楚状态标识。参考:com.thread1.t16

1.3 停止线程——异常法

        使用this.interrupt()给线程打了停止标识之后,要在线程中使用this.interrupted()或者isInterrupted()方法来判断线程是否是停止状态,如果是停止状态,则后面的代码不再运行。参考:com.thread1.t17

此时,要使用异常法来终止线程,保证后面的后面的代码不再执行。参考:com.thread1.t18

1.4 停止线程——在沉睡停止的线程

  • 先休眠再停止:会进入catch语句。参考:com.thread1.t19
  • 先停止在休眠:线程会一直运行,遇到sleep方法时,才进入catch语句,不会立即停止。参考:com.thread1.t20

1.5 停止线程——与return结合    参考:com.thread1.t23

(二)、stop方法

2.1 停止线程——暴力停止Stop() 参考:com.thread1.t21

2.2 stop()的缺点

  1. 如果强制停止线程有可能使一些清理性的工作得不到完成。
  2. 对锁定的对象进行了“解锁”,导致数据得不到同步的处理,出现数据不一致的问题。参考:com.thread1.t22

十二、暂停线程

     暂停线程意味着此线程还可以恢复运行。在Java多线程中,可以使用suspend()方法暂停线程,使用resume()方法恢复线程的执行参考:com.thread1.t24

1、suspend与resume方法的缺点——独占    参考:com.thread1.t25

      suspend与resume方法如果使用不当,极易造成公共的同步对象的独占,使得其他线程无法访问公共同步对象。

2、suspend与resume方法的缺点——不同步     参考:com.thread1.t26

      使用suspend与resume方法时,也容易出现出现因为线程的暂停而导致数据不同步的情况。

十三、yield方法

作用:放弃当前的CPU资源,将它让给其他的任务去占用CPU执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。 参考:com.thread1.t27

十四、线程的优先级

概念:优先级高的线程得到的CPU资源较多,也就是CPU先运行优先级较高的线程对象中的任务

线程优先级的作用:有助于“线程规划器”确定在下一次选择哪一个线程来优先执行。

setPriority()方法:设置线程的优先级

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

1、线程优先级具有继承性    参考:com.thread1.t28

概念:线程优先级具有继承性,比如A线程启动B线程,则B线程的优先级和A先级的优先级时一样的。

2线程优先级具有规则性    参考:com.thread1.t29

解释:高优先级的线程总是大部分先执行完(CPU尽量将执行资源让给优先级比较高的线程),但是不代表全部执行完。

3、优先级具有随机性

解释:线程的优先级还具有“随机性”,也就是优先级高的线程不一定每一次都先执行完。

       优先级较高的线程并不一定每一次都先执行完run()方法中的任务。

4、优先级高的线程运行的快

十五、守护线程

       Java中有两种线程:用户线程(非守护线程)和守护线程。

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

      典型的守护线程是垃圾回收线程,当进程中没有非守护线程了,则垃圾回收机制也就没有存在的必要了,自动销毁。用个比较通俗的比喻来解释“守护线程”就是:任何一个守护线程都是整个JVM中所有非守护线程的“保姆”,只要当JVM实例中存在任何一个非守护线程没有结束,守护线程就在工作,只有当最后一个非守护线程结束时,守护线程才随JVM一同结束工作。

      Daemon的作用就是为其他线程的运行提供便利服务。     参考:com.thread1.t30

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值