目录
程序、进程、线程
进程的特点
线程注意事项
线程与进程的关系编辑
主内存与本地内存的关系
JMM有以下规定
volatile关键字
作用解释:
串行,并行和并发
时间片
上下文切换
OS底层执行线程规则
线程的生命周期
线程经历的阶段
阻塞分类
创建线程的方式
三种方式
继承Thread类
总结:
实现Runnable接口
总结:
实现callable接口
实现callable接口与实现runnable接口的区别
获取返回值
具体代码
理解第三种线程创建方式
线程池
线程池优点
ExecutorService
常见方法
案例
线程池的5种状态
Lamda表达式
Lamda表达式优点
转换过程
线程的的常用方法
构造方法
普通方法
线程的停止
线程休眠
线程礼让
join方法
观测线程状态
属性值
获取线程状态
线程的优先级
前言:
优先级属性
优先级方法
守护线程
设置守护线程
线程安全
线程安全问题条件
java中线程安全的三方面体现
同步与异步
同步机制
使用同步锁的问题
同步代码块
案例
注意:
同步方法
具体案例
总结:
Lock锁
前言
lock与synchronized的区别
Lock锁的使用
乐观锁与悲观锁
乐观锁案例
锁的状态
锁升级原理
死锁
死锁的必要条件
线程通信
常用方法(Object类中)
线程wait和notify工作原理
经典案例
Threadlocal
常用方法
Semaphore
常用方法
程序:为完成特定任务,用某种语言编写的一组指令的集合,是一段静态代码
进程(process):其是程序的一次执行过程,正在运行的一个程序,进程作为资源分配的单位,在内存中会为每个进程分配不同的内存区域。进程是一个动的过程,进程的生命周期:有它自身的产生,存在和消亡的过程。
线程(thread):线程是操作系统OS能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位
注意:我们平时执行main方法时一般伴随着3个线程的执行,分别为main主线程,异常处理线程,垃圾回收线程
java作为高级语言屏蔽了cpu多层缓存这些细节,用jmm定义了一套读写内存的数据规范,虽然不需要关心一级缓存和二级缓存这些问题,但jmm抽象了主内存和本地内存的概念
前言:其仅能使用在变量级别,使用该关键字的变量,一个线程修改则其他线程可见,并且避免了指令的重排序
并行:同一时刻有多个线程同时执行
并发:同一个对象被多个线程同时操作
串行:同一时刻一个线程只执行一个任务,这个任务执行完才执行下一个
并发编程的目的:充分利用处理器的每一个核,以达到最高的处理性能
时间片,即CPU分配给各个线程的一个时间段,称作它的时间片,即该线程被允许运行的时间,如果在时间片用完时线程还在执行,那CPU将被剥夺并分配给另一个线程,将当前线程挂起,如果线程在时间片用完之前阻塞或结束,则CPU当即进行切换,从而避免CPU资源浪费,当再次切换到之前挂起的线程,恢复现场,继续执行。
当前任务执行完CPU时间片切换到另一个任务之前会先保存自己的状态以便下次切换回这个任务时可以加载这个任务的状态。任务从保存到再加载的过程就是一次上下文切换
注意:CPU以纳秒的级别进行时间片的分配,高速完成着线程的切换
含义:线程从开始到消亡的过程
注意:
//继承Thread类
public class MyThread extends Thread{
//重写run方法,里面放执行逻辑
@Override
public void run() {
//run方法线程体
for (int i=0;i<9;i++){
System.out.println("我是线程"+getName()+"\t"+i);
}
}
}
class Test1{
public static void main(String[] args) {
new MyThread().start();
new MyThread().start();
new MyThread().start();
}
}
注意:
public class TestThread implements Runnable{
int count=30;
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"——"+i);
count--;
}
System.out.println(Thread.currentThread().getName()+"\t"+count);
}
}
class Test2{
public static void main(String[] args) {
TestThread target = new TestThread();
//因为公用1个target所以共享count
new Thread(target,"线程1").start();
new Thread(target,"线程2").start();
new Thread(target,"线程3").start();
}
}
注意:实现Runnable接口方式创建线程避免了单继承的局限性,可以多个线程跑一个对象
语法:Object o = FutureTask适配器对象.get();
注意:该方法可能产生阻塞,因为它要等待返回值返回
public class TestRandomNum implements Callable {
//上面的泛型参数对应下面方法的返回值参数,如果不写,那么下面的返回值默认为Object类型
@Override
public Integer call() throws Exception {
System.out.println("线程执行体"+Thread.currentThread().getName());
return new Random().nextInt(10);
}
}
class Test06{
public static void main(String[] args) throws Exception {
//定义一个线程对象
TestRandomNum num = new TestRandomNum();
//新建适配类
FutureTask task = new FutureTask(num);
FutureTask task1 = new FutureTask(num);
new Thread(task).start();
new Thread(task1).start();
//获取线程得到的返回值
Object o = task.get();
Object n = task1.get();
System.out.println("task:"+o+"\ttask1:"+n);
}
}
注意:
前言:经常创建和销毁,使用量特别大的资源,对性能影响很大,因此可以提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁的创建和销毁,实现重复利用。
ExecutorService:真正的线程池接口,常见子类ThreadPoolExecutor
void execute(Runnable command):执行任务或命令,没有返回值
Future 执行任务,有返回值submit(Callable task): void shutdown():关闭线程池
Executors:工具类,线程池的工厂类,用于创建并返回不同类型的线程池
static ExecutorService newFixedThreadPool(int n) 最多n个线程的线程池
static ExecutorService newCachedThreadPool() 足够多的线程,使任务不必等待
static ExecutorService newSingleThreadExecutor() 只有一个线程的线程池
public class TestPool {
public static void main(String[] args) {
//创建线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
//上面创建最多10个线程,但是只用了6个
for (int i = 0; i < 6; i++) {
pool.execute(new RThread());
}
//关闭连接
pool.shutdown();
}
}
class RThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
函数式接口:任何接口,如果只包含唯一一个抽象方法,那么他就是一个函数式接口
注意:对于函数式接口,我们可以通过Lamda表达式来创建该接口的对象
public class Test {
public static void main(String[] args) {
//局部内部类
class Have implements OneMethod{
@Override
public void fly(int a) {
System.out.println("我飞了"+a+"米");
}
}
Have have = new Have();
have.fly(3);
//OneMethod的匿名内部类写法
OneMethod one=new OneMethod(){
@Override
public void fly(int a) {
System.out.println("我飞了"+a+"米");
}
};
one.fly(3);
//OneMethod的lambda表达式写法
OneMethod oneMethod=(int a)->{
System.out.println("我飞了"+a+"米");
};
oneMethod.fly(3);
}
}
//定义一个函数时接口
interface OneMethod{
void fly(int a);
}
注意:
前言:因为线程的创建方式有3种:继承Thread、实现Runnable接口、实现Callable接口,Runnable接口只有运行方法run,Callable接口只有运行方法call,而线程的启动都需要借助Thread类对象,所以线程的方法也可以理解为Thread类的方法
Thread():分配新的Thread对象
Thread(String name):分配新的Thread对象并为线程起名
Thread(Runnable target):分配新的Thread对象
Thread(Runnable target,String name):分配新的Thread对象并为线程起名
static Thread currentThread( ):返回对当前正在执行的线程对象的引用
long getId():返回该线程的标识
String getName():返回该线程的名称
static void sleep(long millions):在指定的毫秒数内让指定的线程休眠
void start():使该线程开始执行(Java虚拟机调用该线程的run())int getPriority():获取线程优先级
void setPriority(int newPriority):设置线程优先级别
void join():等待该线程终止(插入该线程)
static void yield():暂停当前正在执行的线程对象,并执行其他线程
void interrupt():中断线程,别用这个方式
boolean isAlive():测试线程是否处于活动状态
Thread.State getState():返回该线程状态
public class TestStop implements Runnable{
//设置一个标志位
private boolean flag=true;
@Override
public void run() {
int i=0;
while (flag){
System.out.println("run……Thread"+i++);
}
}
public void stop(){
this.flag=false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i=0;i<1000;i++){
System.out.println("main"+i);
if (i==900){
//调用stop方法切换标志位,让线程停止
testStop.stop();
System.out.println("线程停止了");
}
}
}
}
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"a线程").start();
new Thread(myYield,"b线程").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
//线程礼让
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i=0;i<100;i++){
try {
//为了能有机会插队
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是VIP");
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
for (int i=0;i<300;i++){
if (i==200){
//此线程执行完,主线程才能继续执行
thread.join();
}
System.out.println("main"+i);
}
}
}
语法:Thread.State.属性
注意:Thread.State为嵌套类
语法:线程.getState()
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
for (int i=0;i<5;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("我是标记");
});
//观察状态
Thread.State state = thread.getState();
System.out.println(state);
//启动后
thread.start();
//只要线程不处于退出状态就一直打印状态
while (state!=Thread.State.TERMINATED){
Thread.sleep(100);
state=thread.getState();
System.out.println(state);
}
}
}
- Thread.MIN_PRIORITY=1
- Thread.MAX_PRIORITY=10
- Thread.NORM_PRIORITY=5
获取线程优先级:int getPriority()
设置线程优先级:void setPriority(int xxx)
public class TestPriority {
public static void main(String[] args) {
MyPriority myPriority = new MyPriority();
Thread t1 = new Thread(myPriority);
Thread t2 = new Thread(myPriority);
Thread t3 = new Thread(myPriority);
//设置优先级再启动
t1.setPriority(1);
t2.setPriority(5);
t3.setPriority(10);
t1.start();
t2.start();
t3.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"优先级为"+Thread.currentThread().getPriority());
}
}
语法:线程.setDaemon(true);
注意:默认值为false表示他为普通线程
public class TestDaemon {
public static void main(String[] args) throws InterruptedException {
//守护线程
God god = new God();
//用户线程
You you = new You();
Thread thread = new Thread(god);
thread.setDaemon(true);//默认为false,表示是用户线程
thread.start();
new Thread(you).start();//用户线程启动
}
}
class God implements Runnable{
@Override
public void run() {
while (true){
System.out.println("主与你同在");
}
}
}
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 36500; i++) {
System.out.println("你一生都开心的活着");
}
System.out.println("goodbye world");
}
}
同步:体现了排队的效果,同一时刻只能有一个线程独占资源,其他没有权利的线程排队。
异步:体现了多线程抢占资源的效果,线程间互相不等待,互相抢占资源。
注意:同步执行效率低但安全,异步执行效率高但不安全
前言:处理多线程问题时,多个线程访问同一个对象,并且,某些线程还想修改这个对象,此时我们就需要线程同步,线程同步实际上就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕,下一个线程再使用。
线程同步条件:队列+锁
注意:由于同一进程的多个线程共享同一块存储空间,在带来方便的同时也带来了冲突问题,为了保证数据在方法中被访问的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排他锁,独占资源,其他线程必须等待,使用后释放锁即可
synchronized (同步监视器){
……需要同步的代码……
}
public class Test08 {
public static void main(String[] args) {
ByTicketThread t = new ByTicketThread();
new Thread(t,"小明").start();
new Thread(t,"小红").start();
new Thread(t,"小兰").start();
}
}
class ByTicketThread implements Runnable{
int ticketNum=100;
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
synchronized (this) {
if (ticketNum > 0) {
System.out.println(Thread.currentThread().getName() + "买到第" + ticketNum-- + "张");
}
}
}
}
}
//给方法加同步修饰符
public synchronized void method(int args){}
public class Test08 {
public static void main(String[] args) {
ByTicketThread t = new ByTicketThread();
new Thread(t,"小明").start();
new Thread(t,"小红").start();
new Thread(t,"小兰").start();
}
}
class ByTicketThread implements Runnable{
int ticketNum=100;
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
get();
}
}
//如果是继承Thread类,则同步方法需加static
public synchronized void get(){
if (ticketNum > 0) {
System.out.println(Thread.currentThread().getName() + "买到第" + ticketNum-- + "张");
}
}
}
synchronized是java中的关键字,这个关键字的识别是靠jvm来识别完成的,是虚拟机级别,但是lock锁是API级别,提供了相应的接口与对应的实现类,这个方式更灵活,表现出来的性能优于之前的方式
拿锁:Lock lock=new ReentrantLock();
打开锁:lock.lock();
关闭锁:lock.unlock();
注意:
- 打开锁与关闭锁之间放代码块,并且关闭锁尽量放到finally种
- Lock为一个接口,不可以直接创建对象
public class Test08 {
public static void main(String[] args) {
ByTicketThread t = new ByTicketThread();
new Thread(t,"小明").start();
new Thread(t,"小红").start();
new Thread(t,"小兰").start();
}
}
class ByTicketThread implements Runnable{
int ticketNum=100;
//拿来一把锁
Lock lock=new ReentrantLock();
@Override
public void run() {
for (int i = 1; i <= 100; i++) {
//打开锁
lock.lock();
try {
if (ticketNum > 0) {
System.out.println(Thread.currentThread().getName() + "买到第" + ticketNum-- + "张");
}
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭锁
lock.unlock();
}
}
}
}
CAS(compare and swap)
cas(&i,0,1)
CAS操作包含三个操作数:(内存位置V,期望原值A,和新值B)
注意:当且仅当内存地址的V的值和预期的值都相等时,cpu才会更新到新值B,否则失效
ABA问题:一个线程将数值改成了b,接着又改成了a,此时cas会认为其没变化,其实已经变化过了
解决办法:使用版本号标识,每操作一次version+1来解决
自旋锁:就是让线程等待一段时间,不会被立即挂起,看持有锁的线程是否很快的释放锁,其不会改变以前的旧值,当判断另一个线程释放锁后再将其改为约定的新值
锁的四种状态:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态
注意:
在锁对象的对象头里面有一个 threadid 字段,在第一次访问的时候threadid 为空,jvm 让其持有偏向锁,并将 threadid 设置为其线程 id,再次进入的时候会先判断threadid 是否与其线程 id 一致,如果一致则可以直接使用此对象,如果不一致,则升级偏向锁为轻量级锁,通过自旋循环一定次数来获取锁,执行一定次数之后,如果还没有正常获取到要使用的对象,此时就会把锁从轻量级升级为重量级锁,此过程就构成了锁的升级。
含义:两个线程互相抱着对方需要的资源,互相等待对方的执行结果,形成僵持,并且两个线程均不会释放各自的资源
public class DeadLock {
public static void main(String[] args) {
Makeup g1 = new Makeup(0, "灰姑娘");
Makeup g2 = new Makeup(1, "白雪公主");
g1.start();
g2.start();
}
}
class Lipstick{}
class Mirror{}
class Makeup extends Thread{
//需要的资源只有一份
static Lipstick lipstick=new Lipstick();
static Mirror mirror=new Mirror();
int choice;
String girlName;
Makeup(int choice,String girlName){
this.choice=choice;
this.girlName=girlName;
}
@Override
public void run() {
//化妆
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void makeup() throws InterruptedException {
if (choice==0){
synchronized (lipstick){
System.out.println(this.girlName+"获得口红的锁");
Thread.sleep(1000);
synchronized (mirror){
System.out.println(this.girlName+"获得镜子的锁");
}
}
}else {
synchronized (mirror){
System.out.println(this.girlName+"获得镜子的锁");
synchronized (lipstick){
System.out.println(this.girlName+"获得口红的锁");
}
}
}
}
}
void wait():表示线程一直等待,直到其他线程通知(等待时释放锁)
void wait(long timeout):指定等待的毫秒数
void notify():随机唤醒一个处于等待状态的线程
void notifyAll():唤醒同一对象上所有调用了wait方法的线程争抢资源
当对象Object调用wait方法后,线程进入Object对象的等待队列,多个线程等待同一个对象,当对象Object调用了notify方法后,就会从等待队列队列里随机选取一个线程并将其唤起,完全随机
注意:
public class Message {
final static Object OBJECT=new Object();
public static class T1 extends Thread{
@Override
public void run() {
synchronized (OBJECT){
System.out.println(System.currentTimeMillis()+"——t1 start");
try {
System.out.println(System.currentTimeMillis()+"——t1 wait for object");
OBJECT.wait();
}catch (Exception e){
e.printStackTrace();
}
System.out.println(System.currentTimeMillis()+"——t1 end");
}
}
}
public static class T2 extends Thread{
@Override
public void run() {
synchronized (OBJECT){
System.out.println(System.currentTimeMillis()+"——t2 start notify one thread");
OBJECT.notify();
System.out.println(System.currentTimeMillis()+"——t2 notify end");
try {
Thread.sleep(5000);
System.out.println(System.currentTimeMillis()+"——t2 end");
}catch (Exception e){
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
new T1().start();
new T2().start();
}
}
Threadlocal属于线程自身所有,不会在多个线程间共享,java提供了Threadlocal类支持线程局部变量,是一种实现线程安全的方式
注意:在管理环境下使用线程局部变量时要特别小心,在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长,任何线程局部变量一旦在工作完成后没有释放,java应用就会存在内存泄露的风险(避免前世记忆影响今生行为,使用前最好remove掉)
创建对象:ThreadLocal
threadLocal=new ThreadLocal<>();
T get():返回此线程局部变量当前值
void set(T value):为此线程局部变量设置指定的值
void remove():移除此线程局部变量在该线程当前的值
public class Current {
static ThreadLocal threadLocal=new ThreadLocal<>();
public static void main(String[] args) throws Exception {
Thread t1 = new Thread(() -> {
System.out.println(threadLocal.get());
threadLocal.set(0);
System.out.println(threadLocal.get());
});
Thread t2 = new Thread(() -> {
System.out.println(threadLocal.get());
threadLocal.set(1);
System.out.println(threadLocal.get());
});
t1.start();
Thread.sleep(1000);
t2.start();
}
}
含义:Semaphore就是一个信号量,他的作用是限制某代码段中的并发数
工作原理:Semaphore计数信号量会初始化指定数量的许可,每调用一次acquire方法,一个许可被调用的线程取走,每调用一个release方法,一个许可会被返还给信号量,因此在没有任何release调用时最多n个线程能通过acquire方法,n是该信号量初始化时指定许可的数量,这些许可只是简单的计数器
创建对象:Semaphore semaphore = new Semaphore(int n,boolean flag);
注意:第一个值为int类型,表示许可数量,第二个值为boolean类型,表示是否为公平策略,默认为false(非公平);若为公平策略,则按照请求事件获取许可,即先发送的请求先获得许可,若为非公平策略,则先发送的请求未必先获得许可,这有助于提高程序的吞吐量,但有可能导致某些请求始终获取不到许可
void acquire():从此信号量中获取一个许可,若无许可获得,则会一直等待
void release():释放一个许可,来返还给信号量
public class Sign {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2);
new SignThread(semaphore, "线程1").start();
new SignThread(semaphore, "线程2").start();
new SignThread(semaphore, "线程3").start();
}
}
class SignThread extends Thread{
private Semaphore semaphore;
public SignThread(Semaphore semaphore,String s) {
this.semaphore = semaphore;
//为线程起名
setName(s);
}
@Override
public void run() {
try {
//取走许可
semaphore.acquire();
System.out.println(getName()+"取走了许可");
sleep(2000);
//释放许可
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}