Course(进程)
什么是进程?
进程就是在某种程度上相互隔离的、独立运行的程序
什么是多进程?
就是让系统看起来像是同时运行多个程序
注意:对于一个CPU而言,在某个时间点上只能执行一个程序,多进程只是在多个程序间进行不断跳跃,执行
Thread(线程)
什么是线程?
线程是指程序中的某个任务
什么是多线程?
多线程是指在能够同时运行多个线程的程序就叫做多线程
多进程和多线程的区别?
对于进程来说,每个进程都有自己的一组完整的变量,而线程则是共享相同数据的
线程的创建方式
方式一:通过继承Thread类,创建线程
方式二:通过实现Runnable接口,创建线程
两种创建方式的比较:
(1)使用Runnable
使用Runnable接口,可以将CPU、代码和数据分开,形成清晰的模型
还可以从其它地方继承类
保持程序风格的一致性
(2)直接继承Thread类
不能再从其它类继承
编写简单,可以直接操纵线程,无需使用Thread.currentThread().
Thread类
特性:实现Runnable接口,程序中的某个线程
方法:
构造方法:
Thread():创建一个默认名称的Thread
Thread(String name):创建一个指定名称的Thread
Thread(Runnable target)
Thread(Runnable target,String name)
Thread(ThreadGroup group,String name)
Thread(ThreadGroup group,Runnable target)
Thread(ThreadGroup group,Runnable target,String name):创建一个指定名称,指定调用run方法的target和该线程属于group的Thread对象
Thread(ThreadGroup group,Runnable target,String name,long stackSize)
普通方法:
static Thread currentThread():返回对当前正在运行的线程的引用
int getPriority()[praɪ'ɒrɪtɪ]:返回当前线程的优先级
void setPriority(int newPriority()):设置线程的优先级
static void sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
void start():使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
static void yield():暂停当前线程,并且执行其它线程
long getId():返回该线程的标示符
String getName():返回该线程的名称
void setName(String name):设置线程的名称
Thread.State getState():返回该线程的状态
ThreadGroup getThreadGroup():返回该线程所处的线程组
void interrupt():中断线程
bolean interrupted():判断该线程是否已经终止
boolean isAlive():判断该线程是否还活着
boolean isDaemon()['diːmən]:判断该线程是否为守护线程
void setDaemon():将该线程标记为守护线程活着是用户线程
void join():等待线程的结束
void join(long millis):在指定时间内等待线程的结束
void run():如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回
后台线程(守护线程)
什么是后台线程?
在后台运行的,为其它的线程提供服务,这种线程被称为后台线程(Daemon Thread)
作用:一般用于任务结束时的善后处理
特点:优先级一般比其它线程低
注意:
和后台线程相比较,一般线程称为“用户线程”
如果一个应用中只有后台线程在运行,JVM将退出该应用程序
Thread State(线程状态)
五种状态:New(新建)、Runnable(可运行)、Running(运行)、Blocked(阻塞)、Dead(死亡)
关系图:
New --> 通过调用start() --> Runnable <--> 线程调度 --> Running -->run()完毕 -->Dead
Running --> 发生某些阻塞时间 --> Blocked --> 通过解除阻塞 --> Runnable
线程详解:
(1)当使用了new来新建一个线程时,它处于New状态,这个时候,线程并未进行任何操作
(2)然后,调用线程start()方法,来向线程调度程序(通常是JVM或操作系统)注册一个线程,这个时候,这个线程准备就绪,等待CPU时间
(3)线程调度程序根据调度策略来调度不同的线程,调用线程的run()方法给已经注册的各个线程以执行的机会,被调度的线程
进入Running状态。当线程的run()方法执行完毕,线程将被抛弃,进入Dead状态,此时,不能使用restart()方法重新开始已死掉的线程,但
可以使用处于死亡状态的线程方法
(4)如果线程在Running()状态中因为IO阻塞、等待键盘录入,调用了线程的sleep()方法,调用了对象的wait()方法等操作,则线程将进入Blocked(阻塞)状态,
直到这些阻塞原因被解除,解除后将进入Runnable状态,重新等待线程调度程序调度,注意的是,被阻塞的线程,解阻塞后不会直接进入Running状态
线程什么时候结束?
(1)当线程的run()方法执行到结尾之后
(2)当线程抛出一个未捕获的异常或者错误之后
(3)当另一个线程调用一个Deprecated的stop()方法(不建议使用)
线程的控制
1)中断线程
static void sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
static void yield():暂停当前线程,并且执行其它线程
两者的区别:
相同点:sleep()和yield()在调用后都将会让出CPU的控制权
不同点:sleep()在指定的睡眠时间内,一定不会获得运行的机会,直到它睡眠的时间结束
而yield()方法在让出控制权后。还有可能马上被系统的调度机制选中来运行,这样就不能达到让出控制权的目的
注意:当一个线程在执行很长的循环时,应该适当的调用sleep()和yield()方法,确保其它线程可以得到运行的机会,否则就是一个利己线程。
2)线程Join方法的使用
public class JoinThreadTest {
static int[] a = new int[50];
public static void main(String args[]){
JoinThread jt = new JoinThread();
Thread thread = new Thread(jt);
thread.setName("chenzui");
thread.start();
try{
thread.join();
}catch(InterruptedException e){
e.printStackTrace();
}
for (int i = 0; i < a.length; i++) {
System.out.println(thread.getName()+":"+a[i]);
}
}
static class JoinThread implements Runnable{
public void run(){
for (int i = 0; i < 50; i++) {
System.out.println("i:"+(i-50));
a[i] = i-50;
}
}
}
}
Multi-Thread(多线程)
“多线程”描述的是,在主线程中有多个线程在运行,
一般指:
多个线程来自同一个Runnable实例
多个线程使用同样的数据和代码
多线程中的共享数据问题
原因:多线程中,数据共享,因为不是同步的因素,会导致,当某个线程在对某个数据进行操作的过程中,还没操作完成,
就被另一个线程打断,使得第一个线程修改的数据,没有被第二个线程获取到,结果是两个线程的结果不一致的问题
解决方法:使用synchronized(线程锁)
synchronized(线程锁、互斥锁、对象锁):线程锁锁定的是当前调用的对象
synchronized的使用方式:
(1)放在方法前面,这样调用该方法的线程将获得当前对象上的锁
(2)放在代码块前面,用于修饰对象引用,两种方式:
用于修饰当前对象的引用————synchronized(this){.....}
或synchronized{.......}代码块中的代码将获得当前对象引用的锁
用于修饰指定的对象————synchronized(Object obj){}:代码块中的代码将获得指定
对象引用的锁
注意:synchronized也可以用来修饰类,被修饰的类表示里面的方法都是synchronized的
一个类中只有某个或多个方法被synchronized修饰,当某个线程调用该类中被锁定的方法时,其它
线程可以调用该类中的无锁方法
线程归还锁的时机:
当线程执行到synchronized块结束的时候,释放对象锁
当在synchronized中遇到return、break或Exception时,将自动释放对象锁
当一个线程调用wait()方法时,它放弃拥有的对象锁并进入等待队列
使用synchronized遇到死锁的原理:
一个程序中,锁定了两个或两个对象,当某个线程获取了第一个对象上的锁,在等待第二个对象的锁时,此时
有另一线程取得第二个对象上的锁,同时也在等待第一个对象上的锁时,就出现了大家都在等待的现象,
这就是死锁。
解决死锁的方法:保证在获取多个锁的同时,每个线程都是以相同的顺序获取锁的。
线程间的通信:
原理:使用Object我对象中的wait()、notify()、notifyAll()方法,通过某个条件将调用者释放锁
并且等待,其它线程获得锁运行,通过调用notify或notifyAll唤醒等待在该对象的上的所有线程,从而实现
线程间的通信原理
wait:是使调用某个对象的线程,在调用的对象上进行等待
notify和notifyAll:是指唤醒在调用的对象上等待的线程,notify是唤醒一个(具体唤醒哪个,由系统决定),
notifyAll唤醒的是所有线程
wait和sleep的区别:wait会使线程释放占用的对象锁,sleep不会使线程释放所占有的锁
旋锁:
原理:在线程通信间,当使用判断条件控制是否等待时,需要使用while循环进行判断,叫做“旋锁”
目的:是为了解决两个或两个以上线程同时苏醒后,后面的线程能够再次判断等待条件,保证程序的安全性 |