什么是进程?什么是线程?它们之间有什么关系?
进程是一个应用程序。进程是系统进行资源分配和调度的一个独立单位,最小的资源管理单位。
线程是一个进程中的执行单元/执行场景。线程是进程的一个实体,是 CPU 调度和分派的基本单位,它是比进程更小的能独立运行的基本单位,最小的 CPU 执行单元。
一个进程可以启动多个线程。
进程与进程之间内存独立不共享。
线程与线程之间可能就会存在资源共享,在java中是堆内存和方法区内存共享,栈内存不共享,栈内存独立,一个线程一个栈。假设启动10个线程,会有10个栈空间,每个栈和每个栈之间互不干扰,各自执行各的,这就是多线程并发。
使用了多线程之后,mian方法结束,是不是有可能程序不会结束。mian方法结束只是主线程结束了,主栈空了,其它的栈(线程)可能还在压栈弾栈。
分析一个问题,对于一个单核的CPU来说,真的可以做到真正的多线程并发吗?
对多核的CPU电脑,真正的多线程并发是没问题的。4核CPU表示同一个时间点上,可以真正的有4个进程并发执行。
单核CPU表示只有一个大脑,在某一个时间点上只能处理一件事情,不能做到真正的多线程并发,但是CPU处理速度极快,通过时间片轮转的方式多个线程频繁切换执行,可以给人一种多线程并发的感觉。
java实现多线程的三种方式
-
第一种,编写一个类直接继承Thread类,重写run()方法。
-
第二种,编写一个类实现Runnable接口,重写run()方法。
-
第三种,实现Callable接口,重写call()方法。
(这种方式可以获取线程的返回值,call方法可以抛异常;上面两种方式线程不可以有返回值,run方法不能抛异常;)
启动线程
如果不用start()方法,直接调用run()方法,会怎样?
线程不会启动,不会分配新的分支栈空间。这种方式就相当于单线程,只是一个普通方法调用。
线程的生命周期
sleep()
- 关于sleep()方法的一个面试题: 判断new的这个线程会休眠5秒吗?不会。
因为sleep方法是Thread类的一个静态方法,跟谁调没关系(就是不用多态方式,用myThread3 t = new myThread3();去调,是同样的结果),这个方法当前出现在哪就是让当前线程睡眠。
- **如何中断sleep睡眠?这种方式是使用了线程的异常处理机制,执行interrupt()**时,会让run方法报出异常,catch从而捕获异常,就此try/catch结束,程序继续往下执行。就实现了中断线程睡眠。
调用 interrupt() 方法仅仅是在当前线程中打一个停止的标记,并不是真的停止线程。
也就是说,线程中断并不会立即终止线程,而是通知目标线程,有人希望你终止。至于目标线程收到通知后会如何处理,则完全由目标线程自行决定。
-
**如何终止一个线程?**stop(), interrupt(), 设置标记位。
-
1、stop()方法,已过时不建议使用。因为这个方法存在很大的缺点:容易丢失数据。它是直接将线程杀死了,线程没有保存的数据将会丢失。不建议使用。
-
2、flag标记位终止。(推荐使用)
-
public class testThread { public static void main(String[] args) { myThread t = new myThread(); t.start(); //... t.exit = true;//修改标志位,退出线程 } } class myThread extends Thread{ public volatile boolean exit = false; @Override public void run() { /* while (!exit){ System.out.println(Thread.currentThread().getName()); }*/ //或者 if (!exit){ System.out.println(Thread.currentThread().getName()); }else { //return 就结束了,结束之前,有什么没保存的在这里保存。 return; } } }
-
线程调度
-
常见线程的调度模型有哪些
-
java中提供了哪些方法是和线程调度有关系的呢?
线程安全(重点)
- 为什么是重点?
以后在开发中,我们会很少有机会去写一个线程类,因为我们项目都是运行在服务器中的,而服务器已经将线程的定义,线程对象的创建,线程的启动等,都已经实现了。这些代码是不需要我们编写的。
最重要是:我们要知道,你编写的程序需要放到一个多线程的环境下运行,你更需要关注的是这些数据在多线程并发的环境下是否是安全的。(重点)
-
什么时候数据在多线程并发的环境下会存在安全问题呢?
三个条件:(满足这三个条件就会发生线程安全问题)
- 多线程并发
- 有共享数据
- 有对共享数据修改的行为
怎么解决这个个问题呢?(使用线程同步机制)
-
线程排队执行(不能并发)。用排队执行解决线程安全问题。这种机制被称为“线程同步机制”。
线程同步实际上就是线程排队了,不并发了,线程必须排队执行。线程排队会牺牲一部分效率,没办法,数据安全第一位!
-
异步编程模型&同步编程模型
- 异步编程模型:两个线程各自执行各的,互不干扰,其实就是多线程并发(效率较高)。异步就是并发
- 同步编程模型:线程执行的时候,一个线程执行,其他必须等待,线程之间发生了等待关系,线程排队执行。同步就是排队
-
synchronized实现线程同步
- 同步代码块:synchronized(共享对象){同步代码块}
- 在实例方法上使用synchronized修饰。表示共享对象一定是this,并且同步代码块是整个方法体。这种方式不灵活。
- 使用在静态方法上。表示类锁。只有一个,就算new100各对象,也只有一把类锁。
-
java中有三大变量:
- 实例变量:在堆中。
- 静态变量:在方法区。
- 局部变量:在栈中。
- 以上三个变量中,局部变量永远不会存在线程安全问题。因为局部变量不共享。(一个线程一个栈。)局部变量在栈中,所以局部变量永远不会共享。实例变量在堆中,堆只有一个,静态变量在方法区中,方法区只有一个。堆和方法区都是多线程共享的,所以可能存在线程安全问题。
-
死锁:
假设两个线程t1和t2分别取锁两个对象o1和o2。t1先锁o1再锁o2,t2先锁o2再锁o1。这时候,t1就锁不上o2(因为已经被t2锁了),t2就锁不上o1(因为已经被o1锁了),两个线程就会僵持在那里,造成死锁现象。
死锁代码要会写,一般面试官会要求你写,只有会写才会在以后的开发中注意,因为死锁很难调试。
public class DeadLock{ public static void mian(String[] args){ Object o1 = new Object(); Object o2 = new Object(); Thd1 t1 = new Thd1(o1,o2); Thd1 t2 = new Thd2(o1,o2); t1.start(); t2.start(); } } class Thd1 extends Thread{ Object o1; Object o2; public Thd1(Object o1,Object o2){ this.o1 = o1; this.o2 = o2; } public void run(){ synchronized(o1){ try{ Thread.sleep(1000) }catch(Exception e){ e.printStackTrace(); } synchronized(o2){ } } } } class Thd2 extends Thread{ Object o1; Object o2; public Thd2(Object o1,Object o2){ this.o1 = o1; this.o2 = o2; } public void run(){ synchronized(o2){ try{ Thread.sleep(1000) }catch(Exception e){ e.printStackTrace(); } synchronized(o1){ } } } }
这就是一个死锁程序,运行后,不会报错,也不会停止,一致这样僵持下去。因此,在开发中synchronized最好不要嵌套使用,一不小心就容易造成死锁。
-
以后我们开发中要怎么解决线程安全问题?
是一上来就选择线程同步吗?synchronized。不是。synchronized会让程序的执行效率降低,用户体验不好。系统的用户吞吐量降低。用户体验差。在不得以的情况下再选择线程同步机制。
第一种方案:尽量使用局部变量代替“实例变量和静态变量”
第二种方案:如果必须是实例变量,那么可以考虑创建多个对象,这样实例变量的内存就不共享了。(一个线程对应一个对象,100个线程对应100个对象,对象不共享,就没有数据安全问题了。)
第三种方案:如果不能使用局部变量,对象也不能创建多个,这时候就只能选择synchronized了。线程同步机制。
-
守护线程
Java中线程分为两大类:用户线程;守护线程(后台线程)其中最有代表性的就是垃圾回收线程(守护线程)。
守护线程的特点:一般守护线程是一个死循环,所有的用户线程只要结束,守护线程自动结束(用户线程没了,就没必要继续守护了)。主线程mian方法是一个用户线程。
使用很简单,只需要在线程start前调用 Daemon()方法即可。
-
定时器
定时器的作用:间隔特定时间,执行特定程序。
在开发中,每隔多久执行一段特定的程序,这种需求时很常见的。
- Callable接口,第三种线程实现方式
-
关于Object类中wait和notify方法
-
生产者和消费者模式