停止线程
在Java中有三种方法可以终止正在运行的线程:
- 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止
- 使用stop方法强行终止线程(不推荐),因为stop和suspend及resume一样,都是作为过期的方法,使用它们可能产生不可预料的结果
- 使用interrupt方法中断线程
判断线程是否是停止状态,Thread类中有以下两个方法:
- this.interrupted():测试当前线程是否已经中断,执行后具有将状态标志置为清除状态(false)的功能
- this.isInterrupted():测试线程是否已经中断,不清除状态标志
暂停线程
suspend()方法暂停线程,resume()方法恢复线程的执行
缺点:
- 独占,使用suspend和resume方法时,如果使用不当,极易造成公共的同步对象的独占,使得其他线程无法访问公共同步对象
- 不同步,容易出现因为线程的暂停而导致数据不同步的情况
yield方法
yield()方法的作用是放弃当前的CPU资源,将它让给其他的任务去占用CPU执行时间,但放弃的时间不确定,有可能刚刚放弃,马上又获取CPU时间片
线程的优先级
- CPU优先执行优先级较高的线程对象中的任务,设置线程优先级使用setPriority()方法,值为1~10
- 线程优先级具有继承性,比如A线程启动B线程,则B线程的优先级与A线程是一样的
- 优先级具有一定的规则性,也就是CPU尽量将执行资源让给优先级比较高的线程
- 优先级具有随机性
守护线程
在Java线程中有两种线程,一种是用户线程,另一种是守护线程
守护线程是一种特殊的线程,它的特性有”陪伴”的含义,,Daemon的作用就是为其他线程的运行提供便利的服务,当进程中不存在非守护线程了,则守护线程自动销毁。典型的守护线程就是垃圾回收线程
当进程中不存在非守护线程时,则守护线程自动销毁 ---比如gc
在两个线程访问同一个对象中的方法没有同步的方法时,则可能出现非线程安全问题
在两个线程访问同一个对象中的同步方法时一定是线程安全的,
脏读:一定会出现在操作实例变量的情况下,是不同线程争抢实例变量的结果。
对象及变量的并发访问
synchronized同步方法
- 方法内的变量为线程安全(方法内变量为私有变量)
- 多个对象多个锁,如果多个线程访问多个对象,则jvm会创建多个锁
- 可重入锁,自己可以再次获取自己的内部锁,在一个synchronized方法/块的内部调用本类的其他synchronzed方法/块时,是永远可以得到锁的。
- 可重入锁也支持父子类继承的环境中,子类是完全可以通过可重入锁调用父类的同步方法的
- 当一个线程执行代码出现异常,锁自动释放
- 同步不具有继承性
synchronized同步语句块
用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行一个长时间的任务,那么B线程必须等待比较长的时间
synchronized同步代码块的使用:当两个并发线程访问同一个对象object中的synchronized(this)同步代码块时,一段时间内只能有一个线程被执行,另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
即为在synchronized块中就是同步执行,不在synchronized块中就是异步执行,这样可以提高运行效率
在使用synchronized(this)代码块时需要注意的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对同一个object中所有其他synchronized(this)同步代码块的访问将被阻塞,这说明synchronized使用的”对象监视器”是一个
同步synchronized(this)代码块是锁定当前对象的
多个线程调用同一个对象中的不同名称的synchronized同步方法或synchronized(this)同步代码块时,调用的效果就是按顺序执行,也就是同步的,阻塞的。
除了使用synchronized(this)格式来同步代码块外,Java还支持对”任意对象”作为”对象监视器”来实现同步功能,这个”任意对象”大多数是实例变量及方法的参数,使用格式为synchronized(非this对象x)
- 1)在多个线程持有”对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象x)同步代码块中的代码——当多个线程同时执行synchronized(x){}同步代码块时呈同步效果
- 2)当持有”对象监视器”为同一个对象的前提下,同一时间只有一个线程可以执行synchronized(非this对象x)同步代码块中的代码——当其他线程执行x对象中synchronized同步方法时呈同步效果
- 3)锁非this对象具有一定的优点:如果在一个类中有很多个synchronized方法,这时虽然能实现同步,但会受到阻塞,所以影响运行效率,但如果使用同步代码块锁非this对象,则synchronized(非this)代码块中的程序与同步方法是异步的,不与其他锁this同步方法争抢this锁,则可以大大提高运行效率——当其他线程执行x对象方法里面的synchronized(this)代码块时也呈现同步效果
org.gradle.jvmargs=-Xmx1024m
静态同步synchronized方法与synchronized(class)代码块
关键字synchronized还可以应用在static静态方法上,相当于对当前*.java文件对应的Class类进行持锁,Class锁可以对类的所有对象实例起作用
同步synchronized(class)代码块的作用和synchronized static方法的作用一样
多线程的死锁
死锁:不同的线程都在等待根本不可能被释放的锁,从而导致所有的任务都无法继续完成
当多个线程同时持有锁对象时,如果持有的是相同的锁对象,这些线程之间就是同步的,否则就是异步的
volatile关键字
主要作用是使变量在多个线程间可见,关键字volatile强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值,不具备同步性,原子性
使用场合是在多个线程中可以感知实例变量被更改了,并且可以获得最新的值使用,也就是多线程读取共享变量时可以获得最新值得使用。
会提示线程每次从共享内存中读取变量,而不是从私有内存读取。
volatile解决的是变量在多个线程之间的可见性,而synchronized关键字解决的是多个线程之间访问资源的同步性,而且具有将工作内存中的私有变量与公共内存中的变量同步的功能。
第三章线程之间的通讯
等待通知机制(wait/notify)
*必须在同步代码中使用,lock.wait,lock.notify
wait使当前执行的代码进行等待,将当前线程放入预执行队列,并在所在代码处停止执行,直到通知或被中断,在wait前需获得对象锁,执行wait后,释放锁
notify也在同步代码块中使用,notify后,线程规划器会随机挑选一个在同步代码锁对象上wait的线程唤醒,在notify所在的同步代码执行完成后,选择唤醒的线程进行执行,
notify本身不释放锁
Object中等待与通知的锁模型。
调用wait()方法,当前线程释放锁,会在wait()代码停止执行,直到中断或接到通知。
调用notify()方法会在线程退出同步体后释放锁,并通知其他在等待对象锁的线程。
notifyAll唤醒所有在锁上的线程
每个锁都有两个队列,一个是就绪队列,一个是阻塞队列,就绪队列为将要获得锁的线程,阻塞队列为被阻塞的线程
当线程呈wait状态时,调用线程的interrupt()方法会出现interruptExcption
方法wait(long)功能是等等某一时间内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒
*生成者消费者
线程之间的通讯,通过管道流,pipedInputStream,pipedOutputStream,pipedReader,pipedWriter
join方法
join方法使当前线程等待,直到调用该方法的线程运行完在执行当前线程,join内部使用的事wait
使用join会释放当前线程的锁,如果当前线程加锁了的话
ThreadLocal
ThreadLocal会为每个线程分配一个Object,每个线程可以获得各自的Object,虽然在一个共有对象内,
也就是说:类A里有个ThreadLocal属性,不同线程调用A中的ThreadLocal的set方法放入不同的值,不同线程在调用ThreadLocal的get方法时会获得自己set的值
InheritableThreadLocal会使子线程获得父线程继承下来的值,如果子线程未设置