Java基础——线程同步

1.同步/异步编程

异步编程模型:t1线程与t2线程各自执行,两个线程之间互不干扰;

同步编程模型:t1线程与t2线程执行,当t1线程必须等t2线程结束之后才能执行,即同步编程模型。

2.什么时候进行同步,为什么引入同步机制

  1. 为了数据的安全性,尽管应用程序的使用率降低,但是为了保证数据是安全的,必须加入线程同步机制,线程同步机制使程序变成了(等同)单线程;
  2. 什么条件下使用线程同步:必须是多线程环境;多线程环境共享同一个数据;共享的数据涉及到修改操作。

3.线程安全问题

  1. 当多个线程同时操作堆区或方法区的同一个数据时,可能导致数据不一致的现象,称为线程安全问题。
  2. 线程安全问题解决方式:

                (1)每个线程都访问自己的局部变量,不会产生线程安全问题;

                (2)如果多线程必须同时操作堆区或方法区同一个数据时,可以采用线程同步机制。

4.线程同步

4.1synchronized同步代码块:

语法格式:

synchronized(锁对象){

代码块

}

原理:

(1)任何对象都可以作为对象锁,每一个对象都有一个内置锁;

(2)线程想要执行同步代码块,必须先获得锁对象;

(3)线程a获得锁对象,可以执行同步代码块,会一直持有这个锁对象,直到同步代码块执行完释放对象锁;

(4)一个锁对象,在某一时刻最多只能被一个线程持有。

4.2对象锁

语法格式:

public synchronized void m(){代码};

当某个实例方法的整个方法体都需要进行同步,并且锁对象是this时,可以直接使用synchronized关键字修饰这个实例方法,称为同步实例方法。同步实例方法把整个方法体作为同步代码块,默认对象锁是this,也叫对象锁。

4.3类锁

语法格式:

public static synchronized void m(){代码};

当某个静态方法的整个方法体都需要进行同步,并且锁对象是当前类的运行时类时,可以直接使用synchronized修饰这个静态方法,称为同步静态方法。同步静态方法是把整个方法体作为同步代码块,默认锁对象是当前类的运行时类对象,也叫类锁。

5.例

模拟银行账户取款,多线程进行。

账户对象:

public class Account {

    private String account;
    private double balance;

    public Account(String account, double balance) {
        this.account = account;
        this.balance = balance;
    }

    public void withdraw(double money){
        double after = balance - money;
        this.setBalance(after);
    }

    public String getAccount() {
        return account;
    }

    public void setAccount(String account) {
        this.account = account;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }
}

取款线程:

public class Thread04 implements Runnable {

    Account account;

    public Thread04(Account account) {
        this.account = account;
    }

    @Override
    public void run() {
        account.withdraw(100);
        System.out.println("取款:" + 100 + "元,余额:" + account.getBalance());
    }
}

主方法测试:

public class SynchronizedTest {

    public static void main(String[] args) {
        Account account = new Account("招商银行", 1000);

        Thread t1 = new Thread(new Thread04(account));
        Thread t2 = new Thread(new Thread04(account));

        t1.start();
        t2.start();
    }

}

因为使用多线程模拟取款,并且操作同一个账户,所以会出现线程安全问题,如下图。

所以可以使用synchronized关键字。

在withdraw方法中使用synchronized代码块:

public void withdraw(double money){
    //这里使用字符串作为锁对象
    synchronized ("锁对象"){
        double after = balance - money;
        this.setBalance(after);
    }
}

将withdraw方法变为同步实例方法:

public synchronized void withdraw(double money){
    double after = balance - money;
    this.setBalance(after);
}

6.其他:

(1)同步代码块想要实现同步必须保证同一个锁对象,同步代码块只要使用了同一个对象锁就可以实现同步;

(2)经常使用一个常量作为锁对象,在实例方法中也会使用this对象作为锁对象;

(3)有时在静态方法中也会使用当前类的运行时类作为锁对象,也叫类锁;

(4)最好使用synchronized代码块,这样较为灵活,可以使同步的代码量降到最低,效率更高。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值