java多线程(三) 线程的同步与通信

1 线程同步

线程的同步是指协同、协助、互相配合,指多个线程协同配合访问临界资源。
为什么要进行线程的同步?
主要是线程可见性问题
java 中如何实现线程同步?
一般说来java线程实现同步有以下几种方式

1通过synchronized实现同步
这里主要有两种方式,分别是同步块,与同步方法:
同步块:

    public void saveMoney2(int money){
        synchronized (this){
            account += money;
        }
    }

其中这里的this指的是当前调用该方法的对象,这样锁就是当前对象的锁,也可以换成其他的对象,这样就表示锁是其他对象的锁。

同步方法:

public synchronized void saveMoney(int money){
        account += money;
        System.out.println(Thread.currentThread().getName()+" 余额:<<----- " + getAccount());
}

同步方法中锁表示当前对象的锁,即调用该方法的锁。

下面看一个完整的线程同步的例子

public class Bank {
    private int account;

    public int getAccount() {
        return account;
    }

//    public  void saveMoney(int money){
//        account += money;
//    }

    public synchronized void saveMoney(int money){
        account += money;
    }

    public void saveMoney2(int money){
        synchronized (this){
            account += money;
        }
    }

//    public void getMoney(int money){
//        account -= money;
//    }

    public synchronized void getMoney(int money){
        account -= money;
    }






public class SaveTask implements Runnable {
    private Bank bank;

    public SaveTask(Bank bank){
        this.bank = bank;
    }

    @Override
    public void run() {
        while (true){
            bank.saveMoney(20);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}


/**
 * Created by qiyei2015 on 2017/2/11.
 */
public class TakeTask implements Runnable {

    private Bank bank;

    public TakeTask(Bank bank){
        this.bank = bank;
    }

    @Override
    public void run() {
        while (true){
            bank.getMoney(30);
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

测试代码:
private static void testSynchronzied(){
        Bank bank = new Bank();
        SaveTask saveTask = new SaveTask(bank);
        TakeTask takeTask = new TakeTask(bank);

        for (int i = 0 ;i < 10;i++){
            new Thread(saveTask,"存钱 线程" + (i+1)).start();
            new Thread(takeTask,"取钱 线程"+ (i+1)).start();

        }

    }

程序输出:
这里写图片描述

2 线程通信

这里的通信主要是指线程的唤醒,比如一个线程进入阻塞状态,另一个线程去唤醒它。用到的主要是wait()与notify()。notifyAll()

wait():Object类中方法,线程中调用了wait()方法,线程会进入阻塞等待状态,并释放同步锁,线程会进入等待池。因此需要在同步块或者同步方法中调用
notify():Object类中方法,唤醒同一锁中等待池中的一个线程,唤醒的具体线程由虚拟机实现
notifyAll();Object类中方法,唤醒同一锁中等待池中的所有方法。

接着上面的例子,我们发现我们的余额会变成负数,这个与实际情况肯定是不相符的。因此我们需要修改我们的程序,让余额不能为负数,这里就会用到我们的线程通信的知识了。

修改取钱代码,只有取了之后的余额大于200。才能取钱,否则让取钱线程在此处等待。

    public synchronized void getMoney(int money){
        //如果小于200,则等待存钱线程
        if ((account - money) < 200){
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else {
            account -= money;
            System.out.println(Thread.currentThread().getName()+" 余额:------>> " + getAccount());
        }
    }

修改存钱线程,当发现余额大于200时,就表明可以取钱了。这样我们就能保持我们的余额永远大于200。

    public synchronized void saveMoney(int money){
        account += money;
        //存钱结束后唤醒全部存钱线程
        if (account > 200){
            notifyAll();
        }
        System.out.println(Thread.currentThread().getName()+" 余额:<<----- " + getAccount());
    }

其他的都不变,运行代码,程序输出:
这里写图片描述

可以发现,余额不再会低于200了,表明达到我们的效果了。

3 线程join()

关于线程的join的理解是一个难点,我们还是看JDK的解释。
public final void join()
throws InterruptedException
Waits for this thread to die.
关于这里的this就是指调用join()的线程对象。因为join()是一个实例方法。意思是等待调用join()线程对象结束。例如:t.join()用于阻塞调用join()方法的线程,直到t运行结束。

注意:一般t.join()用于在主线程调用。用于按照一定的顺序组织多个线程的运行
例子:假设有1,2,3,4四个线程,我现在想让他们按照3,1,2,4顺序运行,则可以使用join来实现。


/**
 * Created by qiyei2015 on 2017/2/6.
 */
public class MyThread extends Thread {

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        int i = 0;
        while (i < 5){
            i++;
            String name = Thread.currentThread().getName();
            System.out.println(name + " 运行....." + i);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

测试代码:
    private static void testJoin(){
        MyThread thread1 = new MyThread("线程1");
        MyThread thread2 = new MyThread("线程2");
        MyThread thread3 = new MyThread("线程3");
        MyThread thread4 = new MyThread("线程4");

        try {
            thread3.start();
            thread3.join();

            thread1.start();
            thread1.join();

            thread2.start();
            thread2.join();

            thread4.start();
            thread4.join();

            System.out.println("线程全部运行结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

运行结果如下:

这里写图片描述

好,关于本节内容就完成了,下一节会讲JVM内存模型与线程可见性问题

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值