Java教程

Java 多线程

本文主要是介绍Java 多线程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

线程与进程

  进程:

    是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间。

  线程:

    是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少一个线程。

    线程实际上是在进程基础之上的进一步划分,一个进程启动后,里面的若干执行路径又可以划分成若干个线程。

线程调度

  分时调度

    所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。可以理解为排队使用,一个使用结束下一个再接着使用。

   抢占式调度

    优先让优先级高的线程使用CPU,如果现场层的优先级相同,那么会随机选择一个(线程随机性),Java使用的就是抢占式调度

    CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核而言,某个时刻,只能执行一个线程,而CPU的在多个线程间切换速度相对我们的感觉很快,看上去就是像在同一时刻运行。其实,多线程程序并不能提高程序的运行速度,但是能够提      

  高程序运行效率,让CPU的使用率更高。

同步和异步

  同步:

    排队执行,效率低但是安全

  异步:

    同时执行,效率高但是数据不安全。

 

并发与并行

  并发:

    指两个或多个事件在同一时间段内发生

  并行:

    指两个或多个事件在同一时刻发生(同时发生)

创建一个线程的两种方法

  继承Thread

  实现Runnable

  都需要实现run方法

实现Runnable与继承Thread相比有如下优势:

  1. 通过创建任务,然后给线程分配的方式来实现的多线程,更适合多个线程同时执行相同任务的情况

  2. 可以避免单继承所带来的局限性

  3. 任务与线程本身是分离的,提高了程序的健壮性

  4. 后续学习的线程池技术,接受Runnable类型的任务,不接受Thread类型的线程

Thread类

  线程是程序中执行的线程。Java虚拟机允许应用程序同时运行多个执行线程。

  每个线程都有优先级。具有较高优先级的线程优先于具有较低优先级的线程执行。每个线程可能也可能不会被标记为守护进程。当在某个线程中运行的代码创建一个新的Thread对象时,新线程的优先级最初设置为等于创建线程的优先级,并且当且仅当创建线程是守护进程时才是守护进程线程。

 

设置和获取线程名称

  Thread thread = new Thread(Runnable target,String name)  

  Thread.currentThread().getName()

线程休眠

  Thread.sleep()

线程阻塞

  就好比需要用户从控制台输入数据,如果用户一直不输入,那么等待用户输入的线程就一直处于阻塞状态

线程的中断

  一个线程是一个独立的执行路径,他是否应该结束应该由其自身决定

  以前直接调用stop方法让其线程直接死亡,但是这个方式是不合理的,现在已经不能用了,现在采取给线程一个中断标记。来告知线程应该结束了。

  给线程t1添加中断标记   t1.interrupt();

  当线程在执行的时候就会去发现这个标记,然后由程序员决定是否杀死这个线程,需要结束的话就直接return 

守护线程

  线程:分为守护线程和用户线程

  用户线程:当一个进程不包含任何的存活的用户线程时,进程结束

  守护线程:用于守护用户线程,当最后一个用户线程结束时,所有守护线程自动死亡

  在开启线程之前使用t1.setDaemon(true)将线程设置为守护线程

线程安全问题

  1.同步代码块(简单理解为,同步代码块中是排队进行的)

    格式:synchronized(锁对象){

 

    }

    看同一把锁,不然也达不到线程安全的问题

  2.同步方法

    也就是将需要排队的地方,放到一个方法中吗,然后去对这个方法加锁

  3.显示锁 Lock 子类 ReentrantLock

    同步代码块和同步方法都属于隐式锁

显示锁Lock和隐式锁synchronized的区别

   一、层面不同

    synchronized:Java中的关键字,是由JVM来维护的,是JVM层面的锁。

      synchronized底层是通过monitorenter进行加锁

      底层是通过monitor对象来完成的,其中的wait/notify等方法也是依赖于monitor对象的。并且只有在同步块或同步方法中,JVM才会调用monitory对象的,才可以调用wait/notify等方法

      通过monitorexit来退出锁

    Lock:是JDK5以后才出现的具体的类。使用lock是调用对应的API,是API层面的锁。

      lock是通过调用对应的API方法来获取锁和释放锁的。

   二、使用方式不同

    synchronized

      程序能够自动获取锁和释放锁。Sync是由系统维护的,如果非逻辑问题的话,不会出现死锁。

    Lock

      需要手动的获取和释放锁。如果没有释放锁,就有可能导致出现死锁的现象。
      手动获取锁方法:lock.lock()。释放锁:unlock方法。并且需要配合tyr/finaly语句块来完成

   三、等待是否可中断

    synchronized

      不可中断,除非抛出异常或者正常运行完成。

    Lock

    可以中断的。
    中断方式:

      调用设置超时方法tryLock(long timeout ,timeUnit unit)

      调用lockInterruptibly()放到代码块中,然后调用interrupt()方法可以中断

   四、加锁的时候是否可以设置成公平锁

    synchronized

      只能为非公平锁

     Lock

      两者都可以的。默认是非公平锁

      在其构造方法的时候可以传入Boolean值。true:公平锁、false:非公平锁

   五、锁绑定多个条件来condition

    synchronized

      不能精确唤醒线程。要么随机唤醒一个线程;要么是唤醒所有等待的线程。

     Lock

      用来实现分组唤醒需要唤醒的线程,可以精确的唤醒。

   六、性能区别

    synchronized

      托管给JVM执行,Java1.5中,由于需要调用操作接口,可能导致加锁消耗时间过长,与Lock性比性能低。1.6以后,语义定义更加清晰,有适应自旋、锁粗化、锁消除、轻量级锁、偏向锁等,可进行许多优化,性能提高了,与Lock差不多。

     Lock

      java写的控制锁的代码,性能高

 

公平锁和不公平锁

  公平锁:谁先排队,谁先得到

  不公平锁:所有线程共同去抢锁,不存在先到先得

 

线程死锁

  比如线程A在等B,线程B也在等A,那么就会出现死锁

 

多线程通信问题

  举例:生产者和消费者

    保证生产者在生产的时候,消费者没有在消费,消费者在消费的时候生产者没有在生产

 

线程的六种状态

  

 

带返回值的线程Callable

  

  Runnable与Callable的不同点   Runnable没有返回值;Callable可以返回执行结果   Callable接口的Call()允许抛出异常,Runnable的run()不能抛出   相同点   都是接口   都可以编写多线程程序   都采用了Thread.start()启动线程   Callable获取返回值   Callable接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞  

线程池概述( Executors )

  如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程 就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容 器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。

线程池的好处

  降低资源消耗

  提高响应速度

  提高线程的可管理性

  

Java中的四种线程池 . ExecutorService

  一、缓存线程池

    获取一个缓存线程池

      ExecutorService service = Executors.newCachedThreadPool(); 

    执行流程:       1.判断线程池是否存在空闲线程       2.存在则使用       3.不存在,则创建线程,并放入线程池,然后使用     加入任务方法:       service.execute(任务)   二、定长线程池     创建定长线程池,长度为2,长度可以自由变动

      ExecutorService service = Executors.newFixedThreadPool(2);  

    执行流程:

      1.判断线程池是否存在空闲线程

      2.存在则使用

      3.不存在空闲线程,且线程池未满的情况下,则创建线程,并放入线程池,然后使用 

      4.不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程

     加入任务方法:

      service.execute(任务)   三、单线程线程池     创建单线程线程池

      ExecutorService service = Executors.newSingleThreadExecutor(); 

      也可以使用创建定长线程池,创建的时候传入数值1,效果一致

    执行流程:

      1.判断线程池的那个线程是否空闲

      2.空闲则使用

      3.不空闲,则等待,池中的单个线程空闲后使用

·  四、周期性任务定长线程池

     ScheduledExecutorService service = Executors.newScheduledThreadPool(2) 

    周期任务 定长线程池

      执行流程:

        1.判断线程池是否存在空闲线程

        2.存在则使用

        3.不存在空闲线程,且线程池未满的情况下,则创建线程,并放入线程池,然后使用

        4.不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程

    定时执行

      

      周期执行

     

 

 

 

这篇关于Java 多线程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!