java多线程核心技术---学习笔记

1.java多线程技能

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

进程:进程是操作系统的基础,是一次程序的执行,是一个程序及数据在处理机上顺序执行时所发生的活动。是程序在一个数据集合上运行的过程,他是系统进行资源分配和调度的一个独立单位。

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

多线程优点:最大限度的利用CPU的空闲时间来处理其他子任务。

1.2. 使用多线程

直接使用java main方法运行程序,也运行了一个线程。一个进程至少有一个线程在运行。

实现多线程的方式主要有两种: 继承Thread类,实现Runnable接口。其实Thread 也是实现了Runnable接口,两者没有本质的区别。

直接启动run方法,没有异步效果,即使起了很多线程也会是单线程执行的模式,指向start方法才会异步执行,将线程对象交给线程规划器执行。

执行start的顺序,不代表线程启动的顺序,start方法只是通知线程调度器此线程已经准备就绪,具体顺序由jvm控制。

为了可以继承其他的类,一般采用实现Runnable接口的方式编写多线程代码。

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

jvm中 i - - 分三步执行,取得i值,计算i-1,对i进行复制。多个线程同时访问,就会出现问题

1.3. currentThread()方法

返回代码段正在被哪个线程调用的信息。

1.4. isAlive()方法

判断当前线程是否处于活动状态

线程处于正在运行,或者准备开始运行的阶段,就认为线程是存活测状态。

currentThread 表示当前执行的线程,this表示当前的对象

1.5.sleep()方法

在指定的毫秒数内让当前正在执行的线程 休眠。

1.6. getId()方法

取得线程的唯一标识

1.7. 停止线程

使用退出标志,使线程征程退出,run执行完之后退出
使用stop强行停止线程, 不推荐这么使用,stop,suspend及resume都是作废过期的方法
使用interrupt方法中断线程,interrupt只是给当前线程打了一个停止标志,并不是真正的停止线程。

this.interrupted()测试当前线程是否已经是中断状态,执行后具有将状态标志清除为false的功能。
this.isInterrupted()测试线程对象是否已经是中断状态,不清除状态标志。

线程中抛出异常也会停止线程。

在sleep状态进入执行interrupt,会抛出,InterruptedException异常,反过来也是一样的

可以使用interrupt和return结合来停止线程,不过建议使用抛异常的方式来实现线程的停止。异常可以上抛,使异常事件得以传播。

1.8. 暂停线程

suspend和resume方法分别是暂停和恢复线程的运行,但是极易造成,公共的同步对象独占,其他线程无法访问到这个对象,问题很多,建议不适用。

1.9 yield()方法

放弃当前的CPU资源。但是自己会进入就绪队列,所以可能放弃之后又马上占有CPU资源

1.10. 线程的优先级

优先级越高的线程,就越容易得到CPU资源,但是具体由谁来执行还是由线程规划器来确定执行的线程。使用setPrisetPriority()方法设置优先级,级别为1-10

线程的优先级具有继承性,执行线程默认和启动线程具有相同的优先级
线程的优先级和代码的执行顺序无关,但是CPU尽量将执行资源让给优先级比较高的线程。
线程优先级的特性:继承性,规则性和随机性

1.11. 守护线程

线程有两种:用户线程和守护线程
守护线程是一种特殊的线程,没有非守护线程时,守护线程会自动销毁。
垃圾回收机制就是典型的守护线程,使用setDaemon(true)就可以将线程设置为守护线程。

2. 对象及变量的并发访问

2.1. synchronized同步方法

方法内部的变量永远是安全的,因为作用域就在方法内
两个线程访问同一个对象的同步方法时,一定是线程安全的。
关键字synchronized取得的锁都是对象锁,而不是将一段代码或者方法当做锁

只有共享资源的读写访问才需要同步化,如果不是共享资源,就不需要同步

锁重入:synchronized具有锁重入的功能,当一个线程获取对象锁之后,再次请求此对象锁时是可以再次得到该对象的锁的。存在子父类继承关系时,子类完全可以通过可重入锁调用父类的同步方法。

当一个线程执行的代码出现异常时,其所持有的锁会自动释放。

同步不具体继承性,子类方法无法继承父类的synchronized关键字

2.2. synchronized同步语句块

synchronized申明方法是有弊端的,只有当方法执行完毕之后才会释放对象锁。使用同步块的话可以这样的问题。
当两个并发线程访问同一个对象的synchronized(this) 同步代码块时,一段时间内只有一个线程被执行,另一个线程必须等待当前线程执行完这个代码执行完以后才能执行该该代码。
当一个线程访问objec的synchronized(this)同步代码块时,其他线程对同一个Object中其他所有的同步代码块的访问都会被阻塞。
synchronized(非this对象)代码块的内容和同步方法是异步的。

synchronized关键字加到static 静态方法上时,是给Class加锁,加在非静态方法时时给对象加锁

死锁:互相等待对方释放锁
如果同时持有相同的锁,那么这些线程就是同步的,如果分别获取对象锁,这些线程就是异步的。

2.3. volatile 关键字

主要作用是使变量在多个线程中可见。强制从公共堆栈中取得变量的值,而不是从私有数据栈中取得变得值。
volatile最致命的缺点就是不支持原子性。

原子操作是不能分割的整体,没有其他线程能够中断或者检查原子操作中的变量。使用AtomicInteger实现原子性

3. 线程间的通信。

3.1. 等待/通知 机制

通过while循环侦测某一个条件,缺点是浪费CPU资源

通过wait,notify 等待、通知机制实现线程的通信,wait的作用是使当前执行的线程进行等待。
在调用wait之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步快中调用wait方法,在执行wait方法之后,当前线程释放锁。
notify也要在同步方法或者同步方法块中调用,在调用之前,线程也必须获得该对象的对象级别锁。
执行notify方法之后,当前线程也不会马上释放该锁,呈wait状态的线程也不会马上获得该对象锁,需要等到notify线程将程序执行完毕。

notify可以随机唤醒等待队列中等待同一共享资源的一个线程,并使该线程进入等待状态。notifyAll方法可以使所有的正在等待队列中等待统一共享资源的的全部线程从等待状态退出。

方法wait执行完之后,锁会被自动释放,但是notify方法被执行之后,锁不会被释放,而需要等待线程被执行完。wait(long)方法会在等待一定时间后自动唤醒,当然也可以notify唤醒

生产者消费者问题建议看书。

3.2.方法join的使用

等待线程对象销毁执行。方法join具有使线程排队运行的作用,有点类似同步的效果。

join(long)设置等待的时间。join(long)内部使用wait实现,所以会释放锁,sleep(long)却不释放锁。

3.3 类ThreadLocal的使用

为每个线程绑定自己的值。数据具有隔离性。

4. lock 的使用

 ReenTrantLock类的使用,在需要同步的代码前使用lock.lock(),结束处使用lock.unlock()释放锁
关于这块,建议看书。主要讲等待/通知的另外一种实现方式。

5. 定时器timer Timer视屏

可以参考书籍和慕课网 
主要讲解
    schedule(TimerTask task, Date time)
    schedule(TimerTask task, Date firstTime, long period)
    schedule(TimerTask task, long delay)
    schedule(TimerTask task, long delay, long period)
    scheduleAtFixedRate(TimerTask task, long delay, long period)

6. 单例模式和多线程

主要讲解 单例模式在多线程中的安全性

7. 拾遗增补

7.1. 线程的状态

NEW 至今尚未启动的
RUNNABLE 正在java虚拟机中执行的线程
BLOCKED    受阻塞并等待某个监控器锁的线程
WAITTING    无限期的等待另一个线程来执行某一个特定操作的线程
TIME_WAITING    等待一个线程执行取决于指定等待时间
TERMINATED        已退出的线程

7.2 线程租

可以将线程归属到某一个线程组中,线程组中可以拥有线程对象,也可以拥有线程组,组中还可以有线程。
线程组可以批量管理线程或者线程组对象,有效的对线程或线程组对象进行组织。

猜你喜欢

转载自my.oschina.net/u/2615530/blog/1783653