《Java多线程编程核心技术》-笔记

来源:互联网 发布:c编程思想pdf下载 编辑:程序博客网 时间:2024/04/29 13:57

第一章      Java多线程技能

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

我们可以将一个正在操作系统中运行得exe程序理解成一个“进程”。进程是受操作系统管理的基本运行单元。

线程可以理解成是在进程中独立运行的子任务。比如,QQ.exe运行时就有很多的子任务在同时运行,比如:传文件、听音乐、发送表情等功能都有对象的线程在后台默默地运行。

单任务的特点就是排队执行,也就是同步,就像在cmd中输入一条命令后,必须等待这条命令执行完才可以执行下一条命令一样。多任务,cpu可以在任务1和任务2之间来回切换,使任务2等到10秒再运行,系统的运行效率大大得到提升。使用多线程就是在使用异步。

1.2     使用多线程

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

1.2.1  继承Thread类

其实,使用继承Thread类的方式创建新线程时,最大的局限就是不支持多继承。用Thread和Runnable两种方式创建的线程在工作时的性质是一样的,没有本质区别。

线程是一个子任务,cpu以不确定的方式,或者说是以随机的时间来调用线程中的run方法。

Thread.java类中的start()方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象的run()方法。这个过程其实就是让系统安排一个时间来调用Thread中的run()方法,也就是使线程得到运行,启动线程,具有异步执行的效果。如果调用代码thread.run()就不是异步执行了,而是同步,那么此线程对象并不交给“线程规划器”来进行处理,而是由main主线程来调用run()方法,也就是必须等run()方法中的代码执行完后才可以执行后面的代码。

执行start()方法的顺序不代表线程启动的顺序。

 

 

 

1.2.2 实现Runnable接口

1.2.3 实例变量与线程安全

共享数据的情况就是多个线程可以访问同一个变量。比如在实现投票功能的软件时,多个线程可以同时处理同一个人的票数。

某些JVM中,i—的操作要分成如下3步:

1)        取得原有i值

2)        计算i-1

3)        对i进行赋值

在这3个步骤中,如果有多个线程同时访问,那么一定会出现非线程安全问题。

其实这个示例就是典型的销售场景:5个销售员,每个销售员卖出一个货品后不可以得出相同的剩余数量,必须在每一个销售员卖完一个货品后其他销售员才可以在新的剩余物品数上继续减1操作。这时就需要使多个线程之间进行同步,也就是用按顺序排队的方式进行减1操作。

在run()方法前加入synchronized关键字,使多个线程在执行run方法时,以排队的方式进行处理。当一个线程调用run前,先判断run方法有没有被上锁,如果上锁,说明有其他线程正在调用run方法,必须等其他线程对run方法调用结束后才可以执行run方法。这样也就实现了排队调用run方法的目的,也就达到了按顺序对count变量减1的效果了。Synchronized可以在任意对象及方法上加锁,而加锁的这段diamante称为“互斥区”或“临界区”。

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

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

1.2.4 留意i—与System.out.println()的异常

虽然println()方法在内部是同步的,但i—的操作却是在进入println()之前发生的,所以有发生非线程安全问题的概率。所以,还是应该继续使用同步方法。

1.3     currentThread()方法

currentThread()方法可返回代码段正在被哪个线程调用的信息。

run方法时自动调用的方法。

1.4     isAlive()方法

isAlive()的作用是测试线程是否处于活动状态。活动状态就是线程已经启动且尚未终止。线程处于正在运行或准备开始运行的状态,就认为线程时“存活”的。

1.5 sleep()方法

1.6 getId()方法

1.7 停止线程

停止线程时在多线程开发中很重要的技术点。

停止一个线程意味着在线程处理完任务之前停掉正在做的操作,也就是放弃当前的操作。停止一个线程可以使用Thread.stop()方法,但是这个方法时不安全的,并且已经被废弃了。

大多数停止一个线程的操作使用Thread.interrupt()方法,但是还需要加入一个判断才可以完成线程的停止。

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

1)        使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。

2)        使用stop方法强行终止线程,但是不推荐使用。因为stop和suspend及resume一样,都是作废过期的方法,使用它们可能产生不可预料的结果。

3)        使用interrupt方法中断线程

1.7.1 停止不了的线程

1.7.2 判断线程是否是停止状态

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

1.7.4 在沉睡中停止

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

1.7.6 方法stop()与java.lang.ThreadDeath异常

1.7.7 释放锁的不良后果

1.7.8 使用return停止线程

1.8 暂停线程

1.8.1 Suspend与resume方法的使用

1.8.2 Suspend与resume方法的确定——独占

1.8.3 Suspend与resume方法的缺点——不同步

1.9 yield方法

1.10 线程的优先级

1.10.1 线程优先级的继承特性

1.10.2 优先级具有规则性

1.10.3 优先级具有随机性

1.10.4 看谁运行得快

1.11 守护线程

1.12 本章小结

第二章 对象及变量的并发访问

“非线程安全”产生的后果是“脏读”,也就是取到的数据其实是被更改过的。而“线程安全”就是已获得的实例变量的值是经过同步处理的,不会出现脏读的现象。

“非线程安全”问题存在于“实例变量”中,如果是方法内部的私有变量,则不存在“非线程安全”问题。因为方法内部的变量是私有的。

调用关键字synchronized声明的方法一定是排队运行的。要记住“共享”这两个字,只有共享资源的读写访问才需要同步化,如果不是共享资源,那么根本没有同步的必要。

脏读一定会出现在操作实例变量的情况下,这就是不同线程“争抢”实例变量的结果。

Synchronized关键字拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到一个对象锁后,再次请求此对象锁时可以再次得到该对象的锁的。这也证明在一个synchronized方法/块的内部调用本类的其他synchronized方法/块时,是永远可以得到锁的。

 

第三章 线程间通信

本章重点:

1.        使用wait/notify实现线程间的通信

2.        生产者/消费者模式的实现

3.        方法join的使用

4.        ThreadLoacl类的使用

 

线程与线程之间不是独立的个体,它们彼此之间可以互相通信和协作。

方法wait()的作用是使当前执行代码的线程进行等待,wait()方法是Object类的方法。该方法用来将当前线程置入“预执行队列”中,并且在wait()所在代码行处停止执行,直到接到通知或被中断为止。在调用wait()之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步代码块中调用wait()方法。在执行wait()方法后,当前线程释放锁。在从wait()返回前,线程与其他线程竞争重新获得锁。如果调用wait()时没有持有适当的锁,则抛出IllegalMonitorStateException,它是RuntimeException的一个子类,因此,不需要try-catch语句进行捕捉异常。

方法notify()也要在同步方法或同步块中调用,即在调用前,线程也必须获得该对象的对象级别锁。如果调用notify()时没有持有适当的锁,也会抛出IllegalMonitorStateException。该方法用来通知那些可能等待该对象的对象锁的其他线程,如果有多个线程等待,则由线程规划器随机挑选处一个呈wait状态的线程,对其发出通知notify,并使它等待获取该对象的对象锁。需要说明的是,在执行notify()方法后,当前线程不会马上释放该对象锁,呈wait状态的线程也并不能马上获取该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出synchronized代码块后,当前线程才会释放锁,而呈wait()状态所在的线程才可以获取该对象锁。当第一个获得了该对象锁的wait线程运行完毕以后,它会释放掉该对象锁,此时如果该对象没有再次使用notify语句,则即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,还会继续阻塞在wait状态,直到这个对象发出一个notify或notify all。

用一句话来总结一下wait和notify:wait使线程停止运行,而notify使停止的线程继续运行。

对象监视器也就是同步锁

 

 

 

 

 

第四章 Lock的使用

 

 

第五章 定时器Timer

 

 

 

 

 

第六章 单利模式与多线程

 

 

第七章 拾遗增补

 

 

 

 

阅读全文
0 0