Java高级编程——多线程

目录

1.1 程序、进程、线程的基本概念

1.2 线程的创建和使用

1.3 线程的生命周期

1.4 线程的同步

1.5  线程的通信


1.1 程序、进程、线程的基本概念

  • 程序:一段静态的代码
  • 进程:程序的一次执行过程。进程是动态的,有自身的创建、运行到消亡。一个进程可以有多个线程。
  • 线程:进程可进一步细化为线程,是一个程序内部的一条执行路径。多个线程共享进程的堆和方法区,但每个线程有自己的程序计数器、虚拟机栈、本地方法栈。


1.2 线程的创建和使用

(1)方式一:继承于Thread类

* 1.创建一个继承于Thread类的子类

* 2.重写Thread类的run()-->将此线程执行的操作声明在run()中

* 3.创建Thread类的子类的对象

* 4.通过此对象调用start()

(2)方式二:实现Runnable接口

* 1.创建一个实现了Runnable接口的类

* 2.实现类去实现Runnable中的抽象方法:run()

* 3.创建实现类的对象

* 4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象 Thread t1 = new Thread(实现类);

* 5.通过Thread类的对象调用start()

(3)方式三:实现Callable接口(JDK 5.0新增)

* 1.创建一个实现Callable的实现类

* 2.实现类去实现Callable中的抽象方法:call()

* 3.创建实现类的对象

* 4.将此Callable接口实现类的对象作为传递到FutureTask构造中,创建FutureTask的对象

* 5.将FutureTask类的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()

* 6.获取Callable中call()方法的返回值——get()返回值即为futureTask构造器参数Callable实现类重写的call()的返回值

(4)方式四:使用线程池

* corePoolSize:核心池的大小

* maximumPoolSize:最大线程数

* keepAliveTime:线程没有任务时最多保持多长时间后会终止

(5)比较创建线程的两种方式继承于Thread类/实现Runnable接口——开发中优先选择实现Runnable接口的方式 。 原因:

1)实现的方式没有类的单继承性的局限性

2)实现的方式更适合来处理多个线程有共享数据的情况

(6)Thread中常用的方法:

* 1.start():启动当前线程;调用当前线程的run()

* 2.run():通常需要重写Thread类中的此方法,将创建的线程执行的操作声明在此方法中

* 3.currentThread():静态方法,返回执行当前代码的线程

* 4.getName():获取当前线程的名字

* 5.setName():设置当前线程的名字

* 6.yield():释放当前CPU的执行权

* 7.join():在线程A中调用线程B的join(),此时线程A就进入阻塞状态,直到线程B完全执行以后,线程A才 * 结束阻塞状态

* 8.stop():已过时。当执行此方法时,强制结束当前线程。

* 9.sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前 * 线程是阻塞状态。

* 10.isAlive():判断当前线程是否存活

* 11.线程的优先级:

         * 1.三个常量

                   * MAX_PRIORITY:10

                   * MIN_PRIORITY:1

                   * NORM_PRIORITY:5 -->默认优先级

        * 2.如何获取和设置当前线程的优先级:

                   * getPriority():获取线程的优先级

                   * setPriority(int p):设置线程的优先级 *

* 说明:高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下 * 被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。


1.3 线程的生命周期

  • 新建:被声明并创建时,即new
  • 就绪:调start()方法后,进入线程队列等待CPU时间片
  • 运行:线程被调度并获得CPU资源,run()方法运行
  • 阻塞:被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行
  • 死亡:线程完成它的全部工作或被提前强制性终止或出现异常导致结束


1.4 线程的同步

在Java中,我们通过同步机制,来解决线程的安全问题。

(1)方式一:同步代码块

synchronized(同步监视器){

          //需要被同步的代码 

}

 说明:1.操作共享数据的代码,即为需要被同步的代码。-->不能包含代码多了,也不能包含代码少了

            2.共享数据:多个线程共同操作的变量

            3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。 要求:多个线程必须要共用同一把锁。Object obj = new Object();obj就可以直接用来当锁。

                    补充:1)  在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器;

                               2)  在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。

(2)方式二:同步方法——操作共享数据的代码完整的声明在一个方法中,即将此方法声明同步的。

                        1)非静态的同步方法:private synchronized void show(){...}  ==》 同步监视器是this

                        2)静态的同步方法:private static synchronize void show(){...}  ==》 同步监视器是当前类本身

(3)方式三:Lock锁(jdk5.0新增)

         Lock需要手动的启动同步(.lock()),同时结束同步也需要手动的实现(.unlock())。

(4)优缺点

         优点:同步的方式,解决了线程的安全问题。

         缺点:操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。

(5)死锁问题

理解:不同的线程分别占用对方需要的同步资源不放弃, 都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。

解决:专门的算法、原则;尽量


1.5  线程的通信

涉及到的三个方法:

* wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器

* notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,就唤醒优先级高的那个。

* notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。

 说明:

* 1.wait()、notify()、notifyAll()三个方法必须使用在同步代码块或同步方法中

* 2.wait()、notify()、notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。 否则会出现IllegalMonitorStateException异常。

* 3.wait()、notify()、notifyAll()三个方法是定义在java.lang.object类中。

面试题:sleep() 和 wait()的异同?

* 1.相同点:一旦执行,都可以使得当前的线程进入阻塞状态

* 2.不同点:1)两个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait()

                    2)调用的要求不同:sleep()可以在任何需要的场景下调用。wait()必须使用在同步代码块或同步方法中

                    3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值