Java多线程
1、背景
每个Java程序都有一个默认的主线程main。如果main方法中又创建了其他线程,那么JVM就要在主线程和其他线程之间轮流切换,保证每个线程都有机会使用CPU资源,main方法即使执行完最后的语句(主线程结束),JVM也不会结束Java应用程序,JVM一直要等到Java应用程序中的所有线程都结束之后,才会结束Java程序。
2、线程的状态和生命周期
Java中使用Thread类及其子类对象来表示线程。
(1).新建
(2).运行
(3).中断:
JVM将CPU资源从当前线程切换给其他线程,使本线程让出CPU使用权进入休眠状态
线程使用期间执行了sleep(int millsecond)方法,是当前线程进入休眠状态,等待millsecond毫秒后重新进入排队队列等待CPU资源,以便在中断处继续运行
线程使用CPU期间,执行了wait()方法,是当前线程进入等待状态,不会主动进入到线程队列中排队等待CPU资源,必须有其他线程调用notify()方法通知他使他从新回到线程队列中等待CPU资源,以便在中断处继续运行
线程使用CPU资源期间,执行了某个操作进入阻塞状态,比如执行读/写操作引起的阻塞。进入阻塞状态的线程不能进入排队队列,只有当引起阻塞的原因消除时,线程才重新进到线程队列中排队等待CPU资源,以便从原来中断处开始继续运行
(4).死亡
正常运行完run()方法
被提前强制终止run()方法
注:
在java中,多个线程同时执行可能会造成线程安全问题(线程之间同时拥有一个变量,且发生了修改),为了避免这个问题,需要线程锁(synchronized)来使一个线程执行时,其他线程会等待这个线程执行完毕才执行,而不是”同时“执行。
3、 Thread常用方法
*1.start():启动当前的线程,调用当前线程的run()
*2.run():通常需要重写Thread类中的此方法,将创建要执行的操作声明在此方法中
*3.currentThread():静态方法,返回代码执行的线程
*4.getName():获取当前线程的名字
*5.setName():设置当前线程的名字
*6.yield():释放当前CPU的执行
*7.join():在线程a中调用线程b的join()方法,此时线程a进入阻塞状态
*8.stop():已过时的方法,当执行此方法是,强制结束当前进程
*9.sleep(long millitime):让当前进程睡眠指定的毫秒数,在指定时间内,线程是阻塞状态
*10.isAlive():判断进程是否存活
4、举个栗子
package day06;
/**
* 多线程安全并发问题
* 当多线程并发操作到了同一临界资源时,由于线程切换的时间不确定,
* 导致操作未按程序设计的执行,运行出现混乱,严重时可能导致系统瘫痪
*
* 临界资源:只能被单线程操作的资源
* @author ASUS
*
*/
public class synTest1 {
public static void main(String[] args) {
Table t = new Table();
Thread t1 = new Thread(){ //创建一个线程
public void run(){ //重写run( )方法,将需要执行的操作声明在此方法中
while(true){
//bean变量用来记录拿了多少颗豆子
int bean = t.getBean();
if(t.getBean()<0){
break;
}
Thread.yield(); // 暂停当前正在执行的线程对象,并执行其他线程。
System.out.println(getName()+":"+bean);
}
}
};
Thread t2 = new Thread(){
public void run(){
while(true){
//bean变量用来记录拿了多少颗豆子
int bean = t.getBean();
if(t.getBean()<0){
break;
}
Thread.yield();
System.out.println(getName()+":"+bean);
}
}
};
t1.start();
t2.start();
}
/*
* 给线程定义一个小游戏
*
* 这里需要两个线程来完成,从桌子上拿豆子
*
* 当一个方法使用了synchronized修饰后,该方法就称为同步方法。也就是说超过一个线程就不可以在方法内部执行
* 将多个线程并发操作临界资源改为同步执行时,就可以解决多线程并发的安全问题
*/
}
class Table{
private int beans = 20;
public synchronized int getBean(){
//让线程主动放弃CPU时间,模拟线程执行到这里就没有时间了
Thread.yield();
return beans--;
}
}
概念补充:
一、线程概念
线程可以理解为在进程中独立运行的子任务。线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.
一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.
二、线程与进程的区别
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
线程的划分尺度小于进程,使得多线程程序的并发性高。
另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。
三、与线程相关的三个概念
串行:一个线程执行到底,相当于单线程。
并发:多个线程交替执行,抢占cpu的时间片,但是速度很快,在宏观角度看来就像是多个线程同时执行。
并行:多个线程在不同的cpu中同时执行。
四、在Java的开发包中,已经自带对多线程技术支持。实现对多线程编程的方式有两种,一种是继承Thread类,第二种是实现Runnable接口,第三种是实现Callable接口。常用的为前两者。先来看看Thread类结构