第三章-线程之间的通信-第二篇

本文详细介绍了Java中线程控制的重要技巧,包括如何利用synchronized关键字实现线程间的等待和通知,探讨了notify与notifyAll的区别及应用场景。此外,还深入讲解了join方法的使用方法及其与sleep的区别,并通过实例演示了ThreadLocal的基本使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

等待通知之交叉备份

请看如下代码

package chaprer3;

/**
 * @program: demo
 * @description: 数据库帮助类
 * @author: lee
 * @create: 2018-09-01
 **/
public class DBtools {
    volatile private boolean prevIsA = false;

    synchronized public void backupA() {
        try {
            while (prevIsA == true) {
                wait();
            }
            for (int i = 0; i < 5; i++) {
                System.out.println("***********");
            }
            prevIsA = true;
            notifyAll();
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }


    synchronized public void backupB() {
        try {
            while (prevIsA == false) {
                wait();
            }
            for (int i = 0; i < 5; i++) {
                System.out.println("-----------");
            }
            prevIsA = false;
            notifyAll();
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}    


package chaprer3;

/**
 * @program: demo
 * @description: 线程A
 * @author: lee
 * @create: 2018-09-01
 **/
public class ThreadA  implements Runnable{
    private DBtools dBtools;

    public ThreadA(DBtools dBtools) {
        this.dBtools = dBtools;
    }


    @Override
    public void run() {
        this.dBtools.backupA();
    }
}

package chaprer3;

/**
 * @program: demo
 * @description: 线程A
 * @author: lee
 * @create: 2018-09-01
 **/
public class ThreadB implements Runnable{
    private DBtools dBtools;

    public ThreadB(DBtools dBtools) {
        this.dBtools = dBtools;
    }


    @Override
    public void run() {
        this.dBtools.backupB();
    }
}

package chaprer3;

/**
 * @program: demo
 * @description: 主线程
 * @author: lee
 * @create: 2018-09-01
 **/
public class Run {

    public static void main(String[] args) {
        DBtools dBtools = new DBtools();
        for (int i = 0; i < 5000; i++) {
            new Thread(new ThreadA(dBtools)).start();
            new Thread(new ThreadB(dBtools)).start();
        }
    }
}    

输出结果

...
-----------
-----------
-----------
-----------
-----------
***********
***********
***********
***********
***********
-----------
-----------
-----------
-----------
-----------
***********
***********
***********
***********
***********
-----------
-----------
-----------
-----------
-----------
...

不断的交替输出,结果非常快。

思考一个问题,当notifyall改为了notify以后会出现什么情况,会出现假死状态,即很可能notify唤醒的都是同类线程,最终所有的同类线程都处于阻塞状态.

package chaprer3;

/**
 * @program: demo
 * @description: 数据库帮助类
 * @author: lee
 * @create: 2018-09-01
 **/
public class DBtools {
    volatile private boolean prevIsA = false;

    synchronized public void backupA() {
        try {
            while (prevIsA == true) {
                wait();
            }
            for (int i = 0; i < 5; i++) {
                System.out.println("***********");
            }
            prevIsA = true;
            notify();
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }


    synchronized public void backupB() {
        try {
            while (prevIsA == false) {
                wait();
            }
            for (int i = 0; i < 5; i++) {
                System.out.println("-----------");
            }
            prevIsA = false;
            notify();
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}    

程序会一直挂起不停止.

join方法的使用

join方法的作用就是等待线程对象销毁

实际用法,请参考如下代码

package chaprer3;

/**
 * @program: demo
 * @description: 数据库帮助类
 * @author: lee
 * @create: 2018-09-01
 **/
public class DBtools {
    volatile private boolean prevIsA = false;

    synchronized public void backupA() {
        try {
            while (prevIsA == true) {
                wait();
            }
            for (int i = 0; i < 5; i++) {
                System.out.println("***********");
            }
            prevIsA = true;
            notify();
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }


    synchronized public void backupB() {
        try {
            while (prevIsA == false) {
                wait();
            }
            for (int i = 0; i < 5; i++) {
                System.out.println("-----------");
            }
            prevIsA = false;
            notify();
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}    

package chaprer3;

/**
 * @program: demo
 * @description: 线程A
 * @author: lee
 * @create: 2018-09-01
 **/
public class ThreadA  implements Runnable{
    private DBtools dBtools;

    public ThreadA(DBtools dBtools) {
        this.dBtools = dBtools;
    }


    @Override
    public void run() {
        this.dBtools.backupA();
    }
}

package chaprer3;

/**
 * @program: demo
 * @description: 主线程
 * @author: lee
 * @create: 2018-09-01
 **/
public class Run {

    public static void main(String[] args) {
        DBtools dBtools = new DBtools();
        ThreadA threadA = new ThreadA(dBtools);
        Thread thread = new Thread(threadA);
        thread.start();
        System.out.println("我想等thread执行完毕后执行");
    }
}    

输出结果

我想等thread执行完毕后执行
***********
***********
***********
***********
***********

显然这并没有等线程执行完了再执行后面的结果。程序做如下修改

public class Run {

    public static void main(String[] args) {

        try {
            DBtools dBtools = new DBtools();
            ThreadA threadA = new ThreadA(dBtools);
            Thread thread = new Thread(threadA);
            thread.start();
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("我想等thread执行完毕后执行");
    }
}    

输出结果

***********
***********
***********
***********
***********
我想等thread执行完毕后执行

达到了们想要的结果,如果用sleep方法也可以,但是不能确定具体的等待时间。

join(long)的使用

方法join中的参数是设置等待时间,请看如下代码

/**
 * @program: demo
 * @description: 数据库帮助类
 * @author: lee
 * @create: 2018-09-01
 **/
public class DBtools {
    volatile private boolean prevIsA = false;

    synchronized public void backupA() {
        try {
            while (prevIsA == true) {
                wait();
            }
            Thread.sleep(5000);
            for (int i = 0; i < 5; i++) {
                System.out.println("***********");
            }
            prevIsA = true;
            notify();
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }


    synchronized public void backupB() {
        try {
            while (prevIsA == false) {
                wait();
            }
            for (int i = 0; i < 5; i++) {
                System.out.println("-----------");
            }
            prevIsA = false;
            notify();
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}    

public class Run {

    public static void main(String[] args) {

        try {
            DBtools dBtools = new DBtools();
            ThreadA threadA = new ThreadA(dBtools);
            Thread thread = new Thread(threadA);
            thread.start();
            thread.join(2000);//等待两秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("我想等thread执行完毕后执行");
    }
}    

输出结果

我想等thread执行完毕后执行
***********
***********
***********
***********
***********

join的实现方法

/**
     * Waits at most {@code millis} milliseconds for this thread to
     * die. A timeout of {@code 0} means to wait forever.
     *
     * <p> This implementation uses a loop of {@code this.wait} calls
     * conditioned on {@code this.isAlive}. As a thread terminates the
     * {@code this.notifyAll} method is invoked. It is recommended that
     * applications not use {@code wait}, {@code notify}, or
     * {@code notifyAll} on {@code Thread} instances.
     *
     * @param  millis
     *         the time to wait in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

join方法的内部是用wait方法实现的,所以join方法具有释放锁的特点,sleep方法则没有释放锁的特点,即释放了锁以后其他的线程可以调用此线程中的同步方法了。

方法join和sleep的区别

请看如下代码

public class ThreadB extends Thread{
    @Override
    public void run() {
        try {
            System.out.println("b开始运行了");
            Thread.sleep(5000);
            System.out.println("b运行结束了");
        } catch (InterruptedException ex) {

        }
    }

    synchronized  public void bService() {
        System.out.println("b服务启动了");
    }
}

public class ThreadA extends Thread {

    private ThreadB threadB;

    public ThreadA(ThreadB threadB) {
        this.threadB = threadB;
    }

    @Override
    public void run() {

        try {
            synchronized (threadB) {
                threadB.start();
                Thread.sleep(5000);
                //threadB.join(5000);
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ThreadC  extends Thread{
    private ThreadB threadB;

    public ThreadC(ThreadB threadB) {
        this.threadB = threadB;
    }

    @Override
    public void run() {
        threadB.bService();
    }
}

public class Run {

    public static void main(String[] args) {
        try {
            ThreadB threadB = new ThreadB();
            ThreadA threadA = new ThreadA(threadB);
            threadA.start();
            Thread.sleep(1000);
            ThreadC threadC = new ThreadC(threadB);
            threadC.start();
        } catch ( InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}    

输出结果

b开始运行了
b运行结束了
b服务启动了

稍微修改代码

public class ThreadA extends Thread {

    private ThreadB threadB;

    public ThreadA(ThreadB threadB) {
        this.threadB = threadB;
    }

    @Override
    public void run() {

        try {
            synchronized (threadB) {
                threadB.start();
                //Thread.sleep(5000);
				 //释放掉threadB的锁
                threadB.join(5000);
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

join之后的代码提前运行

轻卡如下代码

public class ThreadA extends Thread {

    private ThreadB threadB;

    public ThreadA(ThreadB threadB) {
        this.threadB = threadB;
    }

    @Override
    public void run() {
        try {
            synchronized (threadB) {
                System.out.println("threadA开始运行了");
                Thread.sleep(5000);
                System.out.println("threadA结束运行了");
            }
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}

public class ThreadB extends Thread {
    @Override
    synchronized public void run() {
        System.out.println("threadB开始运行了");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("threadB结束运行了");
    }

}

public class Run {

    public static void main(String[] args) {
        for (int i =0 ;i<10;i++) {
            try {
                ThreadB threadB = new ThreadB();
                ThreadA threadA = new ThreadA(threadB);
                threadA.start();
                threadB.start();
                threadB.join(2000);
                System.out.println("主线程即将结束");
            } catch ( InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }
}  

如果线程A先获得线程锁,则输出结果如下,则A线程会先执行完成,如果线程B先获得对象锁,两秒以后释放对对象B的加锁,其他线程也会有机会进行执行,所以这里的数据结果将会有差异。 输出结果 一下是一种情况

...
threadA结束运行了
threadA结束运行了
主线程即将结束
threadB开始运行了
threadB结束运行了
...

ThreadLocal使用

变量值的共享可以使用public static的变量形式,所有的线程都使用一个public static变量。如果想要实现每一个线程都有自己的变量共享,则可以采用如下方式。

public class Tools {
    public static ThreadLocal local = new ThreadLocal();
} 

public class ThreadA extends Thread {
    @Override
    synchronized public void run() {
        try {
            for (int i = 0; i < 100; i++) {
                Tools.local.set(i);
                System.out.println(Thread.currentThread().getName() + "----" + Tools.local.get());
                Thread.sleep(1000);
            }

        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }

}

public class ThreadB extends Thread {
    @Override
    synchronized public void run() {
        try {
            for (int i = 0; i < 100; i++) {
                Tools.local.set(i);
                System.out.println(Thread.currentThread().getName() + "----" + Tools.local.get());
                Thread.sleep(1000);
            }

        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }

}

public class Run {

    public static void main(String[] args) {
        try {
            ThreadB threadB = new ThreadB();
            threadB.setName("thread-b");
            ThreadA threadA = new ThreadA();
            threadA.setName("thread-a");
            threadA.start();
            threadB.start();
            for (int i = 0; i < 100; i++) {
                Tools.local.set(i);
                System.out.println("main--"+Tools.local.get());
                Thread.sleep(1000);
            }
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }
}   

输出结果

主线程即将结束
main--0
thread-a----0
thread-b----0
main--1
thread-a----1
thread-b----1
main--2
thread-a----2
thread-b----2
main--3
thread-a----3
thread-b----3
main--4
thread-a----4
thread-b----4
main--5
thread-a----5
thread-b----5
....

从结果可以看出,线程之间的变量互相不影响。

解决ThreadLocal中get为null

请看如下代码

public class Tools extends ThreadLocal{

    @Override
    protected Object initialValue() {
        return "不会再返回null值了";
    }

    public static void main(String[] args) {
        System.out.println(new Tools().get());
    }
}

输出结果

不会再返回null值了

转载于:https://my.oschina.net/jiansin/blog/1940674

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值