多线程
一、进程与线程
1、进程:指正在运行的程序,即一个程序进入内存运行,就变为一个进程,进程是处于运行中的程序,并且具有一定独立性
2、线程:是进程中的一个执行单元,负责当前进程中程序的运行,一个进程至少有一个线程,可以有多个线程,即一个程序运行至少有一个进程,几个进程中可以包含多个线程。
二、线程创建方式:
1、继承Thread类创建线程,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this即可获取当前线程。
package com.thread;
//通过继承Thread类实现自定义线程类
public class MyThread extends Thread {
//线程体
@Override
public void run() {
System.out.println("Hello, I am the defined thread created by extends Thread");
}
public static void main(String[] args){
//实例化自定义线程类实例
Thread thread = new MyThread();
//调用start()实例方法启动线程
thread.start();
}
优点:实现简单,只需要实例化继承类的实例,即可使用线程
缺点:java是单继承的语言,如果一个类已经继承了其他类,就无法通过继承来实现自定义线程;
2、通过Runnable接口创建线程类
package com.thread;
public class MyRunnable implements Runnable {
//线程体
@Override
public void run() {
System.out.println("Hello, I am the defined thread created by implements Runnable");
}
public static void main(String[] args){
//线程的执行目标对象
MyRunnable myRunnable = new MyRunnable();
//实际的线程对象
Thread thread = new Thread(myRunnable);
//启动线程
thread.start();
}
}
优点:扩展性好,可在此基础上继承其他类,实现其他必须功能;
支持多线程共享资源的场景,适用于多线程处理一份资源的场景
缺点:构造线程实例的过程相对繁琐
3、通过Callable和Future创建线程,必须使用Thread.currentThread()方法获取当前线程
package com.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
return "Hello, I am the defined thread created by implements Callable";
}
public static void main(String[] args){
//线程执行目标
MyCallable myCallable = new MyCallable();
//包装线程执行目标,因为Thread的构造函数只能接受Runnable接口的实现类,而FutureTask类实现了Runnable接口
FutureTask<String> futureTask = new FutureTask<>(myCallable);
//传入线程执行目标,实例化线程对象
Thread thread = new Thread(futureTask);
//启动线程
thread.start();
String result = null;
try {
//获取线程执行结果
result = futureTask.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(result);
}
}
优点:扩展性好,支持多线程处理同一份资源,具备返回值及可以抛出受检查异常;
缺点:相较于实现Runnable接口的方式,较为繁琐
三、Runnable和Callable区别
Callable()规定重写的方式是call(),执行后可返回值,call()方法可以抛出异常,
Runnable()重写的方法是run(),没有返回值,run()方法不可以抛出异常
四、线程基本状态 新建(New)、可运行(Runnable)、等待(Waitting)、超时等待(Time Waitting)、阻塞(Blocked)、终止(Terminated)
- 新建(New):当线程实例被new出来之后,调用start()方法之前,线程实例处于新建状态
- 可运行(Runnable):当线程实例调用start()方法之后,线程调度器分配处理器资源之前,线程实例处于可运行状态或者线程调度器分配处理器资源给线程之后,线程实例处于运行中状态,这两种情况都属于可运行状态
- 等待(Waitting):当线程处于运行状态时,线程执行了obj.wait()或Thread.join()方法、Thread.join、LockSupport.park以及Thread.sleep()时,线程处于等待状态
- 超时等待(Timed Waitting):当线程处于运行状态时,线程执行了obj.wait(long)、Thread.join(long)、LockSupport.parkNanos、LockSupport.parkUntil以及Thread.sleep(long)方法时,线程处于超时等待状态
- 阻塞(Blocked):当线程处于运行状态时,获取锁失败,线程实例进入等待队列,同时状态变为阻塞
- 终止(Terminated):当线程执行完毕或出现异常提前结束时,线程进入终止状态
五、synchronized关键字和volatile关键字:
1、synchronized:用于控制线程同步,在多线程的环境下,控制synchronized代码段不被多个线程同时执行;synchronized可加在一段代码上也可加在方法上。
2、volatile关键字:修饰的变量,保证了其在多线程之间的可见性;与CAS(比较-交换)结合,保证了原子性
六、sleep()和wait()方法区别:
都可以放弃CPU一定时间,区别在于如果线程持有某个对象的监视器,sleep()方法不会放弃这个对象监视器,wait()则会放弃
七、多线程同步方法:Synchronized关键字,Lock锁实现,分布式锁等