Java多线程学习总结

1. JVM启动时,除了我们编写的主线程main外,自身还启动了其他线程,如gc垃圾回收线程等。

2.线程之间的切换是cpu寻址的随机过程,该过程与时间片有关。

3.当一个进程中的全部线程都运行完毕后,JVM退出。

4.创建线程的两种方式:

    4.1 方式一:继承Thread类,并覆盖Thread类中的run方法。直接创建该子类对象。并调用子类的start方法。(ps:线程的默认名称,主线程:main;其他线程:从0开始编号,依次叫做Thread-0,Thread-1……以此类推。Thread类中提供了构造函数Thread(String name)来更改初始的线程名称)

    4.2 方式二:实现Runnable接口,并覆盖Runnable接口中的run方法。通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。调用Thread对象的start方法运行线程。

    4.3 对Runnable接口的理解:Runnable接口实际上是线程类提取出的,封装成接口形式的线程任务。一般我们以方式二的形式创建线程。从语义上更加符合逻辑(毕竟将某一任务extend Thread后,该任务就成为Thread体系了,而继承Thread仅仅是为了实现任务的并发处理,所以这显然不合理)。规避了java单继承的局限性。

5. 线程安全

    5.1 线程安全问题产生的原因:多个线程在操作共享数据(共享资源)。且操作共享数据的线程代码有多条。

    5.2 线程安全的解决:

        5.2.1 同步代码块:synchronized(obj){} (注意:同步的前提是多个线程使用同一把“锁”)

        5.2.2 同步函数:原理与同步代码块类似,写法上在函数声明中加入synchronized修饰符即可,非静态同步函数的锁为调用该函数的当前对象(即this)。静态同步函数的锁为该函数所属的字节码文件对象(即类名.class或this.getClass())。

        5.2.3 单例设计模式中的线程同步问题:问题出现在懒汉式中。

            解决方案1:将静态函数getInstance()声明为同步函数。(效率偏低,因为每个线程执行该方法时都需判断锁)

          解决方案2:在静态函数getInstance()内部创建同步代码块,并在同步代码块之前,多加入一次判断:if(s==null)                                 synchronized(Single.class){ ... } return s;

    5.3 死锁:常见情景之一:synchronized代码块之间的嵌套。(两个同步代码块中有两把锁locka,lockb。线程1持有locka去访问lockb,线程2持有lockb去访问locka,此时两个线程各自持有对方想要的锁,导致两个线程均被阻塞(冻结))。

6.线程间通信:等待-唤醒机制。

    wait():超类Object中的方法,故使用时应用所在的锁对象进行调用。功能为使当前线程进入阻塞状态(该线程此时被存入线程池中)。使用时需要解决InterruptedException中断异常。注意!线程wait()之后又被notify()唤醒,其将从wait()代码之后开始执行。因此,若该线程在唤醒后有再次判断标记的需求,则应将标记的判断语句if(flag) this.wait();改为循环语句while(flag) this.wait();

    notify():超类Object中的方法,故使用时应用所在的锁对象进行调用。功能为唤醒当前监视器的线程池中的任意一个线程。

    notifyAll():超类Object中的方法,故使用时应用所在的锁对象进行调用。功能为唤醒当前监视器的线程池中的全部线程。很常用,使用这种方法可以避免notify()产生的唤醒随机性。

    JDK 1.5版本后对锁和监视器进行了封装。定义了 Lock接口及其子类、Condition监视器对象等。(在java.util.concurrent.locks包中)

        事例:Lock lock = new ReentrantLock();//创建互斥锁对象

                 Condition cond1 = lock.newCondition();//在该锁上创建一组监视器。

                 Condition cond2 = lock.newCondition();//可以在一把锁上创建多个监视器。方便对不同任务的线程进行管理。

                 lock.lock();//获取锁,进入同步

                 lock.unlock();//释放锁,跳出同步。注意!常将其定义在finally代码块中以保证锁得到及时释放。

                 cond1.await();//即wait()

                 cond1.signal();//即notify()

                 cond2.signalAll();//即notifyAll()

    线程通信经典案例:生产者、消费者

7. 终止线程的方法:

    一:stop():不安全,已被淘汰。(suspend()亦是如此)

    二:run方法的结束:而实际上为了满足线程的处理需要,run方法往往是一直运行的。因此若想让线程正常结束,就必须用标记来控制run方法内部的while语句。让其在满足标记时跳出循环。从而结束run方法。

    三:线程被阻塞如何正常终止:配合interrupt()函数(Thread类中的方法,使用时需要用线程对象进行调用)。该函数的功能为:将被wait()方法,sleep()方法,join()方法所阻塞的线程强制唤醒,注意!在使用interrupt方法唤醒线程是会抛出InterruptedException异常的,需要对其进行throws或catch。

8. 其他注意事项:

    8.1 wait()和sleep的区别。

        wait():Object超类中的方法。在多线程中用锁去调用;调用后,线程释放执行权,且释放锁。

        sleep():Thread类中的静态方法。可直接Thread.sleep()进行调用;调用后,线程释放执行权,但不释放锁。

    8.2 setDaemon(true):用线程对象名.setDaemon(true)调用 且 要在线程start前调用。功能为:将该线程设置为守护线程(后台线程)。这种线程的特点为:当进程中只剩下守护线程处于运行状态时,虚拟机会结束运行。该线程也随之释放。

    8.3 join():用线程对象名.setDaemon()调用(且要在线程开始后调用)。功能为:调用后,让子线程一直处于阻塞状态(挂起),直至父线程结束后。才获得执行资格。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值