Java多线程
概念
进程
进程是程序的一次动态执行过程
线程
比进程更小的执行单位,是进程中的一个单独的顺序控制流。
它是进程中的独立运行的子任务
线程拥有共享的进程资源
Java的多线程
并发
Java虚拟机允许应用程序同时执行多个执行线程,这在Java种叫做并发。
创建线程的方法
- 通过继承Thread创建线程
- 通过实现Runnable接口来创建线程
- 通过实现Callable接口和Future接口来间接创建线程
1.继承Thread创建线程
构造方法

常用方法

注意
-
join()是调用A线程来帮助完成任务 -
join(long millis)参数的意思是设置一个等待时间,超过这个时间就不再等待。 -
通过
start()启动线程后,系统自动调run()方法来执行线程
2.实现Runnable接口创建线程
因为java只能继承一个类,所以通过继承
Thread的方法创建线程的方法不能再继承其他类,不适合资源共享
所以可以用到Runnable接口来创建线程,并且可以继承其他线程。
-
通过覆盖接口
Runnable接口run()方法来创建线程体。 -
需要通过
Tread包装Runnable实现对象来创建线程,因为除了Thead其他方法种没有start()方法,没法启动线程。
3.实现Callable和Future创建线程
前两个方法都不能获取线程执行结果,JDK1.5以后就可以通过这两个接口来获取线程返回的结果
Callable泛型接口
接口中只有一个call()方法 ,他是线程执行体,具有返回值。
Future泛型接口
作用
表示异步任务,用于对具体的Runnable或者Callable任务执行结果进行中断,查询是否完成,获取结果。
方法

FutureTask类
它是
RunnableFuture<V>泛型接口的实现类,而RunnableFuture<V>是Runnable和Future接口的子接口所以这个类可以作为
Runnable被线程执行,同时也可以作为Future得到Callable的返回值
构造方法
| 方法 | 作用 |
|---|---|
FutureTask(Callable<T>callable) |
在运行时执行给定的Callable |
FutureTask(Runnable runnable,V result) |
在运行时执行给定的Runnable,并安排get将在成功完成以后返回给定结果 |
- 通过包装
Callable来创建对象 - 需要通过
Thread包装来执行线程 - 可以通过
FutureTask对象的get()来获取call()的执行结果
主线程
java程序至少有一个主线程,线程名为main,系统会为它自动分配ID,优先级为5,它不需要run()方法
线程的执行顺序
线程随机启动,取决于操作系统,不取决于代码start()顺序。
线程的状态及控制
线程的状态
java中将线程分为6个状态,状态封装在
ThreadState枚举中,这代表虚拟机的状态
线程在操作系统中执行的状态,线程可以分为7种状态
-
新建态(NEW)
创建线程但没有调用
start() -
就绪态也可称为可运行态(RUNABLE)
调用
start()方法以后 -
运行态
分配到CPU之后的状态
-
等待状态
调用
wait()方法线程就进入等待状态- 需要调用
notify()或者notifyAll()才能被唤醒 notifyAll()能唤醒所有线程
- 需要调用
-
休眠状态
调用
sleep()方法以后 -
阻塞状态
线程在运行状态下发出输入/输出请求
-
死亡状态
run()方法执行玩完毕
线程控制
-
线程启动--
start()方法 -
线程休眠--
sleep()方法 -
线程让步--
yield()方法让当前线程放弃当前的cpu资源,让给其他任务去执行,但是放弃的时间不确定。
-
线程等待--
wait()方法常用于线程同步
-
线程唤醒--
notify()、notifyAll()方法 -
暂停线程使用suspend()来暂停线程,使用resume()方法来恢复线程,但是方法不安全,JDK8以后停用 -
线程加入--
join()方法- 使它调用的线程结束时,
join()所在的线程再继续。 - 如果再主方法种使用该方法,那么主方法会最后执行
- 使它调用的线程结束时,
-
线程状态检查--
isAlive()方法 -
结束线程
Thread.stop()方法,但是不推荐用,已经被作废以下有3种结束正在运行的线程方法
- fs
- dsa
- 中断线程,使用
interrupt()来中断
线程优先级
优先级的等级
优先级的修改
| 方法 | 方法 |
|---|---|
public final void setPriority(int new Priority) |
设置优先级 |
| 获取优先级 |
守护线程
守护线程的判断
public final boolean isDaemon()
设置守护线程
public final void setDaemon(boolean on)
-
如果
on为true那么设置为守护线程 -
如果
on为false那么取消设置守护线程
线程组
可以把一组线程作为单个对象进行统一处理和维护,可以对所有线程进行同时操作
构造方法
| 方法 | 作用 |
|---|---|
public ThreadGroup(String name) |
创建名为name的线程组对象 |
public ThreadGroup(ThreadGroup parent,String name) |
创建具有给定父组和名称的线程组对象 |
常用方法
线程同步
加入同步锁来避免在线程调用没有结束之前再被其他线程调用,保证数据准确性和唯一性
同步块
将
synchronized关键字写在某段代码块之前,可以让代码块加上线程锁
格式
synchronized(object){
//代码块
}
object是对象锁,如果在类中代码段进行同步,可以使用this
同步方法
使用
synchronized关键字修饰方法,内置锁(java每个对象都有一个)可以保护整个方法。
内置锁默认为
this
格式
[修饰符]synchronized<返回类型>方法名([参数列表]){
//方法体
}
同步锁
Lock接口
方法
ReentrantLock实现类
他是
Lock接口的实现类,继承并增加了方法
- 创建锁的时候创建为final对象
构造函数
增加方法
步骤
- 在类中创建锁对象
- 在代码前对锁对象执行
lock()方法 - 在
finally子句中对锁对象执行unlock()方法
线程通信
线程之间可以共享资源和数据,这需要Java中的一些机制来保证线程之间的协调运行
等待唤醒模式通信(wait-notify)
在同步机制下,线程使用了
wait()以后就放弃运行资格,处于等待状态。通过调用nitify()来唤指定等待线程(并不能确定唤醒哪个线程,这个由操作系统决定),通过这两个方法实现了线程之间的通信
说明
- 这种通信模式用来同步机制中,因为只有同步才有锁。
- 使用时必须标识他们锁操作线程持有的锁。
例子(生产者-消费者)

加锁机制下的通信模式(Lock+Condition)
JDK1.5之后
Condition是一个接口,将wait,notify,notifyAll替换成了Condition接口对象,用Lock代替了synchronized方法和语句的使用。
通过
Lock对象调用newCondition()方法获得Condition对象,通过Condition对象的await()、signal()、signalAll()方法来实现线程通信
获取Condition对象
Condition 对象名=Lock对象.newCondition();
Condition接口中的方法

await()/signal()必须在lock.lock()和lock.unlock()之间Condition和Object的wait/notify的对应关系await()---wait()signal()---notify()signalAll()---notifyAll()
- 一个
Lock对象上可以有多个Condition对象
线程通信的理解
- 每个对象都有一个
monitor,任意对象都有,可以理解为一把锁,所以可以任意创造一个对象个两个对象通信。 - 如果想要调用这个对象的
wait()方法,必须获得这把锁。 - 如果调用这个对象的
wait()方法,那么当前线程就会让出这个对象的monitor,然后进入等待状态。 notify()可以唤醒一个正在等待monitor的线程,但是只能给一个线程,这个由操作系统决定。notifyAll()可以唤醒所有等待monitor的线程,但也只有一个线程能够得到对象的monitor,也是由操作系统决定的- 唤醒不能立刻获得对象的
monitor,只有线程退出同步块或者取消了lock才能得到锁 - 多个线程通信可能发生假死,比如生产者唤醒生产者,消费者唤醒消费者,这时候将唤醒改为
notifyAll()即可

浙公网安备 33010602011771号