基础知识:操作系统将CPU的时间片分配给每一个线程使用,给人一种并行处理的感觉。
多进程和多线程的区别:每个进程都有自己的一整套不变量(独立代码和数据空间),进程切换开销大含多个线程;
而线程共享数据,每一个线程都有自己独立的运行栈和程序计数器,线程切换开销小
多线程是为了提高CPU的利用率。
--》在java中,每次运行一个程序都会启动两个线程:一个是main线程,一个是JVM垃圾收集器线程。
实现多线程:实现Runnable接口和继承Thread类
--》总结:*****Runnable接口没有单继承的限制
*****适合多个相同的程序代码的线程去处理同一个资源,Thread是多个线程分别完成自己的任 务,Runnable是多个线程共同完成一个任务。如果一个类继承Thread,则不适合资源享。 但是如果实现了Runable接口的话,则很容易的实现资源共享。
Start方法和run方法的区别:
---》直接调用run方法,只会执行同一个线程中的人物,而不会启动新的线程,
而start方法才会创建一个run方法的新线程
线程的6中状态:new---Runnable---Blocked---Waiting---Timed Waiting---Terminated
新建---可运行-----阻塞--------等待------计时等待-------被终止
start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable)
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,
变得可运行
,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行
wait()方法
,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的
同步锁时
,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行
sleep()或join()方法
(等待终止指定的线程)
,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
常用函数:
1、setPriority(): 更改线程的优先级。
2、线程睡眠:Thread.sleep(long millis)方法,使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。sleep()平台移植性好。
3、线程等待:Object类中的wait()方法,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是Object类中的方法,行为等价于调用 wait(0) 一样。
4、线程让步:Thread.yield() 方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。
5、线程加入:join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。
6、线程唤醒:Object类中的notify()方法,唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。 直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。类似的方法还有一个notifyAll(),唤醒在此对象监视器上等待的所有线程。
解决线程安全问题:同步(5种同步方式)
---1》上锁:关键字synchronized,锁是可重入的(即可以嵌套,对锁计数)
----》1.同步方法
即用synchronized关键字修饰的方法。
由于java的每个对象都有一个内置锁,当用此关键字修饰方法时,
内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
public synchronized void save(){}
----》2.同步代码块
即有synchronized关键字修饰的语句块。
被该关键字修饰的语句块会自动被加上内置锁,从而实现同步
synchronized(object){}、
注:同步是一种高开销的操作,因此应该尽量减少同步的内容。
通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。
----》3.使用特殊域变量(volatile)实现线程同步
a.volatile关键字为域变量的访问提供了一种免锁机制,
b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,
c.因此每次使用该域就要重新计算,而不是使用寄存器中的值
d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
class Bank {
//需要同步的变量加上volatile
private volatile int account = 100;
public int getAccount() {
return account;
}
public void save(int money) {
account += money;
}
}
---》4.使用重入锁实现线程同步
---》5.使用局部变量实现线程同步