最近赶着金三银四的风潮,我也顺应了时代一次,果断辞去工作,加入了找工作的大军当中,面来面去,发现大家都对java多线程这块比较感兴趣,于是在工作安定下来之后,我决定重新梳理一下多线程的知识,当做是一个学习复习笔记,看到的朋友,也请大家指出疏漏之处,在此谢过大家了。好了,话不多说,开始正题。
线程的几种状态:
- 新生:用关键字new创建一个线程
- 就绪:线程创建后,其它线程调用对象的start()方法,该线程就处于就绪队列当中变得可运行,只等待获取cpu的使用权,通俗点讲就是万事具备,只欠cpu的状态,即除了cpu,其它运行所需资源已全部获取。
- 运行:就绪状态的线程获取cpu,执行程序代码
- 阻塞:线程因为某种原因放弃cpu的使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
- 等待阻塞:运行的线程执行wait()方法,该线程会释放占有的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其它线程调用notify()或notifyAll()方法才能被唤醒
- 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中
- 其它阻塞:运行的线程执行sleep()或join()方法,或者发出了IO请求时,JVM会把该线程置为阻塞状态。当sleep()方法超时,join等待线程终止或者超时,或者IO处理完毕时,线程重新转入就绪状态
5.死亡:线程执行完了或者因异常退出的run()方法,或者执行了stop()方法(不推荐使用这种方式),该线程结束生命周期。
关于生命周期的详细介绍,参考文章:https://www.cnblogs.com/jijijiefang/articles/7222955.html
下面附一个银行取款的小案例来帮助我们理解java多线程的运行机制,话不多说,直接上代码:
共享变量Bank:
package com.cn.bank;
/**
* 银行
*
*/
public class Bank {
//帐户余额
private int sum =0;
/**
* 存钱
* @param money
*/
public void addMoney(int money){
sum += money;
System.out.println(System.currentTimeMillis()+"存进"+money);
}
/**
* 取钱
* @param money
*/
public void subMoney(int money){
if(sum - money<0){
System.out.println("余额不足");
}else{
sum -= money;
System.out.println(System.currentTimeMillis()+"取出"+money);
}
}
/**
* 查询余额
*/
public void lookMoney(){
System.out.println("帐户余额:"+sum);
}
}
存钱线程:
package com.cn.bank;
/**
* 存钱线程
*
*/
public class AddM implements Runnable{
private Bank bank;
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
synchronized (Bank.class) {
bank.addMoney(100);
bank.lookMoney();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public AddM(Bank bank) {
super();
this.bank = bank;
}
}
取钱线程:
package com.cn.bank;
/**
* 取钱线程
*
*/
public class SubM implements Runnable {
private Bank bank;
@Override
public void run() {
// TODO Auto-generated method stub
while (true) {
synchronized (Bank.class) {
bank.subMoney(100);
bank.lookMoney();
try {
Thread.sleep(5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public SubM(Bank bank) {
super();
this.bank = bank;
}
}
为了能够更好的看到两个线程的交替执行,我分别对两个线程设置了执行时间,两个线程的执行时间设置的不一样,这样做的主要目的,是为了看到余额不足的情况(取钱线程执行时间大于存钱线程)。
测试类:
package com.cn.bank;
public class Test {
public static void main(String[] args) {
final Bank bank = new Bank();
//创建存钱线程
AddM addM = new AddM(bank);
Thread th1 = new Thread(addM, "存钱线程");
//创建取钱线程
SubM subM = new SubM(bank);
Thread th2 = new Thread(subM, "取钱线程");
th1.start();
th2.start();
}
}
说几个主要的地方,首先就是要明白,Thread类的开始是在run()方法,这个就相当于我们java中的mian()(其实也是一个线程,我们叫它主线程),我的个人理解是这个线程的所有操作都要在这个run()方法中来实现,共享变量可以通过方法参数的形式进行传入。所以在run()方法中做好同步工作就非常重要了。run()方法是主线程在调用对象执行start()方法之后 自动执行的,不能手动调用。还有就是锁的范围不能太广,此处可以采用细粒度锁来提高程序的性能,关于这个锁的问题,后续还会继续更新笔记,谢谢您的阅读。