Java_多线程知识汇总
1).多线程:
进程:正在运行的程序,是系统进行资源分配和调用的独立单位。
每一个进程都有它自己的内存空间和系统资源。
线程:是进程中的单个顺序控制流,是一条执行路径。
一个进程如果只有一条执行路径,则称为单线程程序。
一个进程如果有多条执行路径,则称为多线程程序
* 大家注意两个词汇的区别:并行和并发。
* 前者是逻辑上同时发生,指在某一个时间内同时运行多个程序。
* 后者是物理上同时发生,指在某一个时间点同时运行多个程序。
2).jvm虚拟机的启动是单线程的还是多线程的?
多线程的。原因是垃圾回收线程也要先启动,否则很容易会出现内存溢出。 现在的垃圾回收线程加上前面的主 线程,最低启动了两个线程,所以,jvm的启动其实是多线程的。
3).run()和start()的区别?
run():仅仅是封装被线程执行的代码,直接调用是普通方法
start():首先启动了线程,然后再由jvm去调用该线程的run()方法。
分析:
MyThread my = new MyThread();
my.start();// IllegalThreadStateException:非法的线程状态异常
为什么呢?因为这个相当于是my线程被调用了两次。而不是两个线程启动。
4).线程池的好处:线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一 个对象来使用。
如何实现线程的代码呢?
A:创建一个线程池对象,控制要创建几个线程对象。
public static ExecutorService newFixedThreadPool(int nThreads)
B:这种线程池的线程可以执行:
可以执行Runnable对象或者Callable对象代表的线程
做一个类实现Runnable接口。
5).线程的2种创建方式
创建线程的第一种方式:继承Thread ,由子类复写run方法。
步骤: 1,定义类继承Thread类;
2,目的是复写run方法,将要让线程运行的代码都存储到run方法中;
3,通过创建Thread类的子类对象,创建线程 对象;
4,调用线程的start方法,开启线程,并执行run方法。
线程状态: 新建:start() ;
运行:具备执行资格,同时具备执行权;
冻结:sleep(time),wait()—notify()唤醒;线程释放了执行权,同时释放执行资格;
临时阻塞状态:线程具备cpu的执行资格,没有cpu的执行权;
消亡:stop() ;
创建线程的第二种方式:实现一个接口Runnable。
步骤: 1,定义类实现Runnable接口。
2,覆盖接口中的run方法(用于封装线程要运行的代码)。
3,通过Thread类创建线程对象;
4,将实现了Runnable接口的子类对象作为实际参数传递给Thread类中的构造函数。 为什么要传递呢?因为 要让线程 对象明确要运行的run方法所属的对象。
5,调用Thread对象的start方法。开启线程,并运行Runnable接口子类中的run方法。
/*Ticket t = new Ticket();
直接创建Ticket对象,并不是创建线程对象。
因为创建对象只能通过new Thread类,或者new Thread类的子类才可以。
所以最终想要创建线程。既然没有了Thread类的子类,就只能用Thread类。 */
Thread t1 = new Thread(t); //创建线程。
只要将t作为Thread类的构造函数的实际参数传入即可完成线程对象和t之间的关联
为什么要将t传给Thread类的构造函数呢?其实就是为了明确线程要运行的代码run方法。
6).多线程安全问题的原因:
通过图解:发现一个线程在执行多条语句时,并运算同一个数据时,在执行过程中,其他线程参与进来,并操 作了这个数据。导致到了错误数据的产生。
涉及到两个因素:
1,多个线程在操作共享数据。
2,有多条语句对共享数据进行运算。
原因:这多条语句,在某一个时刻被一个线程执行时,还没有执行完,就被其他线程执行了。
解决安全问题的原理:
只要将操作共享数据的语句在某一时段让一个线程执行完,在执行过程中,其他线程不能进来执行就可以解决 这个问题。 如何进行多句操作共享数据代码的封装呢?
java中提供了一个解决方式:就是同步代码块。
格式: synchronized(对象) { // 任意对象都可以。这个对象就是锁。
需要被同步的代码;
}
7). wait和sleep区别: 分析这两个方法:从执行权和锁上来分析:
wait:可以指定时间也可以不指定时间。不指定时间,只能由对应的notify或者notifyAll来唤醒。
sleep:必须指定时间,时间到自动从冻结状态转成运行状态(临时阻塞状态)。
wait:线程会释放执行权,而且线程会释放锁。
Sleep:线程会释放执行权,但不是不释放锁。
8).什么时候产生死锁,该怎么办?
一般造成死锁必须同时满足如下4个条件:
1,互斥条件:线程使用的资源必须至少有一个是不能共享的;
2,请求与保持条件:至少有一个线程必须持有一个资源并且正在等待获取一个当前被其它线程持有的资源;
3,非剥夺条件:分配资源不能从相应的线程中被强制剥夺;
4,循环等待条件:第一个线程等待其它线程,后者又在等待第一个线程。
因为要产生死锁,这4个条件必须同时满足,所以要防止死锁的话,只需要破坏其中一个条件即可。
9).线程的生命周期可以分为四个状态:
1、创建状态:
当用new操作符创建一个新的线程对象时,该线程处于创建状态。
2、可运行状态:
执行线程的start()方法将为线程分配必须的系统资源,安排其运行,并调用线程体-run()方法,这样就使得该线 程处于可运行状态(Runnable)。
3.不可运行状态:
当发生下列事件时,处于运行状态的线程会转入到不可运行状态:
调用了sleep()方法;
线程调用wait()方法等待特定条件满足;
线程输入/输出阻塞。
返回可运行状态:
处于睡眠状态的线程在指定时间过去后;
如果线程在等待某一条件,另一个对象必须通过notify()或notifyAll()方法通知等待线程条件的改变;
如果线程是因为输入输出阻塞,等待输入输出完成。
4.消亡状态
当线程的run()方法执行结束后,该线程自然消亡。
10).线程的优先级:
1.线程的优先级及设置
线程的优先级是为了在多线程环境中便于系统对线程的调度,优先级高的线程将优先执行。
一个线程的优先级设置遵从以下原则:
线程创建时,子继承符的优先级。
线程创建后,可通过调用setPriority()方法改变优先级。
线程的优先级是1-10之间的正整数。
1-MIN_PRIORITY
10-MAX_PRIORITY
5-NORM_PRIORITY
如果什么都没有设置,默认值是5
但是不能依靠线程的优先级来决定线程的执行顺序。