什么是线程之间的通信
多线程之间通讯,其实就是多个线程在操作同一个资源,但是操作的动作不同。
实现多线程直接的通讯
wait()、notify、notifyAll()方法
- wait()、notify()、notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态。
- 这三个方法最终调用的都是jvm级的native方法。随着jvm运行平台的不同可能有些许差异。
- 如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。
- 如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。
- 如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。
注意:一定要在线程同步中使用,并且是同一个锁的资源
wait()和notify()的使用示例
package top.nightliar.study.day04;
/**
* 生产者,消费者(生产者生产一个,消费者消费一个,交替循环)
* Created by Nightliar
* 2018-09-13 15:35
*/
public class ThreadDemo01 {
public static void main(String[] args) {
Res res = new Res();
new InPut(res).start();
new OutPut(res).start();
}
}
/**
* 生成者,重新定义res对象的信息
*/
class InPut extends Thread{
private boolean fl = false;
private Res res;
public InPut(Res res) {
this.res = res;
}
@Override
public void run() {
while (true) {
synchronized (res) {
if (res.flag){
try {
res.wait(); // res为两个共享的锁资源
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (fl){
res.name = "张三";
res.sex = "男";
fl = false;
}else {
res.name = "小红";
res.sex = "女";
fl = true;
}
res.flag = true;
res.notify();
}
}
}
}
/**
* 消费者,输出res信息
*/
class OutPut extends Thread{
private Res res;
public OutPut(Res res) {
this.res = res;
}
@Override
public void run() {
while (true) {
synchronized (res) {
if (!res.flag){
try {
res.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(res.name + "," + res.sex);
res.flag = false;
res.notify();
}
}
}
}
class Res {
public boolean flag;
public String name;
public String sex;
}
Lock的用法
Lock 接口与 synchronized 关键字的区别
- Lock 接口可以尝试非阻塞地获取锁 当前线程尝试获取锁。如果这一时刻锁没有被其他线程获取到,则成功获取并持有锁。
- Lock 接口能被中断地获取锁 与 synchronized 不同,获取到锁的线程能够响应中断,当获取到的锁的线程被中断时,中断异常将会被抛出,同时锁会被释放。
- Lock 接口在指定的截止时间之前获取锁,如果截止时间到了依旧无法获取锁,则返回。
Lock的写法
Lock lock = new ReentrantLock();
lock.lock();
try{
//可能会出现线程安全的操作
}finally{
//一定在finally中释放锁
//也不能把获取锁在try中进行,因为有可能在获取锁的时候抛出异常
lock.ublock();
}
Condition用法
Condition的功能类似于在传统的线程技术中的,Object.wait()和Object.notify()的功能。
Condition condition = lock.newCondition();
res. condition.await(); 类似wait
res. Condition. Signal() 类似notify
使用Condition代替wait和notify
package top.nightliar.study.day04;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 生产者,消费者(生产者生产一个,消费者消费一个,交替循环)
* Created by Nightliar
* 2018-09-13 15:35
*/
public class ThreadDemo02 {
public static void main(String[] args) {
Res2 res = new Res2();
Condition condition = res.lock.newCondition();
new InPut2(res, condition).start();
new OutPut2(res, condition).start();
}
}
/**
* 生产者,重新定义res对象的信息
*/
class InPut2 extends Thread{
private boolean fl = false;
private Res2 res;
private Condition condition;
public InPut2(Res2 res, Condition condition) {
this.res = res;
this.condition = condition;
}
@Override
public void run() {
while (true) {
try {
res.lock.lock();
if (res.flag){
condition.await();
}
if (fl){
res.name = "李四";
res.sex = "男";
fl = false;
}else {
res.name = "小红";
res.sex = "女";
fl = true;
}
res.flag = true;
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
res.lock.unlock();
}
}
}
}
/**
* 消费者,输出res信息
*/
class OutPut2 extends Thread{
private Res2 res;
private Condition condition;
public OutPut2(Res2 res, Condition condition) {
this.res = res;
this.condition = condition;
}
@Override
public void run() {
while (true) {
try {
res.lock.lock();
if (!res.flag){
condition.await();
}
System.out.println(res.name + "," + res.sex);
res.flag = false;
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
res.lock.unlock();
}
}
}
}
class Res2 {
public boolean flag;
public String name;
public String sex;
public Lock lock = new ReentrantLock();
}
锁的类型
- 可重入锁:在执行对象中所有同步方法不用再次获得锁。
- 可中断锁:在等待获取锁过程中可中断。
- 公平锁: 按等待获取锁的线程的等待时间进行获取,等待时间长的具有优先获取锁权利。
- 读写锁:对资源读取和写入的时候拆分为两个部分处理,读的时候可以多线程一起读,写的时候必须同步地写。
笔试题
- lock锁与synchronized同步锁的区别?
- Java的关键字,在jvm层面上,lock是一个接口。
- lock锁手动上锁,手动释放,灵活性高,synchronized自动上锁和解锁。
- synchronized线程执行发生异常,jvm会让线程释放锁,lock在finally中必须释放锁,不然容易造成线程死锁。
网友评论