1、什么是进程?什么是线程?
进程(Process):进程是指在计算机中运行的一个程序。
线程(Thread):线程是进程内的执行单元,也被称为轻量级进程。一个进程可以包含很多个线程,线程是并发执行的,多个线程可以同时执行不同的任务。
2、JAVA中如何实现多线程?
(1)第一种方式:继承Thread类,重写Run方法
①创建一个类来继承Thread类
//第一种方式:继承Thread类
// ①先继承Thread类
public class MyThread extends Thread {
public MyThread(String name){
super(name);
}
//②重写run方法
@Override
public void run() {
for(int i=0;i<20;i++){
System.out.println(getName()+"================="+i);
}
}
}
②创建一个测试类
public class Test {
public static void main(String[] args){
//创建一个线程对象
MyThread t1=new MyThread("线程A");
//开启线程:
t1.start();
for (int i = 0; i <20 ; i++) {
System.out.println("主线程=============="+i);
}
}
}
运行结果(部分):
线程A=================0
线程A=================1
线程A=================2
线程A=================3
线程A=================4
主线程==============0
线程A=================5
线程A=================6
主线程==============1
线程A=================7
主线程==============2
线程A=================8
主线程==============3
主线程==============4
主线程==============5
主线程==============6
主线程==============7
线程A=================9
主线程==============8
线程A=================10
主线程==============9
线程A=================11
主线程==============10
这个结果说明了, 程序启动之后运行main时候,java虚拟机启动一个进程,主线程在main()调用时候被创建。随着调用Thread对象的start方法,又一个线程启动,整个应用就在多线程下运行了。
(2)实现Runnable接口,重写Run方法
①通过实现Runnable接口来创建线程类
//通过实现Runnable接口来创建线程任务类
public class MyRunnable implements Runnable {
@Override
public void run() {
for(int i=0;i<20;i++){
//Thread.currentThread().getName()获取运行时的线程名
System.out.println(Thread.currentThread().getName()+"========================="+i);
}
}
}
②创建一个测试类
public class Test {
public static void main(String[] args) {
//创建线程任务类对象
MyRunnable my=new MyRunnable();
//创建线程对象.Runnable
Thread t1=new Thread(my,"线程A");
//开启线程
t1.start();
for (int i = 0; i <20 ; i++) {
System.out.println("主线程=============="+i);
}
}
}
运行结果:
主线程==============0
主线程==============1
线程A=========================0
主线程==============2
线程A=========================1
主线程==============3
主线程==============4
主线程==============5
主线程==============6
主线程==============7
主线程==============8
主线程==============9
主线程==============10
对于这两种方法,在使用时推荐使用第二种方式,因为继承只能够单继承,在开发中的扩展性比较差,接口的话可以多实现,扩展性较第一种方式强。
3、Thread类中常用的方法
①start():启动线程,使线程进入可执行状态并开始执行run()方法中的代码。
②run():定义线程的执行逻辑,需要在子类中重写该方法。线程启动后会自动调用run()方法来执行线程的任务。
③sleep(long millis):使当前线程休眠指定的时间(以毫秒为单位)。调用该方法会暂停线程的执行,让出CPU资源给其他线程。
④join():让一个线程等待另一个线程完成后再继续执行。调用该方法会阻塞当前线程,直到目标线程执行完成。
⑤interrupt():中断线程,给线程发送中断信号。该方法会设置线程的中断状态,并在线程中对应的位置抛出InterruptedException异常。
⑥isInterrupted():检查线程是否被中断。该方法返回一个boolean值,表示线程的中断状态。
⑦getId():获取线程的唯一标识符(ID)。每个线程都有一个独一无二的ID,用于区分不同的线程。
⑧getName():获取线程的名称。线程可以通过构造方法或setName()方法设置名称。
4、线程安全问题
线程存在线程安全问题,线程安全问题是由于多个线程访问共享资源而产生的问题。
解决线程安全问题的办法:
使用锁,
5、线程的死锁问题
(1)什么是死锁?
当第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。
死锁发生时需要满足的四个条件:
①互斥条件:资源一次只能被一个线程占用
②请求与保持条件:线程在持有资源的同时又请求其他线程所持有的资源
③不可剥夺条件:已经分配的资源不能被其他线程强制性地抢占
④循环等待条件:多个线程之间形成一种循环等待资源的关系
(2)死锁的案例
两个人一个男孩有画笔,一个女孩有画板,但是互相都不愿意给对方东西让对方画画。
public class LockObject {
//一个锁的类
//画笔对象
public static Object brush=new Object();
//画板对象
public static Object drawingBoard=new Object();
}
//创建一个男孩类
public class BoyRunnable implements Runnable{
@Override
public void run() {
synchronized (LockObject.brush){
System.out.println("男孩获取到一只画笔");
synchronized (LockObject.drawingBoard){
System.out.println("男孩获得一个画板");
System.out.println("男孩可以进行画画了");
}
}
}
}
//创建一个女孩类
public class GirlRunnable implements Runnable {
@Override
public void run() {
synchronized (LockObject.drawingBoard){
System.out.println("女孩获得了一个画板");
synchronized (LockObject.brush){
System.out.println("女孩获得了一只画笔");
System.out.println("女孩开始画画了");
}
}
}
}
//Test测试类
public class Test {
public static void main(String[] args) {
//创建男孩对象
BoyRunnable boy=new BoyRunnable();
Thread t1=new Thread(boy);
//创建女孩对象
GirlRunnable girl=new GirlRunnable();
Thread t2=new Thread(girl);
//开启线程
t1.start();
t2.start();
}
}
运行结果:
女孩获得了一个画板
男孩获取到一只画笔
(3)如何解决死锁问题
①尽量避免出现锁的嵌套
②设置超时时间:在获取锁资源时设置超时时间,如果在指定时间内无法获得所需的锁资源,则放弃当前的锁请求,释放已经持有的锁,并进行适当的处理,避免死锁的持续发生
③及时释放锁资源:在使用完锁资源后及时释放,避免长时间持有锁,减少死锁的可能性
6、线程通信
wait和sleep的区别:
①wait需要使用notify或者notifyAll唤醒,而sleep到时候自动唤醒
②wait来自于Object类中,sleep来自Thread类中
③wait会释放锁资源,sleep不会释放锁资源
④wait必须放在同步代码中,而sleep可以放在任意位置