Java 多线程介绍1(基本介绍, 常用方法,生命周期,线程创建)

看完后需要理解的知识

  1. 串行,并发,与并行的区别
  2. 进程与线程的关系
  3. 线程的生命周期,以及关系
  4. 创建线程的前两种方式

1.线程概念

1.1 线程相关概念

进程

    1. 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是操作系统进行资源分配与调度的基本单位
    1. 可以把进程简单的理解为正在操作系统中运行的一个程序

线程

    1. 线程(thread)是进程的一个执行单元.
    1. 一个线程就是进程中一个单一顺序的控制流,进程的一个执行分支
    1. 进程是线程的容器,一个进程至少有一个线程,一个进程中也可以有多个线程.
    1. 在操作系统中是以进程为单位分配资源,如虚拟存储空间,文件描述等,每个线程都有各自的线程栈,自己的寄存器环境,自己的线程本地存储,
1.2 主线程与子线程

-1. JVM 启动时会创建一个主线程,该主线程负责执行main方法,主线程就是运行main方法的线程
-2. Java中的线程不是孤立的,线程之间存在一些联系,如果在A线程中创建B线程,称B线程为A线程的子线程,相应的A线程就是B线程的父线程。

1.3 串行并发与并发

-1. 串行,逐个完成任务,
-2. 并发,等待一个任务完的时间内,就开始下一个任务
-3. 并行, 任务同时开始,时间取决于最长的那个任务

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
并发可以提高事物处理的效率,即一段时间内可以处理或完成更多的事情.
并行是一种更为严格,理想的并发

2线程的创建与启动

在Java中,创建一个线程就是创建一个Thread类(子类)的对象(实列)

  • 调用线程的start方法来启动线程,启动线程的实质就是请求JVM运行相应的线程,这个线程具体在什么时候运行由线程**调度器(Scheduler)**决定

  • 注意:start()方法调用结束并不意味着子线程开始运行。

  • 新开启的线程会执行run()方法

  • 如果开启了多个线程,start()调用的顺序并不一定就是线程启动的顺序

  • 多线程运行结果与代码执行调用顺序无关

Thread有两个常用的构造方法:Thread()与Tread(Runnable).对应的创建的两种方式:

定义Thread类的子类
    1. 定义类继承Thread
    1. 重写Thread父类中的run()
    1. 创建子线程对象
    1. 启动线程
定义一个Runbale接口的实现类
    1. 定义实现类Runnable接口
    1. 重写Runnable接口中的抽象方法,run()方法就是子线程要执行的代码
    1. 创建Runnable 的实现类对象
    1. 使用Runnable对象做为参数创建线程对象
    1. 开启线程

有时调用Thread(Runnable)构造方法时,实参也会传递署名内部类

  Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {

        }
    });
这两种创建线程的方式没有本质的区别
2.1线程的常用方法
2.1.1 currentThread()方法

Thread.currentThread() 方法可以获得当前线程
Java中的任何一段代码都是执行在某以线程当中的,执行当前线程就是当前线程,
同一段代码可能被不同的线程执行,因此当前线程是相对的,Thread.currentThread()方法的返回值是在代码实际运行时的

2.1.2 setName()/getName()

Thread.setName(线程名称), 设置线程名称
Thread.getName() 返回线程名称
通过设置线程名称,有助于线程调试,提高线程的可读性,建议为每个线程都设置一个能够体现线程功能的名称

2.1.3 isAlive

thread.siAlive() 判断线程是否处于活动状态
活动状态就是线程以及启动并且尚未终止

2.1.4 sellp()

Thread.sleep(); 让当前线程休眠指定的毫秒数
当前线程是指Thread.currentThread() 返回的线程

2.1.5 getId()

thread.getId() 可以获得线程的唯一标识,
注意 :
某个编号的线程运行结束后,该编号可能被后续创建的线程使用,
重启的JVM后,同一个线程的编号可能不一样

2.1.6 yield()
	Thread.yieId()方法的作用是放弃当前CPU的资源,
2.1.7 setPriority

thread.setPriority(num ) 设置线程的优先级

  • java下次你的优先级为1-10, 如果超出这个范围会抛出异常IllegaLargumentException
  • 在操作系统红,优先级较高的线程获得CPU的资源越多,
  • 线程优先级本质上只是给线程调度器一个提示信息,以便调度器先调用哪些线程,注意不能保证优先级高的线程先运行
  • Java优先级设置不当或者滥用可能会导致某些线程永远无法得到运行,即产生了线程饥饿
  •   线程的优先级并不是设置的越高越好,一般情况下使用普通的优先级即可,即在开发时不必设置线程的优先级
    
  • 线程的优先级具有继承性
2.1.8 intrrupt()

中断线程.

  • 调用interrupt()方法仅仅在当前线程打一个停止标注,并不是真正的停止线程
  • 需要代码去手动判断是否有中断标注 (this.isInterrupted()
2.1.9 setDaemon()

Java中的线程分为用户线程和守护线程
守护线程

  • 守护线程是为其他线程提供服务的线程,如垃圾回收器(GC)就是一个典型的守护线程
  • 守护线程不能单独运行,当JVM中没有其他用户线程,只有守护线程时,守护线程会自动销毁,JVM会推出
  • 设置守护线程的代码需要在线程启动前
2.2线程的生命周期

在这里插入图片描述

线程的生命周期可以通过getState()方法获得,线程的状态是Thread.state是一个枚举类型有一下几种:

  • new,新建状态,创建了多线程对象,在start() 启动器的状态
  • RUNNABLE, 可运行状态,它所一个复合状态,包含:READY和RUNNING 两个状态,READY状态该线程可以被线程调度器进行调度使他处于RUNNING状态,RUNNINGA状态表示该线程正在执行. Thread.yieId()方法可以把线程由RUNNNG状态转换为READY状态
  • BLOCKED 堵塞状态. 线程发起堵塞的I/O操作,或者申请由其他线程独占资源,线程会转化为BLOCKED堵塞状态, 处于堵塞状态的线程不会占用CPU资源,当堵塞I/o操作完,或者线程获得了其申请的资源,线程就可以转换为RUNNABLE
  • WAITING 等待状态,线程执行了object.wait(), thread.join()方法会把线程转换为WATING等待状态, 执行object.notify()方法,或加入的线程执行完毕,当前线程会转换为RUNNABLE状态
  • TIMED_WAITING 状态,与WAITING状态类似,都是等待状态,区别在于处于该状态线程不会无限的等待,如果线程没有在指定的时间范围里完成期待的操作,该线程自动转换为RUNNABLE
  • TERMINATED 中止状态,线程结束处于中止装
2.3多线程编程的优势与存储的风险
2.3.1优势:
  • 提高系统的吞吐率(Throughout). 多线程编程可以使一个进程有多个并发(concurrent,即同时进行的)的操作
  • 提高响应性(Responsiveness). Web服务器会采用一些专门的线程负责用户的请求处理,缩短了用户的等待时间
  • 充分利用多核处理器资源(Multicore)处理器资源,通过多线程可以充分的利用CPU资源
2.3.2 风险:
  • 线程安全(Thread safe)问题, 多线程共享数据时,如果没有采用正确的并发访问措施,就可能会产生数据一致性问题,如读取脏数据(过期的数据),如丢失数据更新.
  • 线程活性问题(thread liveness)问题.由于程序自身的缺陷或者由资源稀缺性导致线程一直处于非RUNNABLE状态,这就是线程活性问题,常见的活性故障有以下

1 .死锁(deadlock). 类似于鹬蚌相争.
2.锁死(Lockout)类似睡美人故事中王子挂掉了,
3.活锁(Livelock)类似于小猫咬自己尾巴
4 饥饿(Starvation)类似于健壮的雏鸟总是从母鸟嘴中签抢到食物

  • 上下文切换(Context Switch)处理器从执行一个线程切换到执行另外一个线程,消耗系统资源
  • 可靠性,可能导致一个线程导致JVM意外终止,其他线程也无法执行
3.3 内部锁:synchronized 关键字

Java中的每个对象都有一个与之关联的内部锁(Intrinsic lock)。这种锁也称为监视器(Monitor),这种内部锁是一种排他锁,可以保障原子性,可见性与有序性。
内部锁是通过synchronized关键字实现的。 syfanfanchronized关键字修饰代码块,修饰该代码块,修饰方法

synchronized(对象锁) {
	同步代码块,可以在同步代码块中访问共享数据

}

  • 修饰实例方法就称为同步实例方法
  • 修饰静态方法就称为同步静态方法

多线程锁

公平锁与非公平锁

公平锁: 阳光普照,相对非公平锁而已效率会低,
非公平锁: 可能会出现饿死的情况。 效率相对较高

可重入锁

也叫递归锁, 在锁的内存还可以上锁, synchronized (隐式) 与Lock(显式) 都是可以重入锁。

死锁,

两个或两个以上的线程,因为抢占资源的问题造成互相等待的现象,如果没有外力干涉,它们无法再执行下去。

Callable 接口

目前我们学习了两种创建线程的方式-一种通过创建Thread类,一种是通过使用Runnable创建线程。但是,Runnable缺少的一项功能是,当线程终止时(即run()完成时),我们无法使线程返回结果,为了支持此功能,Java中提供了Callable接口

Runnable 接口与Callable接口的区别
  1. Callable接口 有返回值, Runnable 没有
  2. Callable会抛出异常,Runnable 没有
  3. 实现方法不同,一个run方法,一个call方法
    Callable不能像Runnable一样直接start()。 需要使用 FutureTask futureTask1 = new FutureTask<>(new MyThread2()); 来作为一个中间类。
    FutureTask常用方法
  • isDone() 是否执行完毕。
  • get()。 获取返回值。

JUC强大的辅助类

1.减少计数(CountDownLatch)
  • CountDownLatch类可以设置一个计数器,然后通过countDown方法来进行减1的操作,使用await方法等待计数器不大于0,然后继续执行awai方法之后的语句。
  • CountDownLatch主要有两个发放,当一个或多个线程调用await方法时,这些线程会阻塞
  • 其他线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞)
  • 当计数器的值变成0时,因await方法阻塞的线程会被唤醒,继续执行
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {

        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName()+ ":号同学,离开教室");
                countDownLatch.countDown();
            },String.valueOf(i)).start();
        }

        countDownLatch.await();
        System.out.println("班长锁门走人");
    }
}
2.循环栅栏 (CyclicBarrier)

从字面上的意思可以知道,这个类的中文意思是“循环栅栏”。大概的意思就是一个可循环利用的屏障。

它的作用就是会让所有线程都等待完成后才会继续下一步行动。

举个例子,就像生活中我们会约朋友们到某个餐厅一起吃饭,有些朋友可能会早到,有些朋友可能会晚到,但是这个餐厅规定必须等到所有人到齐之后才会让我们进去。这里的朋友们就是各个线程,餐厅就是 CyclicBarrier。

public class CyclicBarrierDemo {

    private static final int NUMBER = 7;

    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier =
                new CyclicBarrier(NUMBER, ()->{
                    System.out.println("收齐7颗龙珠");
                });

        for (int i = 0; i < 7; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + " 龙珠");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }
    }
}
3. 信号灯(Semaphore)
public class SemaphoreDemo {
    public static void main(String[] args) {
        //创建Semaphore, 设置许可证
        Semaphore semaphore = new Semaphore(3);

        //模拟六辆汽车
        for (int i = 0; i < 6; i++) {
            int finalI = i;
            new Thread(()->{
                //抢占
                try {
                    semaphore.acquire();

                    System.out.println("i"+ finalI + "-抢到了车位-" + Thread.currentThread().getName());
                    TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                    System.out.println("i"+ finalI + "--------离开了车位-" + Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值