首页 > 代码库 > 黑马程序员-Java基础-多线程

黑马程序员-Java基础-多线程

第一讲  多线程概述

1、 定义

进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。在程序运行时,会被分配一个内存空间,进程就用于标识这个空间,封装单元,线程才是线程中真正执行的哦部分。

线程:就是进程中的一个独立的控制单元,线程在控制着进程的执行。

一个进程中至少有一个线程。

例子:java JVM 启动时会有一个进程java.exe。该进程中至少一个线程负责java程序的执行,而且这个线程运行的代码存在于main方法中,该线程就称为主线程。

说明:jvm启动时不止一个线程,还有一个负责垃圾回收机制的线程。

2、  多线程

多线程可以使得多个代码“同步”执行。在单核的前提下,多个线程都在获取cpu的执行权,cpu执行到谁,谁就运行。需要明确的是,在某一个时刻,只能有一个线程在运行。

第二讲  创建线程-继承Tread类

1、  创建线程第一种方式:继承Tread类

步骤:

1)定义类继承Tread类;

2)复写Tread类中的run方法;

3)调用线程的start()方法,该方法有两个作用:启动线程、调用run方法;

                      

2、  run和start方法特性

Thread类用于描述线程,该类就定义了一个功能,用于存放线程要运行的代码,该存储功能就是run方法。主线程把运行的代码存储在main方法中。

复写run方法的目的:将自定义的代码存储在run方法中,让线程运行。

3、  练习:创建两个线程,和主线程交替运行

l  定义线程类继承Tread类

l  创建两个线程;

l  启动两个线程;

代码实现如下:

4、  线程运行状态

线程在运行过程中有如下5中状态:创建、运行、冻结、临时状态(阻塞)、消亡。

这几种状态之间的关系如下:

 

5、  线程在虚拟机中的名称

获取线程名称:

获取线程名称格式:线程对象.getName();

线程都有自己默认的名称:Thread-编号,该编号从0开始。

设置线程名称:

setName()或构造函数设置线程名称。

Thread类的构造函数和为线程设置名称,因此,可以在定义线程类时,调用Thread类的构造函数来设置线程的名称。

Thread类中的方法currentThread()可以获取到当前运行的线程对象。

获取和设置线程名称的意义:当线程多的时候,可以对线程进行管理。

 

 

第三讲  创建线程-实现Runnable接口

1、  多个窗口同时卖票例子

创建多个线程,同时执行卖票功能。

利用继承Thread类方法创建线程,实现多个窗口同时卖票,但票的数量必须定义为静态的,否则会出现多卖的情况。然后我们在写代码时,较少定义静态变量,因为静态变量生存周期较长浪费资源。

因此,就引入了第二种创建线程的方式:实现Runnable接口

2、  创建线程的第二种方式——实现Runnable接口

步骤:

1)  定义类实现Runnable接口;

2)  覆盖Runnable接口中的run方法;

目的:将线程要运行的代码存放在该run方法中。

3)  通过Thread类建立线程对象;

4)  将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数;

要将Runnable接口的子类对象传递给Thread类的构造函数。

因为:自定义的run方法所属的对象是Runnable接口的子类对象,所以要让线程去指定指定对象的run方法。即,明确该run方法所属的对象。

5)  调用Thread类的start方法,并开启线程,并调用Runnable接口子类的run方法;

代码实现如下:

实现方式与继承方式的对比的好处

l  实现方式可以避免java单继承的局限性,在定义线程时,建议使用实现方式;

l  资源被独立了出来,解决了多窗口卖票的问题;——这个卖票过程只在内存中创建了一个tick,多个卖票线程只能对这个票资源进行操作。

 

第四讲  多线程安全问题

1、  多线程安全问题概述

问题出现的原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句执行了一部分,还没有执行完,就被另一个线程参与进来执行,导致共享数据的错误。

解决办法:对多条操作共享数据的语句,只能让一个线程执行完了,才能让其他线程参与进来。

         Java中的解决办法:同步代码块

Synchronized(对象)

        {

               需要被同步的代码

}

说明:对象如同锁,持有锁的线程可以在同步中执行,没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。

2、  同步使用

同步使用的前提:必须要有两个或两个以上的线程;必须是多个线程使用同一个锁。

同步的好处:解决了多线程的安全问题;

同步的弊端:多个线程需要判断锁,较为耗费资源;

3、  同步的表现形式

同步代码块

代码示例:

同步函数

4、  同步中的锁

1)         同步代码块的锁可以 是任意的对象;

2)         同步代函数的锁是this(函数所在类的实例对象);

3)         同步静态函数的锁是该类对应的字节码文件对象,类名.class;

 

第五讲  同步在单例设计模式中的应用

1、  概述

单例设计模式有两种体现方式:饿汉式和懒汉式。

因为懒汉式是函数被调用时才会创建对象,而创建对象的语句不止一条,所以在多线程时容易出现安全隐患。

2、  解决懒汉式安全问题方法

将获取对象的函数定义为同步的,如下:

但每次调用该函数时都需要判断锁,影响运行效率,可以通过双重判断形式来稍微提高运行效率,如下。这样,可以减少锁的次数,但代码增多了。

3、  死锁

同步中嵌套同步,而且同步的锁不一样。而且不同线程都在等待对方的锁。

 

 

 

知识点总结

1、  创建线程有两种方式:继承Thread类;实现Runnable接口

实现Runnable接口可以解决多结成问题,还能将资源独立出来,保证资源的唯一性。

2、  多线程的出现使得很多程序可以“同时”运行,但可能会出现共享数据错误,造成多线程安全问题,此时,可以通过同步机制来进行控制,同步包括:同步代码块、同步函数。

3、  使用同步代码块时,需要明确如下问题:

1)  明确哪些代码是多线程运行代码(run方法中的代码);

2)  明确共享数据;

3)  明确多线程运行代码中哪些是操作共享数据的;

4、  懒汉式同步问题

懒汉式的实例是延迟加载的,而且存在多条操作语句,在多线程情况下可能会出现安全问题。此时可以用同步函数或同步代码块来控制,但每次获取实例时都需要判断锁影响效率,我们可以通过双重判断的方式来减少判断锁的次数,从而稍微的提高了运行效率。但实际应用过程中,建议使用饿汉式。

黑马程序员-Java基础-多线程