线程
- 程序,进程,线程
● 程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码.
● 进程(process)就是正在执行的程序,从Windows角度讲,进程是操作系统进行资源分配的最小单位.
● 线程(thread)进程可进一步细化为线程,是一个进程内部的最小执行单元,是操作系统进行任务调度的最小单元,隶属于进程.
早期Cpu执行是以进程为单位
后来改为以更小的线程为单位
-
线程和进程的关系
一 个进程中可以包含多个线程的 , 线程必须属于某一个进程 , 不能独立存在
每一个进程至少包含一个线程(称为主线程);在主线程中开始执行程序, java程序的入口main()方法就是在主线程中被执行的。
一个进程中的所有进程 , 共享该进程中的所有资源 -
Java如何创建线程(除了main线程之外的线程)
继承Thread类的方式:
Thread{public void run(){ //在run()中写需要单独执行的任务 }
}
创建类的对象
调用start();
实现Runnable接口方式public class RunTask implements Runnable{
public void run(){
//在run()中写需要单独执行的任务
}
}RunTask runTask = new RunTask();//创建一个线程要执行的任务对象
Thread thread = new Thread(runTask);
thread.start();//启动线程时区别:
- 继承Thread类之后,由于java是单继承的,所以就不能继承其他类了
- 实现Runnable接口的方式,此时类还可以继承其他类.
-
Thread类中的方法
run();用来写线程要执行逻辑(代码)
start();用来启动线程
Thread(runTask);创建线程对象后为线程添加一个执行任务,并为线程命名
currentThread();获取当前正在执行的线程
getId();获取到线程的id
getName();获取到线程的名字
setPriority();设置线程优先级 -
线程优先级
● 事实上,计算机只有一个CPU,各个线程轮流获得CPU的使用权,才能执行任务;
● 优先级较高的线程有更多获得CPU的机会,反之亦然;
● 优先级用整数表示,取值范围是1~10,一般情况下,线程的默认优先级都是5,但是也可以通过setPriority和getPriority方法来设置或返回优先级;
●调度策略:
● 时间片
● 抢占式:高优先级的线程抢占CPU
●Java的调度方法:
● 同优先级线程组成先进先出队列,使用时间片策略
● 对高优先级,使用优先调度的抢占式策略
● Thread类有如下3个静态常量来表示优先级
● MAX_PRIORITY:取值为10,表示最高优先级。
● MIN_PRIORITY:取值为1,表示最底优先级。
● NORM_PRIORITY:取值为5,表示默认的优先级。 -
线程状态(线程的生命周期,什么时候创建,什么时候销毁)
• 线程在它的生命周期中会处于不同的状态:
新建: new Thread ()/new Thread 的子类
就绪:(可执行)调用start()
任务切换
yeild()让步 主动让出执行权,进入到就绪状态
运行: 获取到cpu执行权
任务正常执行完成
stop()
出现异常未处理
死亡:
sleep();
join();
控制台输入
等待同步锁
wait();
阻塞状态 -
守护线程
线程分为用户线程和守护线程.
守护线程是为其他线程提供服务的,最大的特点是,当系统其他的用户线程结束后,守护线程会自动结束,JVM中的垃圾回收线程,就是一个守护线程. -
多线程
多线程是指程序中包含多个执行单元,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说运行单个程序多个并行程序的线程来完成各自的任务.
指的是一个程序内部,可以创建多个线程来执行多个任务.
多线程的优点:
提高程序的响应
提高CUP利用率
改善程序结构,(将复杂任务分为多个线程,独立运行)
多线程的缺点:
占用内存
占用CPU资源,
需要对多线程进行管理
线程之间共享资源会相互影响,如果不控制,会出现线程安全问题
线程同步:
并发:在一个时间段内,多个任务依次执行
并行:是真正意义上的同时执行,一个时间节点上有多个任务同时执行
由于继承的是Thread类方法,创建两个TicketThread对象
如果不加static修饰num,那么num在每一个对象中都有一个num.
用static修饰后,num在内存中就只有一份了,俩个线程对象共享的就是一个num了
synchronized修饰一段代码块,也可以修饰一个方法
synchronized(同步锁/同步对象){
}
同步对象要求:多个线程必须访问到的是同一个对象,对象用来记录有没有线程进入到同步代码块中
synchronized修饰方法时,锁对象有俩种情况:
1. 修饰的是非静态对象,锁对象默认是this
2. 修饰静态方法,锁对象是该类的Class对象
同步锁
同步锁可以是任何对象,必须唯一,保证多个线程获得是同一个对象(用来充当锁标记)
同步执行过程
1.第一个线程访问,锁定同步对象,执行其中代码
2.第二个线程访问,发现同步对象被锁定,无法访问
3.第一个线程访问完毕,解锁同步对象
4.第二个线程访问,发现同步对象没有锁,然后锁定并访问
一个线程持有锁会导致其他所有需要此锁的线程持起;在多线程竞争下加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
Lock锁
ReentrantLock实现了Lock接口,所以可以称为LOck锁
实现原理不同
是一种Java代码层面的控制实现,而synchronized是关键字,依靠底层编译后的指令实现
加锁范围不同
ReentrantLock只能对某一段代码块加锁,而synchronized可以对代码块和方法加锁
ReentrantLock需要手动的加锁释放锁,而synchronized是隐式的自动的加锁,自动释放锁(代码执行完了或出现异常了)
线程死锁
不同的线程分别占有对方需要的同步资源不放,双方相互等待,程序不报错,不提示
线程通信
线程通讯指的是多个线程通过相互牵制,相互调度,即线程间的相互作用。 涉及三个方法:
.wait一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
.notify一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。
.notifyAll一旦执行此方法,就会唤醒所有被wait的线程。
注意:
.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。
sleep()和wait()区别:共同点(都可以让线程进入阻塞状态)
sLeep() 让线程阻塞指定的时间,时间到了后,自已唤醒进入到就绪状态,不会释放同步锁,是Thread类中的方法
wait() 让线程进入等待(阻塞),不会自己唤醒,必须通过notify(),notifvALL()唤醒,等待时会释放锁,是Object类中的方法.
新增创建线程方式
● 实现Callable接口与使用Runnable相比,Callable功能更强大些.
• 相比run()方法,可以有返回值
• 方法可以抛出异常
• 支持泛型的返回值
• 需要借助FutureTask类,获取返回结果
接收任务
FutureTask futureTask = new FutureTask(任务);
创建线程:
Thread t = new Thread(futureTask);
t.start();
Integer val = futureTask.get();获得线程call方法的返回值