栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

synchronized关键字的用法、使用wait及notify方法实现线程间的通信

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

synchronized关键字的用法、使用wait及notify方法实现线程间的通信

文章目录

1.多线程的同步2.synchronized修饰方法3.synchronized修饰static方法4.synchronized代码块5.小结6.使用wait及notify方法实现线程间的通信

1.多线程的同步

为什么要引入同步机制

在多线程环境中,可能会有两个甚至更多的线程试图同时 访问一个有限的资源。必须对这种潜在资源冲突进行预防。解决方法:在线程使用一个资源时为其加锁即可。访问资 源的第一个线程为其加上锁以后,其他线程便不能再使用 那个资源,除非被解锁。

问题代码举例

俩个线程同时取账户的钱

public class FetchMoney {

    public static void main(String[] args) {

		// 同一资源
        Bank bank = new Bank();

//        MoneyRunnableTherd moneyRunnableTherd = new MoneyRunnableTherd();
//        moneyRunnableTherd.setBank(bank);
//        Thread thread = new Thread(moneyRunnableTherd);
//        Thread thread2 = new Thread(moneyRunnableTherd);
//        thread.start();
//        thread2.start();

        MoneyTherd moneyTherd = new MoneyTherd(bank);
        moneyTherd.setName("银行柜台");

        MoneyTherd moneyTherd2 = new MoneyTherd(bank);
        moneyTherd2.setName("ATM");

        moneyTherd.start();
        moneyTherd2.start();

    }

}

// 个人账户
class Bank {

    private int money = 1000;

    public int getMoney(int number) {

        if (number < 0) {
            return -1;
        } else if (number > money) {
            return -2;
        } else if (money < 0) {
            return -3;
        } else {
            try {
                // 取钱模拟耗时
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            money -= number;
            System.out.println("剩余 money:" + money);
            return number;
        }
    }
}

// 取钱线程extends Thread
class MoneyTherd extends Thread {

    private Bank bank;

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

    @Override
    public void run() {
        // 取出800
        System.out.println("取出:" + bank.getMoney(800));
    }
}

// 取钱线程implements Runnable
class MoneyRunnableTherd implements Runnable {

    private Bank bank;

    public void setBank(Bank bank) {
        this.bank = bank;
    }

    @Override
    public void run() {
        // 取出800
        System.out.println("取出:" + bank.getMoney(800));
    }
}

运行结果

2.synchronized修饰方法

synchronized 关键字:当 synchronized 关键字修饰一个方 法的时候,该方法叫做同步方法。Java 中的每个对象都有一个锁(lock)或者叫做监视器 (monitor),当访问某个对象的 synchronized 方法时,表 示将该对象上锁,此时其他任何线程都无法再去访问该 synchronized 方法了,直到之前的那个线程执行方法完毕 后(或者是抛出了异常),那么将该对象的锁释放掉,其 他线程才有可能再去访问该synchronized 方法。如果一个对象有多个 synchronized 方法,某一时刻某个 线程已经进入到了某个 synchronized 方法,那么在该方法 没有执行完毕前,其他线程是无法访问该对象的任何 synchronized 方法的。

举例一

public class FetchMoney {

    public static void main(String[] args) {

        Bank bank = new Bank();

        MoneyTherd moneyTherd = new MoneyTherd(bank);
        moneyTherd.setName("银行柜台");

        MoneyTherd moneyTherd2 = new MoneyTherd(bank);
        moneyTherd2.setName("ATM");

        moneyTherd.start();
        moneyTherd2.start();

    }

}

// 个人账户
class Bank {

    private int money = 1000;

    public synchronized int getMoney(int number) {

        if (number < 0) {
            return -1;
        } else if (number > money) {
            return -2;
        } else if (money < 0) {
            return -3;
        } else {
            try {
                // 取钱模拟耗时
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            money -= number;
            System.out.println("剩余 money:" + money);
            return number;
        }
    }
}

// 取钱线程
class MoneyTherd extends Thread {

    private Bank bank;

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

    @Override
    public void run() {
        // 取出800
        System.out.println("取出:" + bank.getMoney(800));
    }
}

运行结果:

举例二
如果一个对象有多个 synchronized 方法,某一时刻某个 线程已经进入到了某个 synchronized 方法,那么在该方法 没有执行完毕前,其他线程是无法访问该对象的任何 synchronized 方法的。

public class ThredTeat1 {

    public static void main(String[] args) {

        Example example = new Example();

        TheThred theThred = new TheThred(example);
        TheThred2 theThred2 = new TheThred2(example);

        theThred.start();
        theThred2.start();


    }

}

class Example {

    public synchronized void execute ()  {
        try {
            for (int i = 0; i < 5; i++) {
                Thread.sleep(500);
                System.out.println("hello:" + i);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized void execute2 ()  {
        try {
            for (int i = 0; i < 5; i++) {
                Thread.sleep(500);
                System.out.println("word:" + i);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

class TheThred extends Thread {

    private Example example;

    public TheThred(Example example) {
        this.example = example;
    }

    @Override
    public void run() {
        example.execute();
    }

}

class TheThred2 extends Thread {

    private Example example;

    public TheThred2(Example example) {
        this.example = example;
    }

    @Override
    public void run() {
        example.execute2();
    }

}

运行结果:

hello:0
hello:1
hello:2
hello:3
hello:4
word:0
word:1
word:2
word:3
word:4

Process finished with exit code 0
3.synchronized修饰static方法

如果某个 synchronized方法是 static的, ,那么当线程访问该方法时, 它锁的并不是synchronized方法所在的对象,而是synchronized 方 法所在的对象所对应的Class对象,因为Java中无论一个类有多少 个对象,这些对象会对应唯一一个Class对象,因此当线程分别访 问同一个类的两个对象的两个 static, synchronized 方法时,他们 的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行 完毕后另一个线程才开始执行。

static的方法属于类方法,它属于这个Class(注意:这里的Class不是指Class的某个具体对象),那么static获取到的锁,就是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。而‘非static’方法获取到的锁,就是当前调用这个方法的对象的锁了。所以,他们之间不会产生互斥。

public class ThredTeat1 {

    public static void main(String[] args) {

        Example example = new Example();

        TheThred theThred = new TheThred(example);
        TheThred2 theThred2 = new TheThred2(example);

        theThred.start();
        theThred2.start();


    }

}

class Example {

    public synchronized void execute ()  {
        try {
            for (int i = 0; i < 5; i++) {
                Thread.sleep(500);
                System.out.println("hello:" + i);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized static void execute2 ()  {
        try {
            for (int i = 0; i < 5; i++) {
                Thread.sleep(500);
                System.out.println("word:" + i);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

class TheThred extends Thread {

    private Example example;

    public TheThred(Example example) {
        this.example = example;
    }

    @Override
    public void run() {
        example.execute();
    }

}

class TheThred2 extends Thread {

    private Example example;

    public TheThred2(Example example) {
        this.example = example;
    }

    @Override
    public void run() {
        example.execute2();
    }

}

运行结果: execute() 和 execute2() 同时执行,说明锁的不是同一个对象

hello:0
word:0
word:1
hello:1
word:2
hello:2
word:3
hello:3
word:4
hello:4

Process finished with exit code 0

举例二

public class ThredTeat1 {

    public static void main(String[] args) {

        Example example = new Example();

        TheThred theThred = new TheThred(example);

        example = new Example();

        TheThred2 theThred2 = new TheThred2(example);

        theThred.start();
        theThred2.start();


    }

}

class Example {

    public synchronized static void execute ()  {
        try {
            for (int i = 0; i < 5; i++) {
                Thread.sleep(500);
                System.out.println("hello:" + i);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public synchronized static void execute2 ()  {
        try {
            for (int i = 0; i < 5; i++) {
                Thread.sleep(500);
                System.out.println("word:" + i);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

class TheThred extends Thread {

    private Example example;

    public TheThred(Example example) {
        this.example = example;
    }

    @Override
    public void run() {
        example.execute();
    }

}

class TheThred2 extends Thread {

    private Example example;

    public TheThred2(Example example) {
        this.example = example;
    }

    @Override
    public void run() {
        example.execute2();
    }

}

运行结果

hello:0
hello:1
hello:2
hello:3
hello:4
word:0
word:1
word:2
word:3
word:4

Process finished with exit code 0

4.synchronized代码块

synchronized代码块写法

synchronized(object) {

}
表示线程执行时会对object上锁

public class TherdTest2 {

    public static void main(String[] args) {

        Example2 example2 = new Example2();

        ThredTest thredTest = new ThredTest(example2);
        ThredTest1 thredTest1 = new ThredTest1(example2);

        thredTest.start();
        thredTest1.start();

    }

}

class Example2 {

    private Object object = new Object();

    public void execute() {

        synchronized (object) {
            try {
                for (int i = 0; i < 5; i++) {
                    Thread.sleep(500);
                    System.out.println("hello:" + i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    public void execute2() {
        synchronized (object) {
            try {
                for (int i = 0; i < 5; i++) {
                    Thread.sleep(500);
                    System.out.println("word:" + i);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

class ThredTest extends Thread {

    private Example2 example;

    public ThredTest(Example2 example) {
        this.example = example;
    }

    @Override
    public void run() {
        example.execute();
    }

}

class ThredTest1 extends Thread {

    private Example2 example;

    public ThredTest1(Example2 example) {
        this.example = example;
    }

    @Override
    public void run() {
        example.execute2();
    }

}

运行结果

hello:0
hello:1
hello:2
hello:3
hello:4
word:0
word:1
word:2
word:3
word:4

Process finished with exit code 0
5.小结

synchronized 方法是一种粗粒度的并发控制,某一时刻, 只能有一个线程执行该synchronized 方法;
synchronized 块 则是一种细粒度的并发控制,只会将块中的代码同步,位 于方法内、synchronized 块之外的代码是可以被多个线程 同时访问到的。

6.使用wait及notify方法实现线程间的通信

wait 与 notify 方法都是定义在 Object 类中,而且是 final 的,因此会被所有的 Java类所继承并且无法重写。这两个 方法要求在调用时线程应该已经获得了对象的锁,因此对 这两个方法的调用需要放在synchronized方法或块当中。

当线程执行了 wait 方法时,它会释放掉对象的锁。

另一个会导致线程暂停的方法就是 Thread 类的 sleep方法, 它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中 是不会释放掉对象的锁的。

代码举例

public class Sample {

    private int number;

    public synchronized void increase() {
        while (number != 0) {
            try {
                // wait() 会释放锁、释放cpu  所以每次被唤醒 都要重新检查状态,因为不知道等待期间发生了什么
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        number++;
        System.out.println(number);

        notify();

    }

    public synchronized void decrease() {

        while (number == 0) {
            try {
                // wait() 会释放锁、释放cpu  所以每次被唤醒 都要重新检查状态,因为不知道等待期间发生了什么
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        number--;
        System.out.println(number);

        notify();
    }


}
public class IncreaseThred extends Thread{

    private Sample sample;

    public IncreaseThred(Sample sample) {
        this.sample = sample;
    }

    @Override
    public void run() {

        try {
            for (int i = 0; i < 10; i++) {

                Thread.sleep(600);

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

    }
}

public class DecreaseThred extends Thread{

    private Sample sample;

    public DecreaseThred(Sample sample) {
        this.sample = sample;
    }

    @Override
    public void run() {

        try {
            for (int i = 0; i < 10; i++) {

                Thread.sleep(500);

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

    }
}

public class Maintest {

    public static void main(String[] args) {

        Sample sample = new Sample();

        IncreaseThred increaseThred = new IncreaseThred(sample);
        DecreaseThred decreaseThred = new DecreaseThred(sample);
        IncreaseThred increaseThred1 = new IncreaseThred(sample);
        DecreaseThred decreaseThred1 = new DecreaseThred(sample);

        increaseThred.start();
        decreaseThred.start();
        increaseThred1.start();
        decreaseThred1.start();

    }

}

运行结果

1
0
1
0
1
0
1
0
1
0
1
0
1


Process finished with exit code 0

线程状态图

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/755302.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号