线程
线程的创建方式
- 继承Thread类,重写run()方法
- 实现Runnable接口(重要)
- 实现Callable接口
- 线程池
线程的五大状态
- 创建状态
- 就绪状态
- 运行状态
- 阻塞状态
- 死亡状态
线程的分类
|–用户线程
|–主线程(main)
|–子线程
|–守护线程
线程间的通信方式
线程通信主要可以分为三种方式,分别为共享内存、消息传递和管道流。每种方式有不同的方法来实现。
- 共享内存:线程之间共享程序的公共状态,线程之间通过读-写内存中的公共状态来隐式通信。
volatile共享内存
volatile有一个关键的特性:保证内存可见性,即多个线程访问内存中的同一个被volatile关键字修饰的变量时,当某一个线程修改完该变量后,需要先将这个最新修改的值写回到主内存,从而保证下一个读取该变量的线程取得的就是主内存中该数据的最新值,这样就保证线程之间的透明性,便于线程通信。
- 消息传递:线程之间没有公共的状态,线程之间必须通过明确的发送信息来显示的进行通信。
wait/notify等待通知方式
join方式
使用wait()、notify()和notifyAll()需要注意以下细节
-
使用wait()、notify()和notifyAll()需要先调用对象加锁
-
调用wait()方法后,线程状态由Running变成Waiting,并将当前线程放置到对象的等待队列
-
notify()和notifyAll()方法调用后,等待线程依旧不会从wait()返回,需要调用notify()和notifyAll()的线程释放锁之后等待线程才有机会从wait()返回
-
notify()方法将等待队列中的一个等待线程从等待队列中移到同步队列中,而notifyAll()方法则是将等待队列中所有的线程全部转移到同步队列,被移到的线程状态由Waiting变为Blocked。
从wait()方法返回的前提是获得调用对象的锁
- 管道输入/输出流的形式
管道流是是一种使用比较少的线程间通信方式,管道输入/输出流和普通文件输入/输出流或者网络输出/输出流不同之处在于,它主要用于线程之间的数据传输,传输的媒介为管道。
管道输入/输出流主要包括4种具体的实现:PipedOutputStrean、PipedInputStrean、PipedReader和PipedWriter,前两种面向字节,后两种面向字符。
java的管道的输入和输出实际上使用的是一个循环缓冲数组来实现的,默认为1024,输入流从这个数组中读取数据,输出流从这个数组中写入数据,当这个缓冲数组已满的时候,输出流所在的线程就会被阻塞,当向这个缓冲数组为空时,输入流所在的线程就会被阻塞。
线程安全
多个线程对同一个对象进行操作时,会发生线程之间不同步(对象的属性值不同)的情况,成为线程不安全。
同步锁(synchronized)
加同步锁的方法:
- 同步方法:就是在方法前面加上关键字synchronized,即实现了方法的同步。
public synchronized void show(){
//功能代码
}
- 同步对象
public void show(){
synchronized (this){
//功能代码
}
}
线程的三大特性
- 原子性
- 可见性
- 有序性