JAVA中的synchronized关键字

JAVA中的synchronized关键字

  • 线程同步机制的语法是:
    synchronized(共享对象){
    线程同步代码块
    }
    synchronized后面小括号传的“数据”是相当关键的
    这个数据必须是多线程共享的数据,才能达到多线程排队

  • 使用synchronized会影响程序效率

  • ()中写:
    那要看你想让哪些线程同步:
    假设t1、t2、t3、t4、t5共5个线程,
    只希望t1,t2,t3排队,t4,t5不需要排队怎么办?
    只需在()中写一个t1,t2,t3共享的对象,
    而这个对象对于t4,t5来说不是共享的即可
    此处的共享对象是Account
    账户对象是共享的,那么this指的就是Account
    不一定要this,只需是多线程共享的对象即可

  • ()中填共享对象:
    1、填String字符串:synchronized (“abc”)
    因为String字符串保存在字符串常量池中,所有线程共享,但此时所有线程都会同步
    2、填this:synchronized (this)
    this指向当前对象,只在特定情况下会有用
    3、填实例变量obj:synchronized (obj)
    当前类的实例对象obj,obj是一个Object对象,所有线程共同使用一个obj,可以启到线程同步的作用
    4、填局部变量obj2:synchronized (obj2)
    obj2是一个局部变量,每次调用该方法都会新建对象,无法启到线程同步的作用

  • synchronized出现在实例方法中:
    synchronized出现在实例方法上,一定锁的是this,所以这种方法不灵活
    另一个缺点:synchronized出现在实例方法上,表示整个方法体都需要同步,
    可能会无故扩大同步范围,导致程序执行效率降低
    优点:使代码更简洁

  • 对象锁:在java语言中,任何一个对象都有“一把锁”,其实这把锁就是标记。(只是称呼为锁)
    100个对象,100把锁,1个对象1把锁

  • 类锁:对象再多,也只有一把锁

编写银行账户类:

public class Account {
    private String user;
    private double accountMoney;
    Object obj = new Object();

    public Account() {
    }

    public Account(String user, double accountMoney) {
        this.user = user;
        this.accountMoney = accountMoney;
    }

    public double getAccountMoney() {
        return accountMoney;
    }

    public void setAccountMoney(double accountMoney) {
        this.accountMoney = accountMoney;
    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }
    //取款方法
    public void withdraw(double money){
        //以下几行代码必须是线程排队的,不能并发
        synchronized (this) {  //this指向当前对象,可以
//        synchronized (obj){   obj指向当前类的实例对象obj,obj是一个Object对象
//                                所有线程共同使用一个obj,可以,
//        Object obj2 = new Object();  此时obj2是一个局部变量,每次调用该方法都会新建,
//        synchronized (obj2){     不可以
//        synchronized ("abc"){ 可以,因为String字符串保存在字符串常量池中,
//                                所有线程共享,但此时所有线程都会同步
            //取款前的余额
            double before = this.accountMoney;
            //取款后的余额
            double after = before - money;
            //模拟网络延迟1s
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //更新余额
            this.setAccountMoney(after);

            System.out.println(user + "账户取款" + money + "成功,账户余额:" + after);
        }
    }
}
  • 上方synchronized代码执行原理:
    1、假设t1和t2线程并发,开始执行以下程序的时候,肯定有一个先一个后,
    2、假设t1先执行了,遇到了synchronized,这个时候自动找()中“共享对象”
    的对象锁,找到后会占有这把锁,然后执行同步代码块中的程序,在程序执行过程
    中一直都是占有这把锁的。直到同步代码块结束,这把锁才会释放。
    3、此时t1已经占有锁,t2也遇到了synchronized,也会去找锁,可锁已被
    t1占用,t2只能在synchronized外面等待t1的结束,直到t1执行完了同步代码块
    中的程序,归还了共享对象锁,t2占有后,就进入同步代码块执行
    这样就达到了线程排队执行

多线程实现类:

public class AccountTest {
    public static void main(String[] args) {
        //创建账户
        Account a = new Account("张三", 10000);
        //创建两个线程对象,模拟同时取钱
        Thread t1 = new Thread(new AccountThread(a));
        Thread t2 = new Thread(new AccountThread(a));
        t1.start();
        t2.start();
    }
}

测试类:

public class AccountThread implements Runnable{
    private final Account act;
    //通过构造方法传递账户信息
    public AccountThread(Account act) {
        this.act = act;
    }

    @Override
    public void run() {
        act.withdraw(5000);
    }
}

你可能感兴趣的:(零基础)