1. 什么是线程?
线程是进程的执行单元。
2. 什么是进程?
进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,例:一个简单的项目相当于一个进程。
3. 线程和进程的区别和联系?
线程是进程的执行路径,一个进程至少有一个线程,进程中多个线程共享进程资源。操作系统是把资源分配给进程的,但是CPU资源比较特殊是被分配到线程,因为真正占用CPU使用的是线程,也可以说线程是CPU分配的基本单位。
4. 线程的资源分配
一个进程中有多个线程,多个线程共享进程的堆和方法区资源,但是每个线程有自己的程序计数器和栈资源。
程序计数器是一块内存区域,用来记录当前线程要执行的命令地址,CPU是通过时间片轮转方式让线程轮询占用。程序计数器就是记录线程上次轮询执行地址,以便再次轮询到可以继续执行,所以要设置为线程私有化,如果执行的是native方法,程序计数器记录的是undefined地址。
每个线程有自己的栈资源,用来存储局部变量,这些局部变量为线程私有,别的线程访问不了。
5. 线程的创建。
线程的创建有三种方式。
1. 继承Thread类(Thread类也实现了Runnable接口):
public class MyThread extend Thread{
@override
public void run(){
system.out.println("用extend创建线程!");
}
}
//创建线程
MyThread myThread = new MyThread();
//启动线程
myThread.start();
调用start()方法后线程没有立即执行,而是进入就绪状态,等待CPU的调度。
2.实现Runnable接口:
实现Runnable()接口,重写run()方法。
new Thread(task).start();
3. 实现Callable接口(可以得到返回值get()方法):
public class CallerTask implements Callable {
@Override
public Object call() throws Exception {
System.out.println("Callable创建线程并返回执行结果");
return "Callable";
}
public static void main(String[] args) {
//创建异步任务
FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
//启动线程
new Thread(futureTask).start();
try {
//等待任务异步完成并返回 结构
System.out.println(futureTask.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
小结:使用继承方法方便传参,可以在子类里面添加成员变量,通过set方式设置参数或者通过构造函数进行传递;使用Runnable方式,只能使用主线程中用final声明的变量,前两者都不能回去返回参数。
6.线程基本状态
-
新建状态(NEW)
当程序使用new关键字创建一个线程之后,线程处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量。
-
就绪状态(RUNNABLE)
当线程对象调用start()方法的时候,线程处于就绪状态。JVM会为其创建方法调用栈和程序计数器,等待CPU调度运行。
-
运行状态(RUNNING)
如果就绪状态的线程获得CPU,开始执行run()方法,则该线程处于运行状态。
-
阻塞状态(BLOCKED)
阻塞状态是指线程原因放弃了CPU使用权,暂时停止运行。直到线程再次进入RUNNABLE状态,才有机会再次获得CPU调度,转到运行状态。阻塞的情况分为三种:
1.等待阻塞(o.wait->等待队列)
运行状态的线程执行o.wait()方法,JVM会将该线程放入等待队列中,处于就绪状态。
2.同步阻塞(lock->锁池)
运行中的线程尝试获取同步锁,但是该锁已经被其他线程占有,此时该线程阻塞,JVM把线程放入锁 池中(lock pool)。
3.其他阻塞(sleep/join)
运行的线程调用Thread.sleep(long ms)或者t.join()方法,或发出来I/O请求时,JVM会把该线程置为阻塞状态,当sleep()状态超时,join()等待线程终止或者超时,或者I/O处理完毕,线程重新转为就绪状态。
-
线程死亡(DEAD)
线程会以下面三种方式结束,结束后就是死亡状态。
1.正常结束(run或call方法执行完毕,线程正常结束)
2.异常结束(线程抛出未捕获的Exception或Error)
3.调用stop(直接调用该线程的stop()方法来结束该线程,可能造成死锁,不推荐使用)

7.线程的基本方法
线程相关的基本方法有wait、notify、notifyAll、sleep、join、yield等。
-
线程等待(wait)
调用该方法的线程进入WAITING状态,只有等待另外的线程通知或者被中断才可以返回,调用wait()方法的时候会释放对象的锁,一般用在同步方法或代码块中。
-
线程休眠(sleep)
sleep()导致线程休眠,与wait()不同的是,sleep是Thread静态方法,同时不会释放锁,并且线程进入TIMED-WAITING状态。
-
线程让步(yield)
yield()会使当前线程让出CPU执行时间片,与其他线程一起重新竞争CPU时间片。一般情况下优先级高的线程有更大的可能性成功得到CPU时间片,但不是绝对的,有的操作系统对优先级不敏感。
-
线程中断(interrupt)
中断一个线程本意是给这个线程一个通知信号,影响该线程内部的中断标识位。这个线程的状态并不会因此改变(如阻塞、终止等)。
1.调用interrupt()方法,并不会改变该线程的状态,仅仅改变内部维护的中断标识。
2.若调用sleep()使线程进入TIMED-WAITING状态,这时调用interrupt()方法会抛出interruptedException异常,从而使线程提前结束TIMED-WAITING状态。
3.许多声明interruptedException异常的方法,抛出异常前会清除中断标识。
4.中断状态是线程固有的标识位,可以通过此标识安全的终止线程。比如,想要终止一个线程thread,先调用thread.interrupt()方法,然后在run()方法中根据thread.isInterrupted()的值来终止线程。
-
等待其他线程终止(join)
在当前线程中调用另一个线程的join方法,会使当前线程阻塞,直到另外的线程结束,当前线程由阻塞转为就绪,重新竞争CPU。比如,当主线程需要等待子线程结束后再结束,可以用join。
-
线程唤醒(notify)
notify()方法属于Object类,唤醒在此对象监视器上的单个线程,如果所有的线程都在此对象上等待,则随机唤醒其中一个线程。notify()的使用建立在wait()前提下,wait()会使线程在对象监视器等待。
-
其他方法:
- isAlive():判断线程是否存活;
- activeCount():程序中活跃的线程数;
- enumerate():枚举程序中的线程;
- currentThread():得到当前线程;
- isDaemon():判断线程是否为守护线程;
- setDaemon():设置为守护线程(守护线程不会随JVM的退出而退出,例如GC线程);
- setName():设置线程名称;
- setPriority():设置线程优先级;
- getPriority():获得线程优先级;
8.start()和run()的区别:
1.start()方法来启动线程,真正实现类多线程运行,这时无需等待run()方法体代码执行完毕,可以直接执行后续代码。
2.通过Thread类的start()方法来启动线程,此时线程处于就绪状态,并没有运行。
3.run()方法为线程体,包含要执行的线程内容,线程进入运行状态,开始执行run()里面的代码,run()方法结束,此线程终止,
总结:直接调用run()方法就是普通的方法调用,start()是开启线程执行run()方法。