什么是进程?什么是线程?
- 进程是一个应用程序。所谓进程就是指程序在执行中的时候内存里面所产生的一个与之对应的活动实体,是一个执行中的程序,是系统对该程序进行资源分配和调度的一个独立单位。(只有执行的程序才有对应的进程!)
- 线程是一个进程中的执行成绩/执行单元。所谓的线程就是一些顺序性的指令,是进程中的一个执行单元,是CPU调度和分派的最小单位,它是比进程更小的独立运行的基本单位,也可以称它为轻量级的进程。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),一个线程可以创建和撤销另一个线程;进程由多个线程组成,不同的线程完成不同的工作,这些线程之间交替执行,称为多线程
进程A和进程B的内存独立不共享
在java语言中:线程A和线程B,堆内存和方法区内存共享。但是栈内存独立,一个线程一个栈
- 同步和异步
- 所谓同步(并发)是指多个线程按照一定顺序逐个进行执行;
- 所谓异步(并行)是指多个线程一起不按照顺序进行执行;
线程生命周期
多线程的状态管理:
- New状态:当多线程对象被创建出来的时候,还没有调用start方法之前,属于这种状态;
- Ready状态:当多线程对象调用了start方法,但是此时处于多个线程在争夺CPU使用权,属于这种状态;
- Runnable状态:当某个都想吃抢夺到了CPU的使用权,并且开始运行它的业务逻辑的时候,属于这种状态;
- Block状态:当多线程由于外部的原因导致它无法机械执行业务逻辑的时候,属于这种状态
a、锁阻塞
b、指定时长的等待
c、不指定时长的等待 - Treminated状态:当多线程业务逻辑执行完毕,属于这种状态,一旦进入这种状态,线程对象就无法再调用了,否则会报错!
多线程一旦由于操作系统本身的原因,还是外部的原因,一旦它被阻塞了并且恢复运行之后,都是先回到Ready状态,等待抢夺CPU使用权从而进入到RUnnable状态
从JDK1.5开始,Ready和Running状态合并,Block状态开始进行细分。
JAVA语言中,实现线程有两种方式:
- 编写一个类,直接继承java.lang.Thread,重写run方法
public class ThreadTest01 {
public static void main(String[] args) {
//这里是main方法,这里的代码属于主线程,在主栈中运行。
//新建一个分支线程对象
MyThread myThread = new MyThread();
/*
启动线程
start()方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了
这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来,start()方法就结束了。线程就启动成功了。
启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)。
run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。
*/
myThread.start();
//该代码还是运行在主线程中
for (int i = 0;i<1000;i++){
System.out.println("主线程---->" + i);
}
}
}
/*
实现线程的第一种方式:
编写一个类,直接继承java.lang.Thread,重写run方法
*/
class MyThread extends Thread{
@Override
public void run() {
//编写程序,这段程序运行在分支线程中
for (int i = 0;i<1000;i++){
System.out.println("分支线程---->" + i);
}
}
}
- 编写一个类,实现java.lang.Runnable接口,实现run方法。
/*
实现线程的第二种方法,编写一个类实现java.lang.Runnable接口。
*/
public class ThreadTest02 {
public static void main(String[] args) {
//创建线程对象,使用匿名内部类方式
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0;i<100;i++){
//currentThread就是当前线程对象
Thread currentThread = Thread.currentThread();
System.out.println(currentThread.getName() +"--->" + i);
}
}
});
/*
多线程常用方法
设置线程名字
*/
t.setName("t1");
//获取线程名字
String str = t.getName();
//System.out.println(str);
//启动线程
t.start();
for (int i = 0;i<100;i++){
//currentThread就是当前线程对象
Thread currentThread = Thread.currentThread();
System.out.println(currentThread.getName() +"---->" + i);
}
}
}
关于线程的sleep方法
public class ThreadTest04 {
public static void main(String[] args) {
/*
关于线程的sleep方法:
1、静态方法
2、参数是毫秒
3、作用:让当前线程进入休眠,进入“阻塞状态”放弃占优CPU时间片,让给其他线程使用
4、Thread.sleep()方法,可以做到间隔特定的时间,去执行一段特定的代码,每隔多久执行一次。
*/
//让当前线程进入休眠,睡眠5秒
//当前线程是主线程
/*try {
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//5秒之后执行这里的代码
System.out.println("hello world");*/
for (int i = 0;i<10;i++){
System.out.println(Thread.currentThread().getName() + "--->" + i);
//睡眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
关于Thread.sleep()方法的面试题
/*
关于Thread.sleep()方法的面试题
*/
public class ThreadTest05 {
public static void main(String[] args) {
//创建线程对象
Thread t = new MyThread1();
t.setName("t");
//启动线程
t.start();
//调用sleep方法
try {
//问题:这行代码会让线程t进入休眠状态吗?
t.sleep(1000 * 5); //在执行的时候还是会转换成:Thread.sleep(1000*5);
//这行代码的作用是:让当前线程进入休眠,也就是说main线程进入休眠。
//这样代码出现在main方法中,main线程睡眠
} catch (InterruptedException e) {
e.printStackTrace();
}
//该语句5秒后才会执行
System.out.println("hello world");
}
}
class MyThread1 extends Thread{
@Override
public void run() {
for (int i = 0;i<1000;i++){
System.out.println(Thread.currentThread().getName() + "--->" + i);
}
}
}
唤醒正在睡眠的线程
public class ThreadTest06 {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.setName("t");
//启动线程
t.start();
//希望5秒钟后,t线程唤醒
try {
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//中断t线程的睡眠(这种中断睡眠的方式依靠了java的异常处理机制)
t.interrupt();
}
}
class MyRunnable implements Runnable{
//run()当中的异常不能throws,只能try catch
//因为run()方法在父类中没有抛出任何异常,子类不能比父类抛出更多的异常
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "-----> begin");
//睡眠一小时
try {
Thread.sleep(1000 * 60 * 60);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "----> end");
}
}
合理的终止线程的执行
public class ThreadTest08 {
public static void main(String[] args) {
MyRunnable4 r = new MyRunnable4();
Thread t = new Thread(r);
t.setName("t");
//启动线程
t.start();
//模拟5秒后终止线程的执行
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//终止线程
//想要什么时候终止t线程的执行,那么把标记修成false,就结束了
r.run = false;
}
}
class MyRunnable4 implements Runnable{
//定义一个布尔类型的值标记
boolean run = true;
@Override
public void run() {
for (int i = 0;i<10;i++){
if (run){
System.out.println(Thread.currentThread().getName() + "--->" + i);
//睡眠一秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
//终止当前线程执行
return;
}
}
}
}
线程创建的面试题:
多线程有集中创建方式
- 两种,继承了Thread和实现Runnable,因为在JDK1.6之前,就这两种;
- 四种,除了上面两种之外,从JDK1.6开始,就新增了两个,一个是通过实现Callable接口来创建线程,还有一个是使用线程池来创建线程;
- 一种,不管通过那种方式来创建,反正都要使用Thread类,所以只有通过Thread类才能构建线程对象!