多线程
1、什么是进程?什么是线程?
1.1进程是一个完整独立的程序(独立的代码与独立的数据空间)(资源调配)
1.2线程是进程的一个执行单元(共享的代码与共享的数据空间)(cpu调度)
2、线程内存分析
2.1共享方法区、堆内存
2.2独立一个栈
2.3每个栈与每个栈之间互不干扰,各自执行各自目的
3、主线程
3.1在java中每次程序至少启动三个线程,一个是main主线程一个是垃圾收集线程一个是异常进程
java命令执行一个类的时候,实际上都会启动一个JVM,每个JVM实际就是在
操作系统启动了一个进程
4、线程状态
(sleep\join\Thread.sleep()\.join()) 阻塞状态 (run()\main())结束
1.Thread类(new Thread)——>初始状态(start()\Thread.yield())——>可运行 运行中
(synchronized\notify()\notifyAll()) 锁池状态 等待队列(wait())
4.1新建状态:新创建一个线程对象
4.2就绪状态:线程对象调用start()方法,状态变为可运行,等待cpu使用权
4.3运行状态:就绪状态线程获得cpu使用,执行代码
4.4阻塞状态:放弃cpu使用权,暂时停止运行。知道程序进入就绪状态才有机会运行
4.4.1等待阻塞:线程执行wait(),JVM把该线程放入等待池中(会释放现有的锁)
4.4.2同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
4.4.3其他阻塞:线程执行sleep()/join()方法或者发出I/O请求,JVM就会将
该线程变为阻塞状态,当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,
线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)
4.5死亡状态:线程执行完了或者因异常退出了run()方法,该线程结束生命周期
5、线程调度
5.1Java线程有优先级,优先级高的线程会获得较多的运行机会。(概率)
5.2取值范围是1~10,Thread类有以下三个静态常量:
static int MAX_PRIORITY(10)
static int MIN_PRIORITY(1)
static int NORM_PRIORITY(5)
5.3Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级。
每个线程都有默认的优先级。主线程的默认优先级为Thread.NORM_PRIORITY。
线程的优先级有继承关系,比如A线程中创建了B线程,那么B将和A具有相同的优先级。
JVM提供了10个线程优先级,但与常见的操作系统都不能很好的映射。如果希望程序能移植到各个操作系统中,应该仅仅使用Thread类有以下三个静态常量作为优先级,这样能保证同样的优先级采用了同样的调度方式。
————————————————
版权声明:本文为CSDN博主「Evankaka」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Evankaka/article/details/44153709
@@ 思考一个问题zz
6、线程实现方式 使用了多线程机制之后main方法结束,有可能程序不会结束ma
答:main方法结束只是主线程结束了,其他的线程可能还在压栈
6.1:方式一通过继承Thread类,重写、创建子类对象、调用start()
6.2:方式二通过Runnable接口,重写、创建Thread对象、调用start()
也可以通过匿名内部类
名内部类创建,创建Thread对象(Runnable接口参数)、重写、调用start()
6.3:只有start才启动进程,run不能
一个对象一个进程
7、获取当前线程对象、获取线程对象名字、修改线程对象名字
方法名 作用
static Thread currentThread() 获取当前线程对象
String getName() 获取线程对象名字
void setName(String name) 修改线程对象名字
8、线程的分类:守护线程与用户线程
thread.setDaemon(true)改为守护
9、Thread与Runnable的区别
9.1Thread继承一个类不适合使用资源共享,Runnable实现一个接口适合资源共享
9.2Runnable的优势
1)适合多个相同的代码程序的线程去处理同一个资源
2)避免单继承的限制
3)代码可以被多个线程共享,代码与数据独立,增加层序健壮性
4)线程池的使用只能放入Runnable或callable类线程不能直接放入继承Thread类
9.3**对于Thread继承需要共享数据时,建议使用类单例模式
PS:两个关键点:1、单例;2、类属性(非实例属性)在类本身创建类属性,并通过构造器进行初始化
9.4相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。
目前两种方式,要想启动线程,都是调用的Thread类中的start()。
*/
*
PS:关于run()方法小知识点
为什么run()方法只能try...catch...不能throws
因为run()方法在父类中没有抛出异常,子类不能比父类抛出更多的异常
关于线程方法
1、关于线程的sleep方法、、
1.静态方法:Thread.sleep()
2.参数是毫秒
3.作用:让当前线程进入阻塞状态,放弃占有CPU时间片,
当睡眠结束后,就转为就绪(Runnable)状态。sleep()平台移植性好。
4.不释放锁
2、关于线程中断sleep方法interrupt
1)实例方法:void interrupt()
2)作用终止线程睡眠
3)中断th线程的睡眠(这种中断睡眠的方式依靠了Java的异常处理机制)
使得后面的代码无法执行,但可以使用finnally
4)不要以为它是中断某个线程!它只是线线程发送一个中断信号,
让线程在无限等待时(如死锁时)能抛出抛出,从而结束线程,
但是如果你吃掉了这个异常,那么这个线程还是不会中断的!
3、关于线程的yield()方法
1)静态方法:static void yield()
2)作用:让位方法,当前线程暂停,回到就绪状态
3)与sleep区别:跳过阻塞直接进入就绪状态
yield()只是使当前线程重新回到可执行状态,
所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
4、关于线程的join()方法
1)实例方法:void join()
2)作用:加入一个线程并合并,但让当前线程受阻塞,直到加入的线程结束
无参阻塞时间为:加入的线程结束
一个参数代表:阻塞该线程的时间最长为millis毫秒
两个参数代表:阻塞该线程的时间最长为millis毫秒加nanos纳秒
5、关于线程通信
1.线程通信涉及到的三个方法:
* wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
* notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
* notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
2.说明:
* 1.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
* 2.wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。
* 否则,会出现IllegalMonitorStateException异常
* 3.wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中。
* 4.notify()调用后,并不是马上就释放对象锁的,
* 而是在相应的synchronized(){}语句块执行结束,自动释放锁后,
* JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。
新增方式一:实现Callable接口。 --- JDK 5.0新增
需要先使用FutureTask构造器传参
说明:
* 如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大?
* 1. call()可以返回值的。
* 2. call()可以抛出异常,被外面的操作捕获,获取异常的信息
* 3. Callable是支持泛型的
新增方式二:使用线程池
好处:
* 1.提高响应速度(减少了创建新线程的时间)
* 2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
* 3.便于线程管理
* corePoolSize:核心池的大小
* maximumPoolSize:最大线程数
* keepAliveTime:线程没任务时最多保持多长时间后会终止
*/
5、关于线程通信
1.线程通信涉及到的三个方法:
* wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
* notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
* notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
2.说明:
* 1.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
* 2.wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。
* 否则,会出现IllegalMonitorStateException异常
* 3.wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中。
* 4.notify()调用后,并不是马上就释放对象锁的,
* 而是在相应的synchronized(){}语句块执行结束,自动释放锁后,
* JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。
6、线程同步
1.synchronized关键字
1.1同步代码块
语法:synchronized(同步监视器/锁/对象)
{
//线程同步代码块
}
重点:
synchronized后面小括号() 中传的这个“数据”是相当关键的。这个数据必须是 多线程共享 的数据。才能达到多线程排队。
说明: 1.操作共享数据的代码,即为需要被同步的代码。 -->不能包含代码多了,也不能包含代码少了。
* 2.共享数据:多个线程共同操作的变量。
* 3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。
* 要求:多个线程必须要共用同一把锁。
*
* 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。
1.2同步方法
* 如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。
* 关于同步方法的总结:
* 1. 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
* 2. 非静态的同步方法,同步监视器是:this
* 静态的同步方法,同步监视器是:当前类本身
(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,
其它线程不能同时访问这个对象中任何一个synchronized方法)
1.3Lock锁 --- JDK5.0新增
* 1. 面试题:synchronized 与 Lock的异同?
* 相同:二者都可以解决线程安全问题
* 不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
* Lock需要手动的启动同步(lock(),同时结束同步也需要手动的实现(unlock())
使用的优先顺序:
* Lock ---> 同步代码块(已经进入了方法体,分配了相应资源 ) ---> 同步方法(在方法体之外)
3.利弊
同步的方式,解决了线程的安全问题。---好处
操作同步代码时,只能一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。
7、多线程并发下的数据安全问题
1.条件:1.1:多线程并发
1.2:存在共享数据
1.3:共享数据有修改的行为
2.说明:2.1在多线程下有可能存在多个线程同时修改共享数据,导致数据异常
2.2这是就不能让线程异步(并发)引出线程同步(排队)
2.3缺点:效率降低
8、java中有三大变量
1.实例变量:在堆中
2.静态变量:在方法区中
3.局部变量:在栈中
*PS:4.1以上三大变量,局部变量是没有线程安全问题的,因为数据不共享啊
一个线程一个栈,局部变量在栈中
(另外常量也没有线程问题虽然在常量池共享,但是不能被改变)
4.2堆与方法区都是多线程共享的,蓑衣存在线程问题(堆与方法区只有一个)
9、开发中线程安全问题的解决
1.第一种方案:尽量使用局部变量代替实例变量与静态变量
2.第二种方案:如果必须是实例变量,那么可以考虑创建多个对象,一个对象对应一个线程
让线程对象不共享,就没有共享数据了
3.第三种方案:不能使用局部变量,也不能创建多个对象,这时只能使用synchronized同步线程
10、死锁
Java死锁是指两个或多个线程在互相等待对方释放资源,导致所有线程都无法继续执行的状态。
常见的解决方法是:避免循环等待、避免拥有锁的线程在等待资源时被阻塞、增加重试机制等。
11、守护线程
1.java语言中线程分为两大类
:用户线程(main法方主线程)
:守护线程(代表垃圾回收线程)
2.守护线程的特点:
一般都是一个死循环,随着用户线程共存,用户线程消亡,守护也消亡
3.方法
:void setDaemon(boolean on )
(on 表示把线程设置为守护线程)
*、关于合理结束一个进程的执行(常用)
1..stop()方法(已经过时)
因为这种方式直接将线程杀死,线程没有保存,容易丢失数据
2.,线程正常执行完毕,正常结束。
也就是让run方法执行完毕,该线程就会正常结束。
3..
*/