Java---多线程03:线程间通信
线程是操作系统中的独立个体,而通信使他们成为一个整体,拥有了协作的可能。
这一篇只要有以下几个部分要梳理出来:
- 使用wait/notify实现线程间的通信
- 方法join的使用
- ThreadLocal的使用
1. wait/notify的使用
在不使用这两个关键字的时候,我们可以思考一下这种通信方式:
存在A,B两个线程,A在做一个递增操作,B做一个While死循环判断操作,当线程A执行到5后,线程B判断发现size=5了,于是线程B执行里面的语句。
这就是线程间通信中一个简陋且有弊端的通信方式,要通过While不断循环。
而wait/notify关键字就是为了解决这种情况,实现通信。
方法 | 作用 |
---|---|
wait | 等待,使当前执行线程处在等待状态(“预执行序列”),会在wait()所在代码行出停止执行,直到被通知或者被中断,该方法是Object类的方法。 |
notify | 通知,用来通知可能等待该对象对象锁的线程,并且让他获得该对象的锁 |
上代码演示一下:
//mythread02
private Object lockObject;
public MyThread02(Object lockObject){
this.lockObject=lockObject;
}
@Override
public void run(){
synchronized (lockObject) {
for (int i = 0; i < 10; i++) {
if (i==5) {
lockObject.notify();
System.out.println("发出通知");
}
System.out.println(i);
}
}
}
//Mythread
private Object lockObject;
public Mythread(Object lockObject){
this.lockObject=lockObject;
}
@Override
public void run(){
try {
synchronized (lockObject) {
System.out.println("wait begin"+System.currentTimeMillis());
lockObject.wait();
System.out.println("wait end"+System.currentTimeMillis());
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//主要类:
Object lockObject = new Object();
Mythread mythread =new Mythread(lockObject);
mythread.setName("001");
MyThread02 mythread2 =new MyThread02(lockObject);
mythread2.setName("002");
Thread mythread01 =new Thread(mythread);
mythread01.start();
Thread.sleep(50);
Thread mythread02 =new Thread(mythread2);
//测试结果:
wait begin1520172946893
0
1
2
3
4
发出通知
5
6
7
8
9
wait end1520172946941
注意看:在发出通知后,等到notify的线程执行完了,wait所在的才继续执行。
其实wait(),notify()是个很简单的概念,这里就不过分的细说了,直接说说一些要点。
- wait(),notify()必须获得对象锁,即只能在同步方法或者同步代码块中使用
- 使用wait()后,当前线程会释放锁
- 在从wait()返回前(即继续执行时),线程会和其他线程竞争锁
- 在notify()通知后,当前线程并不会马上获得锁,等notify()的线程执行完,线程才会释放锁,此时其他线程才会竞争锁
- notify()的唤醒是随机唤醒,是从处在等待该锁的线程里面唤醒一个。
- notifyAll()是唤醒所有的线程,然后,同时竞争,优先级最高的最先执行。
- interrupt()方法碰到wait()方法会出现异常
- wait(long)方法用于等待某一时间内是否有线程对锁进行唤醒,否则自动唤醒
- 注意不要通知过早,打乱程序正常的运行逻辑
2- 线程的阻塞,就绪,等待
- 新建一个线程后,调用其start()方法,系统会为此线程分配CPU资源,使其处在可运行状态(就绪状态),直到线程抢到CPU资源,即可处在运行状态。
- 运行与可运行时相互切换的,这涉及到CPU资源的合理分配。以下五种情况会使线程进入可运行状态(就绪状态)
1.调用sleep()后超过了设定的时间
2.线程调用的阻塞IO已经返回,阻塞方法已经执行完毕
3.线程成功的获得了试图同步的监视器
4.线程正在等待通知,而通知此时发布
5.处于挂起的线程调用了resume()方法 - 暂停(阻塞):暂停有以下几种可能出现
1.线程调用了sleep方法,自动放弃资源
2.线程调用了阻塞式IO,返回前不可获得资源
3.线程试图获得一个对象监视器,但是,别人在用
4.线程等待某个通知
5.suspend()方法挂起 - -
3 - join()方法的使用
方法join的作用是使所属的线程对象X正常执行run()方法中的任务,而使当前线程Z进行无限期的阻塞,等待线程X销毁后再执行Z后面的代码,。
join与Sychronized的区别在于,join在内部使用使用wait()进行等待,而Sychronized使用的是对象监视器原理。
//main
public class JoinMain {
public static void main(String[] args){
try {
JoinThread threadJoin =new JoinThread();
threadJoin.start();
threadJoin.join(2000);
//threadJoin.sleep(2000);
System.out.println("end time="+System.currentTimeMillis());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//Thread
public class JoinThread extends Thread{
@Override
public void run(){
try {
System.out.println("success");
System.out.println("begin timer"+System.currentTimeMillis());
Thread.sleep(5000);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
可以看到运行结果和sleep的效果一样,都是暂停了两秒:
![这里写图片描述][Image 1]
注意,这里我没有使用join()而是join(long)
他们两个的区别在于:前者是等待执行完,而join(long)是等待一段时间,如果期间执行完了,则直接马上执行后面的,如果没有执行完,则继续执行。
join(long)与sleep(long)的区别:
join(long)在内部使用wait方法,所以join方法可以释放锁而sleep方法并不会释放锁。
注意join方法后面的代码的代码可能会提前运行:
4- 类ThreadLocal简介
类ThreadLocal主要用于解决每个线程可以绑定自己自的问题,ThreadLocal就是一个全局存放数据的盒子,里面存放每个线程私有数据。实现了每个线程都有自己的共享变量。
先看怎么用
public class ThreadLocalMain {
public static ThreadLocal t1 = new ThreadLocal();
public static void main(String[] args){
if(t1.get()==null){
System.out.println("开始t1为:"+t1.get());
t1.set("初始化");
System.out.println("t1为:"+t1.get());
}
}
}
![这里写图片描述][Image 1]
很明显,就像静态变量一样使用,但是!!ThreadLocal的静态变量并不是通用的,如果多个线程同时对t1进行了设置,但是他们设置的都是自己的t1.
就像沙盒一样,每个线程,他们都属于一个容器,同一个类,但是他们拥有自己的独立的静态变量存储空间。
public class ThreadLocalTest extends Thread{
@Override
public void run(){
try {
for (int i = 0; i < 10; i++) {
Tools.t2.set("ThreadTest:----"+(i+1));
System.out.println("ThreadTest:----"+Tools.t2.get());
Thread.sleep(200);
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
public class ThreadLocal02 extends Thread{
@Override
public void run(){
try {
for (int i = 0; i < 10; i++) {
Tools.t2.set("Thread02:----"+(i+1));
System.out.println("Thread02:----"+Tools.t2.get());
Thread.sleep(200);
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
public static void main(String[] args){
// if(t1.get()==null){
// System.out.println("开始t1为:"+t1.get());
// t1.set("初始化");
// System.out.println("t1为:"+t1.get());
// }
ThreadLocalTest test =new ThreadLocalTest();
ThreadLocal02 test02 =new ThreadLocal02();
test.start();
test02.start();
}
![这里写图片描述][Image 1]
这个叫线程变量的隔离性。
同样,子类和父类同样有隔离性。
这个可以用InheritableThreadLocal来解决
(继承即可)
5- 总结
线程间通信基本上就这些了,后面还有线程的lock的使用。
图片参考于:
http://blog.csdn.net/Prepared/article/details/71616709
内容参考:
《java多线程编程核心技术》
[Image 1]:
还没有评论,来说两句吧...