本篇博文主要包含:
- 多线程之间通讯
- synchronized保证多线程安全
- wait、notify、notifyAll方法在synchronized中的用法及解决重复消费问题
- wait与sleep的区别
- Lock锁(手动)
- Lock 接口与 synchronized 关键字的区别
- Lock锁中使用Condition类控制线程的等待与唤醒
- 如何停止线程
- 什么是多线程之间通讯?
多线程之间通讯,其实就是多个线程在操作同一个资源,但是操作的动作不同。例如:第一个线程写入(input)用户,另一个线程取读取(out)用户.实现读一个,写一个操作。
代码演示:
/**
* 共享资源源实体类
*
*/
class People{
String name;
String sex;
}
/**
* 写线程类
*
*/
class OutputThread extends Thread{
People people;
public OutputThread(People people) {
this.people = people;
}
@Override
public void run() {
int count = 0 ;
while(true) {
if(count==0) {
people.name = "方向";
people.sex = "女";
}else {
people.name = "目标";
people.sex = "男";
}
count = (count+1)%2;
}
}
}
/**
* 读线程类
*
*/
class InputThread extends Thread{
People people;
public InputThread(People people) {
this.people = people;
}
@Override
public void run() {
while(true) {
System.out.println(people.name+", "+people.sex);
}
}
}
public class OutInputThread {
public static void main(String[] args) {
//共享people对象
People people = new People();
OutputThread out = new OutputThread(people);
InputThread input = new InputThread(people);
out.start();
input.start();
}
}
运行结果:
由运行结果可以看出,数据发生错乱,造成线程安全问题。
- 加上synchronized关键字
/**
* 共享资源源实体类
*
*/
class People{
String name;
String sex;
}
/**
* 写线程类
*
*/
class OutputThread extends Thread{
People people;
public OutputThread(People people) {
this.people = people;
}
@Override
public void run() {
int count = 0 ;
while(true) {
//解决线程安全问题加上synchronized
synchronized(people) {
if(count==0) {
people.name = "方向";
people.sex = "女";
}else {
people.name = "目标";
people.sex = "男";
}
}
count = (count+1)%2;
}
}
}
/**
* 读线程类
*
*/
class InputThread extends Thread{
People people;
public InputThread(People people) {
this.people = people;
}
@Override
public void run() {
while(true) {
//解决线程安全问题加上synchronized
synchronized(people) {
System.out.println(people.name+", "+people.sex);
}
}
}
}
public class OutInputThread {
public static void main(String[] args) {
//共享people对象
People people = new People();
OutputThread out = new OutputThread(people);
InputThread input = new InputThread(people);
out.start();
input.start();
}
}
运行结果:
线程安全问题解决了,但是消费者在重复消费。如何解决这个问题呢?我们使用wait()、notify()处理这个问题。
- wait()、notify、notifyAll()方法
wait()、notify()、notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态。
这三个方法最终调用的都是jvm级的native方法。随着jvm运行平台的不同可能有些许差异。
(1)如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。
(2)如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。
(3)如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。
注意:一定要在线程同步中使用,并且是同一个锁的资源
代码演示:
/**
* 共享资源源实体类
*
*/
class PeopleWaitNotify{
String name;
String sex;
//
Boolean flag = false;
}
/**
* 写线程类
*
*/
class OutputWaitNotifyThread extends Thread{
PeopleWaitNotify people;
public OutputWaitNotifyThread(PeopleWaitNotify people) {
this.people = people;
}
@Override
public void run() {
int count = 0 ;
while(true) {
//解决线程安全问题加上synchronized
synchronized(people) {
if(people.flag) {
try {
// 当前线程变为等待(从运行状态变为等待状态),但是可以释放锁
people.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(count==0) {
people.name = "方向";
people.sex = "女";
}else {
people.name = "目标";
people.sex = "男";
}
//唤醒其他等待线程(从等待状态变为运行状态)
people.notify();
people.flag= true;
}
count = (count+1)%2;
}
}
}
/**
* 读线程类
*
*/
class InputWaitNotifyThread extends Thread{
PeopleWaitNotify people;
public InputWaitNotifyThread(PeopleWaitNotify people) {
this.people = people;
}
@Override
public void run() {
while(true) {
//解决线程安全问题加上synchronized
synchronized(people) {
if(!people.flag) {
try {
people.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(people.name+", "+people.sex);
people.notify();
people.flag = false;
}
}
}
}
public class WaitNotifyOutInputThread {
public static void main(String[] args) {
//共享people对象
PeopleWaitNotify people = new PeopleWaitNotify();
OutputWaitNotifyThread out = new OutputWaitNotifyThread(people);
InputWaitNotifyThread input = new InputWaitNotifyThread(people);
out.start();
input.start();
}
}
运行结果:
使用wait()、notify()实现消费者重复消费问题,即生产者线程消费一个,消费者线程立马消费; 生产者没有任何生产,消费者不能读;消费者,没有消费完,生产者不能在继续生产。
注意:wait()、notify()及notifyAll()是用在synchronized代码块内的
- wait与sleep的区别
(1) 对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的。
sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态。
(2)在调用sleep()方法的过程中,线程不会释放对象锁。
而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。 - Lock锁
在 jdk1.5 之后,并发包中新增了 Lock 接口(以及相关实现类)用来实现锁功能,Lock 接口提供了与 synchronized 关键字类似的同步功能,但需要在使用时手动获取锁和释放锁。
(1)lock的写法:
Lock lock = new ReentrantLock();
lock.lock();
try{
//可能会出现线程安全的操作
}finally{
//一定在finally中释放锁
//也不能把获取锁在try中进行,因为有可能在获取锁的时候抛出异常
lock.ublock();
}
(2)Lock 接口与 synchronized 关键字的区别
- Lock 接口可以尝试非阻塞地获取锁 当前线程尝试获取锁。如果这一时刻锁没有被其他线程获取到,则成功获取并持有锁。
- Lock 接口能被中断地获取锁 与 synchronized 不同,获取到锁的线程能够响应中断,当获取到的锁的线程被中断时,中断异常将会被抛出,同时锁会被释放。
- Lock 接口在指定的截止时间之前获取锁,如果截止时间到了依旧无法获取锁,则返回。
- synchronized的缺点:效率低、扩展性不好、不能自动化
代码演示:
/**
* 共享资源源实体类
*
*/
class People{
String name;
String sex;
}
/**
* 写线程类
*
*/
class OutputThread extends Thread{
People people;
public OutputThread(People people) {
this.people = people;
}
@Override
public void run() {
int count = 0 ;
while(true) {
//解决线程安全问题加上synchronized
synchronized(people) {
if(count==0) {
people.name = "方向";
people.sex = "女";
}else {
people.name = "目标";
people.sex = "男";
}
}
count = (count+1)%2;
}
}
}
/**
* 读线程类
*
*/
class InputThread extends Thread{
People people;
public InputThread(People people) {
this.people = people;
}
@Override
public void run() {
while(true) {
//解决线程安全问题加上synchronized
synchronized(people) {
System.out.println(people.name+", "+people.sex);
}
}
}
}
public class OutInputThread {
public static void main(String[] args) {
//共享people对象
People people = new People();
OutputThread out = new OutputThread(people);
InputThread input = new InputThread(people);
out.start();
input.start();
}
}
- Condition用法
在synchronized中使用wait()、notify()控制线程的等待与唤醒,而在Lock锁中使用Condition。
Condition的功能类似于在传统的线程技术中的,Object.wait()和Object.notify()的功能。
代码演示:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 共享资源源实体类
*
*/
class PeopleLock{
String name;
String sex;
boolean flag = false;
//在共享资源里定义lock锁
Lock lock = new ReentrantLock();
}
/**
* 写线程类
*
*/
class OutputLockThread extends Thread{
PeopleLock people;
Condition newCondition;
public OutputLockThread(PeopleLock people,Condition newCondition) {
this.people = people;
this.newCondition = newCondition;
}
@Override
public void run() {
int count = 0 ;
while(true) {
try {
//开启锁
people.lock.lock();
if(people.flag) {
try {
// 当前线程变为等待(从运行状态变为等待状态),但是可以释放锁
newCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(count==0) {
people.name = "方向";
people.sex = "女";
}else {
people.name = "目标";
people.sex = "男";
}
count = (count+1)%2;
people.flag= true;
//唤醒其他等待线程(从等待状态变为运行状态)
newCondition.signal();
} catch (Exception e) {
e.printStackTrace();
}finally {
//关闭锁
people.lock.unlock();
}
}
}
}
/**
* 读线程类
*
*/
class InputLockThread extends Thread{
PeopleLock people;
Condition newCondition;
public InputLockThread(PeopleLock people,Condition newCondition) {
this.people = people;
this.newCondition = newCondition;
}
@Override
public void run() {
while(true) {
try {
//开启锁
people.lock.lock();
if(!people.flag) {
try {
// 当前线程变为等待(从运行状态变为等待状态),但是可以释放锁
newCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(people.name+", "+people.sex);
people.flag= false;
//唤醒其他等待线程(从等待状态变为运行状态)
newCondition.signal();
} catch (Exception e) {
e.printStackTrace();
}finally {
//关闭锁
people.lock.unlock();
}
}
}
}
public class LockThread {
public static void main(String[] args) {
//共享people对象
PeopleLock people = new PeopleLock();
//同一个newCondition
Condition newCondition = people.lock.newCondition();
OutputLockThread out = new OutputLockThread(people,newCondition);
InputLockThread input = new InputLockThread(people,newCondition);
out.start();
input.start();
}
}
运行结果:
使用Lock锁和Condition代替synchronized和wait()、notify()。
- 如何停止线程
(1) 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
代码演示:
class StopThread extends Thread{
//使用volatile关键字保证原子可见性
private volatile boolean flag = true;
@Override
public void run() {
System.out.println("子线程运行begin。。。。。");
while(flag) {
}
System.out.println("子线程运行end。。。。。");
}
public void stopThread() {
this.flag = false;
}
}
public class StopThreadDemo {
public static void main(String[] args) {
StopThread stopThread = new StopThread();
stopThread.start();
for(int i=0; i<5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
if(i==3) {
stopThread.stopThread();
}
}
}
}
运行结果:
(2) 使用stop方法强行终止线程(这个方法不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果),不可恢复。
代码演示:
class StopThread extends Thread{
//使用volatile关键字保证原子可见性
private volatile boolean flag = true;
@Override
public void run() {
System.out.println("子线程运行begin。。。。。");
while(flag) {
}
System.out.println("子线程运行end。。。。。");
}
}
public class StopThreadDemo {
public static void main(String[] args) {
StopThread stopThread = new StopThread();
stopThread.start();
for(int i=0; i<5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
if(i==3) {
stopThread.stop();
}
}
}
}
运行结果:
(3) 使用interrupt方法中断线程。
代码演示:
class StopThread extends Thread{
//使用volatile关键字保证原子可见性
private volatile boolean flag = true;
@Override
public void run() {
System.out.println("子线程运行begin。。。。。");
while(flag) {
try {
wait();
} catch (Exception e) {
stopThread();
e.printStackTrace();
}
}
System.out.println("子线程运行end。。。。。");
}
public void stopThread() {
this.flag = false;
}
}
public class StopThreadDemo {
public static void main(String[] args) {
StopThread stopThread = new StopThread();
stopThread.start();
for(int i=0; i<5; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
if(i==3) {
//当前等待的线程直接抛出异常
stopThread.interrupt();
}
}
}
}
运行结果: