线程基础知识(新手上路,管杀不管埋)

线程

    顺序执行流:
        程序从main方法开始执行,依次执行每行代码,如果程序执行某行代码的时候遇到阻塞,则程序会停滞在该处

    单线程程序:一个线程中只包含一个顺序执行流
    多线程: 可以包括多个顺序执行流程,而且多个执行顺序互不干扰.

    创建线程
    启动线程
    控制线程
    多线程之间的同步操作


    线程池

    通常一般一个程序(.exe),每个运行中的程序就是一个进程,当一个程序运行时,内部可以包含多个顺序执行流程,每个执行流程就是一个线程
        迅雷-->进程
            同时下载多个文件 --> 每个文件都是一个线程

线程和进程

    进程
        所有运行中的程序通常都对应一个进程(Process)
        当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且拥有独立功能,进程是系统进行资源分配和调度的独立单位.
        进程的三种特征:
            1. 独立性: 进程拥有自己独立的资源,每一个进程都拥有自己私有的地址.在没有经过程序本身的允许的情况下,一个用户进程不          可以直接访问其他进程中的内容
            2. 动态性: 进程和程序区别在于,程序只是一个静态指令集合,而进程是一个正在系统中运行的活动指令集合.在进程中加入了时           间的概念,具备了生命周期,存在于创建和死亡等状态
            3. 并发性 :多个进程可以在单个处理器上面并发执行,多个程序互不干扰.
        并发性和并行性
            并行性:在同一时刻,有多条指令,在多个处理器上同时运行
            并发性:在同一时刻,只能有一条指令,但多个进程直接进行快速轮换进行
    线程:
        同一个进程可以同时并发多个处理任务,线程也可以称为轻量级进程,线程是进程中的一个执行单元,就像进程在操作系统中的地位.
        当进程被初始化时,主线程就会被创建,一般程序只要求包含一个主线程,我们可以在改进程中创建多个顺序流程,每条线程也是相互独立的.
        线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程,多个线程共享同一个进程的系统资源.
        线程是独立存在的,它并不知道进程中是否还有其他线程存在.线程的执行是抢占式的
            当前运行的线程在任何时候都可能被挂起,以便另一个线程执行

            一个线程可以创建和销毁另一个线程

    一个程序运行后,至少一个进程,一个进程里可以包含多个线程,但至少包含一个线程

创建线程

    Thread类.在java中代表一个线程类,所有的线程对象都必须是Thread类或其子类的实例
    每条线程的作用是完成一定的任务,实际上就是执行一段代码程序流
    在线程中使用run()方法来封装需要执行的程序流

    1. 定义Thread类的子类,并重写该类的run()方法,该方法即是代表了该线程需要完成的任务,run方法也是线程的执行体
    2. 创建Thread子类的实例,即创建线程对象
    3. 用线程的start()方法来启动线程
        Thread.currentThread():获取当前正在执行的线程对象
        getName():返回线程名称
    实现Runnable接口创建线程类
        因为java是单继承,当继承线程类的时候,不可再继承自己的父类
        java推荐使用Runnable接口机来实现多线程操作
        1. 定义接口的实现类,并重写run()方法
        2. 创建Runnable接口的实例
        3. 将实例对象传入Thread类,来启动线程
    两种方式创建线程比较:
        采用Runnable接口方式实现接口
            1. 线程类只实现Runnable接口还可以继承其他的类
            2. 在这种方式下多个线程共享一个线程对象,非常适合多个相同线程处理同一份资源
            3. 编程稍微复杂,如果要访问当前线程必须使用Thread.currentThread()方法
        采用Thread类方式实现线程
            1. 编写简单,如果需要访问当前线程直接使用this关键字就可以获取到
            2. 因为线程继承了Thread类,不能再继承其他的父类

线程的生命周期

    当线程被创建并启动后,这个线程不一定被执行(进入执行状态),也不是一直处于执行状态.
    线程的生命周期
        1. 新建(new)
        2. 就绪(runnable)
        3. 运行(running)
        4. 阻塞(blocked)
        5. 死亡(dead)

    1. 新建和就绪状态
        当程序使用new关键字之后就处于新建状态
            java虚拟机分配内存,并初始化成员变量,现在和线程无关,程序也不会执行线程执行体中的代码
        当程序对象调用start()方法之后,该线程就处于就绪状态
            Java虚拟机会为其创建方法调用栈和程序计数器
            处于就绪状态线程并没有开始执行,它只是表示当前线程可以开始运行了.至于线程何时被执行,取决于jvm里的线程调度器
            注意:不要对已经就绪状态的线程再次调用start()方法,否则会引发IllegalThreadStateException错误
    2. 运行和阻塞
        如果处于就绪状态的线程获得了CPU,开始执行run方法的线程执行体,则该线程就处于运行状态
            当一条线程开始运行后,它不能一直处于运行状态(除非它的线程执行体足够短)
        线程在运行过程中需要被中断,目的是为了让其他线程获得执行机会.
            当正在运行的线程被中断后,当前的线程就处于阻塞状态

        怎样让线程阻塞
            1. 线程调用sleep方法主动放弃所占用的处理器资源
            2. 线程调用一个阻塞式IO
            3. 线程试图获得一个同步代码块的执行权限
            4. 线程在等待某个通知的时候(notify)
            5. 程序调用了线程的suspend方法,将当前程序挂起

        当前正在执行的线程被阻塞之后,其他线程就可以获得执行的机会
        当线程阻塞完成后自动转换成就绪状态,等待抢占资源
    3. 线程死亡
        1. run()方法执行完成,线程正常结束
        2. 线程抛出一个未捕获的Exception或Error
        3. 直接调用stop()方法来结束该线程--->不推荐使用
        当主线程结束的时候,其他线程不受任何影响.一旦子线程启动,它拥有和主线程相同的地位

        不允许对死亡状态的线程调用atart()方法,程序只能对新建状态的线程调用atart()方法

控制线程

    1. join()方法(插入)
        让一个线程等待另一个线程完成的方法,当某个执行流程中调用了其他线程的join()方法,调用线程将被阻塞,知道被join方法加入的join线程执行完毕之后再执行
        1. join(): 等待被join的线程执行完成
        2. join(long m): 等待被join线程的最长时间
    2. sleep()方法
        如果需要让当前正在执行的线程暂停一段时间,并进入阻塞状态,可以调用Thread类的静态方法sleep();
        sleep(long millis) 让当前正在执行的线程暂停多少毫秒
        sleep(long millis,long n) 
    3. yield()  让步方法
        可以让当前正在执行的线程暂停,但不会进入阻塞状态.直接让当前线程进去就绪状态.
    4. suspend()挂起,暂停    resume();恢复线程    stop()终止  不推荐使用
        suspend()调用之后,线程不会释放已经占用的资源比如(锁),而是占用资源进入睡眠状态.
        stop()在终结一个线程的时候,不保证线程的资源正常释放
    6. setDaemon() 后台线程,守护线程
          这种线程运行在后台,为其他线程提供服务,JVM的垃圾回收机制就是后台线程
          特征:
            1. 前台线程死亡,后台线程也会死亡
            2. 调用线程类的setDaemon(true),设置后台线程


    8. currentThread() 
          返回对当前正在执行的线程对象的引用。
    9. interrupt()  设置当前为中断状态,,如果当前线程被阻塞无法被中断,调用中断方法,可以中断正在阻塞的线程,抛出一个异常,把线              程变成就绪状态
          调用线程的中断方法,即会向当前线程发送信号  -- 线程中断!
          线程中断不会影响当前线程的运行,只是改变当前线程的中断状态
          默认中断的状态是false,如果调用中断方法变成true
        1. isInterrupted();   判断线程是否中断
        2. 安全终止线程
            一个是调用中断判断.一个是自己写一个boolean变量进行终止判断
            private volatile boolean on= true;
    10. 改变线程的优先级
        1. 优先级越高,抢占机会越多
        2. 线程的优先级是根据父级线程决定
        3. MAX_PRIORITY   优先级越高
        4. NORM_PRIORITY  默认优先级5
        5. MAIN_PRIORITY  优先级越低
        6. 在LUNIX中没有优先级概念

线程通信

    volatile:
        修饰成员变量,告诉任何对该变量访问均需要从共享内存中获取,而对它改变,必须同步刷新回共享区域内,他能保证所有线程对变量访问的可见性.
    synchronized(对象,类类型)  同步,
        同步代码块(放入对象,类);
            为当前类加锁,其他类不能使用当前类
            运行完之后释放锁,其他线程去抢占.
        任意一个Java对象都拥有同步监视器,当这个对象的同步监视器被调用的时候,执行方法的线程必须先获取到该对象的监视器才能进入同步代码块或者方法中,而没有获取到监视器的线程将会被阻塞在同步代码块或者同步方法的如后厨,当前线程变成阻塞状态.

        线程安全效率低,但可以保证线程的完整性
        线程非安全效率低,但不能保证数据的完整性

        同步方法
        java支持多线程同时访问一个对象或者是对象中的成员变量,每个线程中都拥有当前变量的拷贝.
        好处是加快执行效率
        当前线程看到的不一定是最新的变量值
    同步锁 :
        ReentrantLock look = new ReentrantLock();
        public void m(){
            lock.lock();  //加锁
            system.out.println("线程安全");
            lock.unlock();  //解锁
        }
    死锁:
        当两个线程或多个线程互相等待对方释放同步监视器,就会发生死锁.
        所有线程变成阻塞状态  --> 死锁
    等待/通知:
        一个线程修改了一个对象的值,二另一个线程感知到了变花,然后进行相应的操作,整个过程开始于一个线程,而终止于另一个线程
            生产者(通知方)     消费者(等待方)
            做什么           怎么做
        1. notify()       :  通知一个在对象上等待的线程,使其从wait()方法返回
        2. notifyAll()    :  通知所有在对象上等待的线程,使其从wait()方法返回
        3. wait()         :  调用该方法使线程进入,等待状态(阻塞状态)
        4. wait(long)     :  最多等待多长时间,如果还没有通知就,返回执行
        5. wait(long,int) : 
    当使用notify(), notifyAll() , wait() 时要先对调用对象加锁
    使用等待通知
        1. 先要获取对象的锁
        2. 如果条件不满足,那么调用对象wait()方法,被通知之后继续执行
        3. 条件满足执行对应的逻辑代码

线程池

    当线程启动一个新的线程的时候,会花费较高的资源
    使用线程池,可以很好的提高性能,尤其是系统项目需要创建大量生命周期很短的线程的时候::推荐使用线程池>
    线程池在系统启动的时候创建大量的空闲线程,程序将一个Runable对象传递给线程池
    线程池回启动一条线程的run()方法,当run方法执行完毕后,并不会死亡,而是在线程池中,成为空闲状态

    Executors  : 工厂类生产连接池使用
        1. newCachedThreadPool() 创建具有缓冲功能的连接池对象
        2. newFixedThreadPool(int)  : 创建一个可重用,具有固定线程数的线程池
        3. newSingleThreadExecutor() : 创建只有一个线程的,线程池,等价newFixedThreadPool(int)
        4. newScheduledThreadPool() : 创建具有指定线程数的线程池,可以指定延迟多长的时间执行.
        5. newSingleThreadScheduledExecutor() 创建只有一个线程的,线程池,并具有延时功能
        6. shutdown()关闭线程池

TimeUnit :

    提供对线程操作暂停的工具类
        常用的颗粒度
        1  TimeUnit.DAYS          //天
        2  TimeUnit.HOURS         //小时
        3  TimeUnit.MINUTES       //分钟
        4  TimeUnit.SECONDS       //秒
        5  TimeUnit.MILLISECONDS  //毫秒  
    例如 :
         TimeUnit.SECONDS.sleep( 5 );
         System.out.println( "延时5秒,完成了");

Timer

    定时器
    一秒执行一次
            import java.util.Timer;
            import java.util.TimerTask;
            public class M {
                public static void main(String[] args) {
                Timer timer = new Timer();
                timer.schedule(new MyTask(), 1000, 2000);
                }
            }
            class MyTask extends TimerTask {
                @Override
                public void run() {
                    System.out.println("dddd");
                }
            }
        timer的功能也可以通过自己构造线程,然后在线程中用sleep来模拟停止一段时间,然后再执行某个动作。
        timertask的源码就立即可以知道,timertask就是实现了runnable接口的。也就是说,通过timer来间隔一段时间执行一个操作,也是通过一个线程来做到的。

ThreadLocal

    线程类,自己查
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值