1.线程简介
进程:进程是活动的程序,已经启动进驻到内存中正在使用的程序,每个进程拥有独立的内存空间。
线程:线程是进程中最小的调度单元,cpu控制的最小的执行单元。
任何一个程序都至少有一个线程在使用,多个线程共享内存。多线程切换消耗的资源少。
2.并发与并行
并发:在同一时间间隔内,同时有多个线程运行。如果一台主机只有一个cpu,那么在某一时刻内最多只能有一个线程在cpu中运行,如果有多个程序需要运行,则将一个时间间隔分成小的时间段,在每个时间段中执行不同的线程。
并行:在同一时刻,同时有多个线程运行。如果一台主机有两个cpu时候,如果有多个线程需要执行,则分别在两个cpu上分时间段运行。
3.如何创建线程
3.1.继承Thread类
要重写run方法
public class MyThread1 extends Thread{
@Override
public void run() {
super.run();
}
}
创建线程对象,启动线程
public class Test {
public static void main(String[] args) {
MyThread1 myThread1=new MyThread1();
myThread1.start();
}
}
3.2.实现Runnable接口
重写run方法
public class MyRun implements Runnable{
@Override
public void run() {
...
}
}
public class Test {
public static void main(String[] args) {
MyRun myRun=new MyRun();
Thread thread=new Thread(myRun);
thread.start();
}
}
3.3.实现Callable接口
可以获取线程的返回值
public class MyCall implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("线程在执行的任务");
return Thread.currentThread().getName();
}
public static void main(String[] args) {
// 创建Callable接口的实例
MyCall myCall=new MyCall();
// FutureTask 实例
FutureTask<String> task=new FutureTask<>(myCall);
Thread thread=new Thread(task);
thread.start();
}
}
小结:
1、创建线程的三种方式:
-
继承Thread类
-
实现Runnable接口
-
实现Callable接口,配套使用FutureTask进行转换
2、什么时候用哪种方式?
-
如果有返回结果:Callable
-
如果没有返回结果:继承Thread类|实现Runnable接口
-
继承只能单继承,多个线程共享数据使用Runnable接口
方法 | 描述 |
---|---|
public static Thread currentThread() | 返回当前的线程 |
public final String getName() | 返回线程名称 |
public final void setPriority(int priority) | 设置线程优先级 |
public void start() | 开始执行线程 |
public static void sleep(long m) | 使目前的线程休眠m毫秒 |
public final void yield() | 暂停目前的线程,运行其他 线程 |
public void run() | 线程要执行的任务 |
4.线程状态
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。
阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
5.线程同步
线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作。那相对的线程异步处理就不用阻塞当前线程,而是允许后续操作,直至其他线程将处理完成,并回调此线程。
线程同步的利弊:
好处:解决了线程同步的数据安全问题
弊端:当线程很多的时候,每个线程都会去判断同步上面的这个锁,很耗费资源,降低效率。
实现同步的方式:
1、基于synchronized(同步)实现
synchronized关键字可以用于修饰方法、代码块或静态方法。当修饰方法时,synchronized关键字将锁定整个方法,而当修饰代码块时,只会锁定代码块中的部分代码。静态方法的锁定范围是整个类。
2.基于Lock实现
ReentrantLock类实现了Lock,它拥有和synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显示加锁,释放锁。
private Lock lock = new ReentrantLock();//定义Lock类型的锁
public void method(int a){
lock.lock();//上锁
try {
。。。
}finally {
lock.unlock();//解锁
}
}
synchronized与Lock的对比
Lock是显示锁,需要自己手动开启和关闭,synchronized是隐式锁,出了作用于自动释放,无需自己释放。
Lock只能锁代码块,synchronized可以锁代码块和方法
使用Lock锁,JVM将花费更少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
6.死锁
死锁:当多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,从而导致两个或者多个线程都在等待对方释放资源,都停止执行的情况。尽量不要让 一个同步代码块 同时拥有两个以上的对象的锁
7.Object类中对线程的支持
方法 | 描述 |
---|---|
public final void wait() | 线程等待 ,释放锁 ,用在同步方法中的。 |
public final void wait(long timeout) | 线程等待,并指定等待时间,以毫秒为单位 |
Public void notify() | 唤醒一个等待的线程 |
Public void notifyAll() | 唤醒全部等待的线程 |
sleep方法和wait方法的区别?
1、sleep是Thread类种的方法,wait是Object中的方法。
2、wait必须用在同步代码中,释放锁,让当前线程处于等待状态;sleep不会解锁;
8.线程池
线程池就是首先创建一些线程,他们的集合称之为线程池。线程池在系统启动时会创建大量空闲线程,程序将一个任务传递给线程池,线程池就会启动一条线程来执行这个任务,执行结束后线程不会销毁(死亡),而是再次返回到线程池中成为空闲状态,等待执行下一个任务。
线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后再需要执行新的任务时重用这些线程而不是新建线程
多线程运行时,系统不断创建和销毁新的线程,成本非常高,会过度的消耗系统资源,从而可能导致系统资源崩溃,使用线程池就是最好的选择
8.1创建线程池
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
-
corePoolSize:线程池核心线程数量
-
maximumPoolSize:线程池最大线程数量
-
keepAliverTime:当活跃线程数大于核心线程数时,空闲的多余线程最大存活时间
-
unit:存活时间的单位
-
workQueue:存放任务的队列
-
handler:超出线程范围和队列容量的任务的处理程序
8.2线程池的工作流程
1、提交任务,判断核心线程是否已满,如果未满,创建核心线程用来处理任务;如果已满进入下一步
2、判断阻塞队列是否已满,如果未满,将任务放入队列中,等待执行,如果已满,进入下一步
3、如果线程池中的最大线程未满,创建线程执行任务,如果已满,按照拒绝策略进行处理。