线程详解

一.进程与线程

进程:操作系统上一个正在运行中的程序,如:QQ,微信,eclipse,记事本....

线程:线程是进程内部的一个执行线索,一个进程内部可以由一道多个线程组成,每个线程有自己

      独立的内存空间,多个线程可以在自己内存空间执行任务

进程:QQ

线程:同时和多个人聊天

二.线程的优缺点

优点:多线程的并发执行,可以提高程序的效率

  并发:操作系统会将时间片分为很多个时间点,在每一个时间点上只允许一个线程进入到cpu中

        执行任务,因为时间片被划分足够细,所以从宏观上看多个线程是同时进行的,但微观

        上来看回切换执行,这种现象就是线程并发,并发可以近似看做同时,但不是真正意义

        上同时

  缺点:多线程在执行同一任务时,可能会发生线程并发问题
        
     解决:加锁

三.线程的创建方式

1.继承Thread

  1)写一个继承类Thread类,该类实例为一个线程对象

  2)重写run()方法,在方法中写线程要执行的任务

  3)创建线程实例,并调用start()启动线程,让线程处于就绪状态(可运行装态)

2.实现Runnadle(推荐)

  1)写一个类实现Runnadle接口,实现类实例为线程要执行的任务对象

  2)重写run()方法,在方法中写具体任务

  3)创建一个任务实例,在创建一个线程实例,将任务通过构造器方法传给选出线程

  4)调用线程start()方法,让线程除以就绪状态(可运行状态)

推荐使用第二种方式

  1)能实现线程和任务解耦合

  2)解决单继承问题     

四、线程主要方法

1、void run()         线程获得cpu之后,执行任务的方法

2、void start()       启动线程,让线程处于就绪可运行状态

3、static int activeCount()   获取当前线程所属线程组中活动的线程个数

4、static Thread currentThrad()   返回当前线程实例

5、int getId()    获取线程Id,主线程默认为1,其他线程10,11,12...

6、void setName()/String getName()   修改/获取线程名,如果没有设置系统自动分配

                                     主线程:main  其他线程:thread-0/1/2...

7、void setPriority()/int getPriority() 修改/获取线程的优先级

   线程切换是由操作系统来进行控制的,无法通过代码改变,但是可以设置线程优先级,最大程度

   上改善获得cpu时间片的概率,线程优先级可以设置1-10

   MAX_PRIOPITY     最大优先级10

   MIN_PRIOPITY     最小优先级1

   NORM_PRIOPITY    默认优先级5

8、void setDaemon()   设置线程为守护线程(后台线程)一个进程中只允许一个线程被设置为守护

   线程,其他线程默认就是前台线程,当所有前台线程死亡后,守护线程也就死亡了

9、static void sleep() 使线程退出cpu进行休眠,进入到阻塞状态,阻塞状态结束回到就绪状态

   在阻塞期间有可能会被其他线程提前唤醒(调用interrupt()方法)

10、void join() 插入式线程,被插入的线程会等待插入的线程执行完任务或执行一段时间后才

   有可能获得cpu,在此期间被插入线程处于阻塞状态

11、static void yield() 使正在运行的线程立即退出cpu处于就绪状态

五、线程间同步与协作

1、什么是线程同步

   异步:一个进程内部的多个线程之间默认是异步并发执行的,轮流获取cpu时间片来执行任务,

   宏观上可以看做事同时的进行的

   同步:多线程默认异步并发操作如果是对同一个资源(临界资源,共享变量)进行操作时,

   有可能会引发线程并发问题,线程不安全,这时候需要多线程有先后顺序的来执行

   异步:并发执行,大家一次干,各干各的

   同步:串发执行,大家有先后顺序的干

2.怎么将多线程间的默认异步操作改为同步操作,java中提供了一种锁机制来保证多线程的同步

  保证临界资源的安全性

  2.1 synchronized同步锁

      1)同步代码块

         如果方法中有一块代码,多线程只能顺次序先后访问,可以给这一块代码加锁,先获取

         锁的线程进入代码块执行,其他线程等待执行完后始放锁,其他获取锁的线程再进入执行 

         synchronized(锁对象){

              //同步代码块
     
         }

      2)同步锁

         如果一个方法,多线程只能顺次序先后调用,可以使用synchronized方法加锁,先获取锁 

         的线程调用方法,同时锁死方法,方法执行完成后始放锁,其他线程再获得锁调用方法

         语法:修饰符 synchroinzed 返回值 方法名 () {}

      3)锁对象的选择

         1>任意Object类型对象都可以作为锁对象,但是需要进行同步的多个线程看到的必须为同

           一个锁对象

         2>同步方法如果为实例对象,锁对象默认为调用当前方法的对象this

         3>同步方法如果为静态方法,锁对象默认为调用当前类被加载到内存的字节码 类.class

 2.2 ReentrantLok可重入锁

     作用与synchronized相同

     主要方法:lock()  上锁

               unlock()  释放锁

     为了保证锁无论如何都能够始放

       try{

         lock();//上锁

       }finally{

         unlock();//始放锁

       }

3.线程间协作

1>协作的概念

   多线程在同步的基础之上,如果在操作同一个资源是时,必须等一个线程操作完成之后,其他

   线程才能操作,多线程必须按照一个排好的顺序来操作这个同一资源,这就是线程间协作

2>线程间协作方式

  1)借助Object类提供wait()/notify()/notifyAll() 三个方法来完成

     wait():调用该方法的线程会处于阻塞状态,并始放已获得的锁,必须等待其他拥有相同锁的

            线程唤醒才可以结束等待,处于就绪状态,在其他线程始放锁后,该线程才可以获得

            锁并往下执行代码,一般写在同步代码之前

     notify():通知其他具备相同锁的线程不要等待了,一般写在同步代码之后(唤醒一个)

     notifyAll():同时通知其他所有处于等待状态的线程(唤醒多个)

     主要:以上三个方法必须写在同步代码块或同步方法中,并由同步锁来进行调用

  2)借助Condition类的提供的await()/signal()/signalAll() 三个方法来完成

     作用与Object类中wait()/notify()/notifyAll() 三个方法相同,必须与ReentrantLock一起

     使用,方法调用必须放在lock()和unlock()代码之间

六.线程的特点

1.原子性

 线程与线程之间相互独立,一个时间段只允许一个线程进入cpu执行,synchronized同步锁能保证

 线程原子性

2.可见性

 一个线程对共享变量的操作能够及时的被其他线程锁看到,线程读取到永远是共享变量的最新值

3.可见性的现象方式有两种

 1)synchronized

    1>获取同步锁

    2>清空工作内存副本值

    3>将主内存共享变量值拷贝到工作内存中

    4>在工作内存操作共享副本值

    5>将工作内存中修改的副本值刷新到主内存

    6>始放同步锁

 2)volatile

     读取:

     1>清空工作内存副本值

     2>将主内存共享变量值拷贝到工作内存中

     3>在工作内存操作共享副本值

     4>将工作内存中修改的副本值刷新到主内存

     
     volatile使用场景:用于修饰共享变量的,线程多次修改的共享变量值之间没有关系

 3)线程之间不可见原因

    1>多线程并发

    2>指令重排序

      代码编写顺序和最终执行顺序不一致

      编译器优化,处理器优化,内存系统优化

      目的是为了程序执行性能,单线程指令重排序不会影响程序最终执行结果(as-if-serial)

      多线程并发有可能会影响最终执行结果

    3>工作内存中修改的副本值没有及时更新到主内存

    4>以上三个问题都可以由synchronized解决

4)synchronized和volatile区别

   1>volatile没有加锁,比synchronized更加轻量级

   2>volatile只能保证线程可见性,synchronized能保证可见性和原则性

   3>synchronized比volatile应用范围更广

七.线程生命周期(线程状态)

1.新建状态

线程对象刚被创建出来,还没有调用start()方法

2.就绪状态(可运行状态)

线程随时可以获取cpu去执行他的任务

1)新建状态的线程调用了start()方法

2)线程sleep()休眠结束或被其他线程提前打断休眠

3)处于wait()等待的线程被其他线程notify()唤醒

4)IO阻塞结束

3.运行状态

就绪状态的线程获得cpu,正在cpu中调用run()方法执行他的任务

4.阻塞状态(等待状态,不一定会有)

线程在阻塞期间不会获得cpu,阻塞结束回到就绪状态

1)线程sleep休眠

2)当前线程被其他线程插入

3)线程wait等待

4)IO阻塞

5.死亡状态(销毁状态)

线程在cpu中执行完他的任务就会被操作系统回收

八.线程池ThreadPool

1.什么是线程池

 一种用于创建和管理若干个线程的容器对象,当我们的程序需要线程来执行任务时,可以直接从线

 程池中取出线程,执行完成后线程不销毁而是回到线程池中,可以提高程序执行效率(空间效率,

 时间效率),避免了重复创建和销毁线程造成资源消耗过多,导致系统崩溃

2.线程池种类

 1)创建单个线程的线程池

 2)创建固定数量线程的线程池

 3)创建可缓存线程池,根据任务数创建线程,但是可以将空闲时间过长的线程回收

3.主要方法

 execute(Runnable task)   将任务交给线程池

 shutdown()               安全关闭,等所有任务执行完成后关闭

 shutdownNow()            立即关闭,返回未完成任务

九.使用Callable创建线程

使用步骤

1.写一个类实现Callble接口,该类代表一个任务,泛型为任务执行结果类型,并重写方法

2.将该任务对象交给线程池执行,并将任务结果封装到Future类中

3.从Future类中取出任务结果

Runnable和Callable有什么区别

1)Runnable执行任务方法为run(),没有返回值,也不能抛出异常

2)Callable执行任务方法为call(),可以带返回值,也可以抛出异常

十.任务调度

借助timer类来执行一个延迟性或周期性任务

1>执行延迟性任务

 timer.schedule(TimerTask调度任务,延迟时间)

2>执行周期性任务

 timer.schedule(TimerTask调度任务,延迟时间,执行周期)

3>取消调度任务

 timer.cancel();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值