要解释线程,就必须明白什么是进程。
进程是指运行中的应用程序,每个进程都有自己独立的地址空间(内存空间),比如用户点击桌面的IE浏览器,就启动了一个进程,操作系统就会为该进程分配独立的地址空间。当用户再次点击左面的IE浏览器,又启动了一个进程,操作系统将为新的进程分配新的独立的地址空间。目前操作系统都支持多进程。
要点:用户每启动一个进程,操作系统就会为该进程分配一个独立的内存空间。
线程(线程是进程内的执行单元):
是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。线程有就绪、阻塞和运行三种基本状态。
命令:
列出进程:jps
- 实现Runnable接口和继承Thread可以得到一个线程类,new一个实例出来,线程就进入了初始状态
- 可运行状态只是说你资格运行,调度程序没有挑选到你,你就永远是可运行状态。
- 调用线程的start()方法,此线程进入可运行状态。
- 当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入可运行状态。
- 当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入可运行状态。
- 锁池里的线程拿到对象锁后,进入可运行状态。
- 线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。
- 当线程的run()方法完成时,或者主线程的main()方法完成时,我们就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。
- 在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
- 当前线程T调用Thread.sleep()方法,当前线程进入阻塞状态。
- 运行在当前线程里的其它线程t2调用join()方法,当前线程进入阻塞状态。
- 等待用户输入的时候,当前线程进入阻塞状态。
- 调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在synchronized(obj) 代码段内。
- 与等待队列相关的步骤和图
- 线程1获取对象A的锁,正在使用对象A。
- 线程1调用对象A的wait()方法。
- 线程1释放对象A的锁,并马上进入等待队列。
- 锁池里面的对象争抢对象A的锁。
- 线程5获得对象A的锁,进入synchronized块,使用对象A。
- 线程5调用对象A的notifyAll()方法,唤醒所有线程,所有线程进入锁池。||||| 线程5调用对象A的notify()方法,唤醒一个线程,不知道会唤醒谁,被唤醒的那个线程进入锁池。
- notifyAll()方法所在synchronized结束,线程5释放对象A的锁。
- 锁池里面的线程争抢对象锁,但线程1什么时候能抢到就不知道了。||||| 原本锁池+第6步被唤醒的线程一起争抢对象锁。
- 当前线程想调用对象A的同步方法时,发现对象A的锁被别的线程占有,此时当前线程进入锁池状态。简言之,锁池里面放的都是想争夺对象锁的线程。
- 当一个线程1被另外一个线程2唤醒时,1线程进入锁池状态,去争夺对象锁。
- 锁池是在同步的环境下才有的概念,一个对象对应一个锁池。
1)继承Thread类创建线程
2)实现Runnable接口创建线程
通过继承Thread类来创建并启动多线程的一般步骤如下
1】d定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。
2】创建Thread子类的实例,也就是创建了线程对象
3】启动线程,即调用线程的start()方法
代码实例
public class MyThread extends Thread{//继承Thread类
public void run(){
//重写run方法
}
}
public class Main {
public static void main(String[] args){
new MyThread().start();//创建并启动线程
}
}
通过实现Runnable接口创建并启动线程一般步骤如下:
1】定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体
2】创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象
3】第三部依然是通过调用线程对象的start()方法来启动线程
代码实例:
public class MyThread2 implements Runnable {//实现Runnable接口
public void run(){
//重写run方法
}
}
public class Main {
public static void main(String[] args){
//创建并启动线程
MyThread2 myThread=new MyThread2();
Thread thread=new Thread(myThread);
thread().start();
//或者 new Thread(new MyThread2()).start();
}
}
Thread.stop() 不推荐使用,它会立即释放所有monitor(对象或锁),会导致多线程的数据的不一致性,例如写进程突然终止,读进程可能会读到刚才还没有写完的“残破”的数据。
public void Thread.interrupt() // 中断线程
public boolean Thread.isInterrupted() // 判断是否被中断
public static boolean Thread.interrupted() // 判断是否被中断,并清除当前中断状态
直接打断,并不能够把该线程中断,因为一直在循环中:
public void run(){
while(true){
Thread.yield();
}
}
t1.interrupt();
优雅的方法,如果发现有人“打招呼”,就会跳出循环:
public void run(){
while(true){
if(Thread.currentThread().isInterrputed()){
System.out.println("interrupted");
break;
}
Thread.yield();
}}
设置睡眠,如果抛出异常后会清除中断标记位 ,需要设置中断状态
public static native void sleep(long millis) throws InterruptedException
public void run(){
while(true){
if(Thread.currentThread().isInterrupted()){
System.out.println("Interruted!");
break;
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("Interruted When Sleep");
//设置中断状态,抛出异常后会清除中断标记位
Thread.currentThread().interrupt();
}
Thread.yield();
}
}
(4)挂起(suspend)和继续执行(resume)线程
– suspend()不会释放锁
– 如果加锁发生在resume()之前 ,则死锁发生