java 线程通讯道通信就,java多线程间通信图解实例

守护线程

总结

今天主要学习了多线程间的通信问题。

1.     多线程间通信

既多个线程在操作同一个资源,但是操作的动作不同。

39be183a4fe3a39f6a0ae515cfb4df55.png

Figure 1

如图,input和output在同时操作同一个资源,但是他们所做的的动作并不同。

2.     解决多线程通信安全问题

当多个线程不同方法操作同一资源时,会出现数据错乱问题,这就涉及到了

多线程间通信的安全问题。

出现安全问题要用同步,加锁,但是这里不能直接将run方法同步,这样会导致其成为单线程。

d4a8f1d273e40ee59fdb261bb703ca10.png

Figure2

要解决此问题,需要将synchronized定义在run方法内部,只需要将线程操作代码同步即可。示例中(代码省略)有input、output线程,因此需要将其各自内部的操作共同资源的代码同步。(虽然他们(线程操作代码)在两个run方法里面,但是它们在操作同一个资源)。

要用同步解决这类问题,必须满足两个前提:

l  明确是否是多线程;

l  明确是否使用同一个锁。

1、

Output类中的run方法里有操作共同资源的代码,因此要对其进行同步

235595085b552158dc8f5e4b15ef4dc1.png

Figure3

Input类中的run方法里也有操作共同资源的代码,因此也要进行同步。

3b4c364645b9bef81af7ca6d300c57a5.png

Figure4

但是,运行后发现仍然出现数据错乱。

此时需要考虑是否使用同一个锁

2、

使用唯一对象。

可以是某唯一的对象,XXX.class(较牵强)

建议使用这里共同操作的唯一资源,——r

90c7bb56a5041fdb3915c51eb8bd4599.png

Figure5

3、数据私有化(private),只对外提供方法

aab78264df59f86b91f60e8715dc7666.png

Figure6

3.     等待唤醒机制

c0faf5463a056f6d2133d9e8aa9f39ac.png

Figure7

操作线程的方法:wait、notify、notifyAll——都在Object类中。

监视器(也就是锁),只有同步才会有锁,所以这些方法都用于同步当中。

因为要对持有监视器(锁)的线程操作。所以要使用在同步中(只有同步才具有锁)。

由于这些方法在操作同步线程时,都必须要标识他们所操作线程持有的锁,只有同一个锁上的被等待线程,才可以被同一个锁上notify唤醒;不可以对不同锁中的线程进行唤醒,所以这些操作线程的方法要定义在Object类中。

也就是说,等待和唤醒必须是同一个锁。

锁可以是任意对象,可以被任意对象调用的方法定义在Object类中(为什么wait、notify、notifyAll都定义在Object类中)。

线程运行时,内存中会建立一个线程池,等待线程都存放在其中,notify唤醒线程池中的线程。notify通常唤醒第一个被等待的线程。

4.     生产者消费者例子

858656ac659da890155f0b0dd5157818.png

Figure8

if只判断一次;while,每次醒来都判断

While—–

Notifyall—-

为什么要定义while判断标记?

让被唤醒的线程再一次判断标记。

为什么要定义notifyAll()?

为了唤醒对方线程。如果只用notify(),容易出现只唤醒本方线程的情况,导致程序中的所有线程都等待——wait().

b513cc93a765428012b449d50df02c3c.png

Figure9

JDK升级后提供了新的包(packet):Java.util.concurrent.locks(使用Lock之前一定要导入此包)

提供了多线程升级解决方案。未升级之前,一个锁只对应一个wait、notify。

将同步synchronized替换成实现Lock。

将Object中的wait、notify、notifyAll替换成了condition对象。该对象可以通过Lock锁进行获取。

Public interface Lock—->Lock提供了比使用synchronized方法和语句可获得的更广泛的锁定操作。此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的Condition对象。

condition将Object监视器方法(wait/notify/notifyAll)分解成截然不同的对象,以便通过将这些对象与任意的Lock实现组合使用,为每个对象提供了多个等待。

Lock替代了synchronized方法和语句的使用。

Condition替代了Object监视器方法的使用

8aedef67a2289d9ae50f4e58d3166e5e.png

Figure10

privateLock lock = new ReentrantLock();

private Condition con = lock.newCondition();

Reentrant:可重入;函数可以由多于一个线程并发使用,而不必担心数据错误。可重入函数可以在任意时刻被中断,稍后再继续运行,不会丢失数据。

ReentrantLock(重入锁):是一种递归无阻塞的同步机制。

d77b1e7c14443da294f534056d0ef5ff.png

Figure11

获取锁lock.lock();————void lock()____获取锁,如果锁不可用,出于线程调度目的,将禁用当前线程,并且在获得锁之前,该线程将一直出于休眠状态。

释放锁lock.unlock();————void unlock()____

04b79bab30a0ea2d5340dfb2402b0808.png

Figure12

condition.await();等待————void await()

condition.signal();唤醒————void signal()

Void signalAll()唤醒所有等待线程

释放锁的动作一定要执行(finally中的内容一定会执行)

finally { } 里存放的一般是释放资源动作

4688addbdc74c3262d322b884f895bc3.png

Figure13

5ba5b1ee7549ca6610a114ef4521e7ae.png

Figure14

和notifyAll的道理一样,也要使用signalAll()唤醒(且唤醒包含对方)。仅仅是将原来的代码wait、notifyAll替换为了lock、signalAll。

新特性好处:一个锁上可以有多个相关的Condition对象。

升级后,可以定义两个对象。就可以在set()、out()两个方法中调用这两个对象的await、signal方法,来彼此交替唤醒。

bcb3ee6ede8ecb0e59565d5995415b0a.png

Figure15

PrivateCondition condition_pro = lock.newCondition();

Private Conditioncondition_con = lock newCondition();

Public void set(Stringname)throws InterruptedException

{

Lock.lock();

Try{

While(flag)

Condition_pro.await();(生产者等待)

——-;

Flag = true;

Condition_con.signal();(唤醒消费者)

}

Finally{

Lock.unlock();

}

}

Public void out()

{

Lock.lock();

Try{

While(!flag)(先判断是否满足标记){

Condition_con.await();

——–;

Flag = false;

Condition_pro.signal();

}

}

Finally{

Lock.unlock();

}

}

互相唤醒(只唤醒对方)。

5.     停止线程

Stop方法已经过时。有bug,但是没有在api中删除,因为有老程序员在用。

l  如何停止线程:run方法结束(唯一的方法)run方法结束,线程就结束。

开启多线程运行,运行代码通常是循环结构。控制住循环,就可以让run方法结束,线程也就结束(最终原理)。

l  当线程处于了冻结(中断)状态,就不会读取到标记,线程就不会结束。(中断不是停止)

Public void interrupt();

Interrupt方法将处于冻结状态的线程强制恢复到运行状态中来,

强制清除冻结状态

b4316047b642a9ab250dec07ebfbbddc.png

Figure 16

当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除。

强制让线程恢复到运行状态中来,这样就可以操作标记线程结束。

Thread类提供该方法interrupt();——sleep、wait、join都能被中断。

6.     守护线程

Public final void       setDaemon(boolean on)

将该线程标记为守护线程或用户线程,当正在运行的线程都是守护线程时,jvm退出。(该方法必须在启动线程前调用)

e530044042f0a3219a8442c6959b1379.png

Figure 17

两个线程都是守护线程时:

可以看到java虚拟机退出。

33da992a1cfc9e933c2e4910403d55d4.png

Figure 18

当所有的前台线程都结束后,后台自动结束运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值