线程同步机制

线程同步机制

概念:

  • 当多个线程同时访问一种共享资源时,可能会造成数据的覆盖等不一致问题,此时需要对线程之间进行通信和协调,该机制叫做线程同步机制
  • 多个线程并发读写同一个临界资源时会发生线程并发安全问题
    异步操作:多线程并发的操作,各自独立运行
    同步操作:多线程串行的操作,先后执行的顺序

实现方式:

  • 在Java语言中使用synchronized关键字来实现同步/对象锁机制从而保证线程执行的原子性

使用同步代码块方式:

使用同步代码块的方式实现部分代码的锁定,格式如下:
synchronized(类类型的引用) {
编写所有需要锁定的代码;
}

案例代码:
package 线程;

/**
 * 线程的同步机制
 * 同步代码块的方式一
 */
public class AccountTest implements Runnable{

    private int balance; // 用于描述账户余额

    public AccountTest() {
    }

    public AccountTest(int balance) {
        this.balance = balance;
    }

    @Override
    public String toString() {
        return "AccountTest{" +
                "balance=" + balance +
                '}';
    }

    public int getBalance() {
        return balance;
    }

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

    @Override
    public void run() {
        synchronized (this) {
            System.out.println(Thread.currentThread().getName() + "正在操作");
            int temp = getBalance();
            // 模拟取款
            System.out.println("正在取款请稍后");
            if (temp > 200) {
                System.out.println("正在出钞,请稍后");
                temp -= 200;
                try {
                    Thread.sleep(5000);
                    System.out.println("取钞完成");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                System.out.println("余额不足");
            }

            // 模拟将最新的账户余额写入后台
            setBalance(temp);
            System.out.println(getBalance());
        }

    }

    public static void main(String[] args) {
        AccountTest at = new AccountTest(1000);
        Thread t1 = new Thread(at);
        Thread t2 = new Thread(at);
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


使用同步方法的方式

直接使用synchronized关键字来修饰整个方法即可
该方式等价于:
synchronized(this) { 整个方法体的代码 }

案例代码:
  • Runnable代码:
/**
 * 线程的同步机制
 * 直接使用synchronized来修饰整个方法
 */
public class AccountTest3 implements Runnable{

    private int balance; // 用于描述账户余额

    public AccountTest3() {
    }

    public AccountTest3(int balance) {
        this.balance = balance;
    }

    @Override
    public String toString() {
        return "AccountTest{" +
                "balance=" + balance +
                '}';
    }

    public int getBalance() {
        return balance;
    }

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

    @Override
    public synchronized void run() {
            System.out.println(Thread.currentThread().getName() + "正在操作");
            int temp = getBalance();
            // 模拟取款
            System.out.println("正在取款请稍后");
            if (temp > 200) {
                System.out.println("正在出钞,请稍后");
                temp -= 200;
                try {
                    Thread.sleep(5000);
                    System.out.println("取钞完成");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                System.out.println("余额不足");
            }

            // 模拟将最新的账户余额写入后台
            setBalance(temp);
            System.out.println(getBalance());
    }

    public static void main(String[] args) {
        AccountTest at = new AccountTest(1000);
        Thread t1 = new Thread(at);
        Thread t2 = new Thread(at);
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


  • Thread代码:

/**
 * 线程的同步机制
 * synchronized直接修饰
 */
public class AccountTest4 extends Thread{

    private int balance; // 用于描述账户余额

    public AccountTest4(){

    }
    public AccountTest4(int balance) {
        this.balance = balance;
    }

    public int getBalance() {
        return balance;
    }

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

    @Override
    public synchronized void run() {

            int temp = getBalance();

            System.out.println("正在取款,请稍后");
            if (temp >= 200) {
                System.out.println("正在出钞");
                temp -= 200;
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            setBalance(temp);
            System.out.println("余额" + getBalance());
    }

    public static void main(String[] args) {
        AccountTest2 at2 = new AccountTest2(1000);
        at2.start();
        AccountTest2 at22 = new AccountTest2(1000);
        at22.start();

        try {
            at2.join();
            at22.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


静态方法锁定的方式

  • 当我们对一个静态方法加锁,如: public synchronized static void xxx(){….}
  • 那么该方法锁的对象是类对象。每个类都有唯一的一个类对象。获取类对象的方式:类名.class
  • 静态方法与非静态方法同时使用了synchronized后它们之间是非互斥关系的
  • 原因在于:静态方法锁的是类对象而非静态方法锁的是当前方法所属对象
  • 使用synchronized保证线程同步应当注意
    • 多个需要同步的线程在访问同步块时,看到的应该是同一个锁对象引用。
    • 在使用同步块时应当尽量减少同步范围以提高并发的执行效率
案例代码:
/**
 * 线程的同步机制
 * 同步代码块方式二
 */
public class AccountTest2 extends Thread{

    private int balance; // 用于描述账户余额
    private static Demo dm = new Demo();

    public AccountTest2(){

    }
    public AccountTest2(int balance) {
        this.balance = balance;
    }

    public int getBalance() {
        return balance;
    }

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

    @Override
    public void run() {

        synchronized (dm) {
            int temp = getBalance();

            System.out.println("正在取款,请稍后");
            if (temp >= 200) {
                System.out.println("正在出钞");
                temp -= 200;
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            setBalance(temp);
            System.out.println("余额" + getBalance());
        }

    }

    public static void main(String[] args) {
        AccountTest2 at2 = new AccountTest2(1000);
        at2.start();
        AccountTest2 at22 = new AccountTest2(1000);
        at22.start();

        try {
            at2.join();
            at22.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
class Demo{}

使用Lock(锁)实现线程同步

  • 从Java5开始提供了更强大的线程同步机制—使用显式定义的同步锁对象来实现
  • java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具
  • 该接口的主要实现类是ReentrantLock类,该类拥有与synchronized相同的并发性,在以后的线程安全控制中,经常使用ReentrantLock类显式加锁和释放锁
  • 常用方法:
方法功能
ReentrantLock()使用无参方式构造对象
void lock()获取锁
void unlock()释放锁
案例代码:
  • Runnable代码:
import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程的同步机制
 * 使用Lock实现线程的同步
 */
public class AccountTest5 implements Runnable {

    private int balance; // 用于描述账户余额

    private ReentrantLock lock = new ReentrantLock(); // 准备了一把锁
    public AccountTest5() {
    }

    public AccountTest5(int balance) {
        this.balance = balance;
    }

    @Override
    public String toString() {
        return "AccountTest{" +
                "balance=" + balance +
                '}';
    }

    public int getBalance() {
        return balance;
    }

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

    @Override
    public void run() {
        lock.lock(); // 关锁
        System.out.println(Thread.currentThread().getName() + "正在操作");
        int temp = getBalance();
        // 模拟取款
        System.out.println("正在取款请稍后");
        if (temp > 200) {
            System.out.println("正在出钞,请稍后");
            temp -= 200;
            try {
                Thread.sleep(5000);
                System.out.println("取钞完成");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            System.out.println("余额不足");
        }

        // 模拟将最新的账户余额写入后台
        setBalance(temp);
        System.out.println(getBalance());
        lock.unlock(); // 开锁
    }

    public static void main(String[] args) {
        AccountTest at = new AccountTest(1000);
        Thread t1 = new Thread(at);
        Thread t2 = new Thread(at);
        t1.start();
        t2.start();
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

  • Thread代码:
import java.util.concurrent.locks.ReentrantLock;

/**
 * 线程的同步机制
 * 使用Lock锁实现同步机制
 */
public class AccountTest6 extends Thread{

    private int balance; // 用于描述账户余额

    private ReentrantLock lock = new ReentrantLock(); // 准备一把锁
    public AccountTest6(){

    }
    public AccountTest6(int balance) {
        this.balance = balance;
    }

    public int getBalance() {
        return balance;
    }

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

    @Override
    public void run() {

        lock.lock();
        int temp = getBalance();

        System.out.println("正在取款,请稍后");
        if (temp >= 200) {
            System.out.println("正在出钞");
            temp -= 200;
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        setBalance(temp);
        System.out.println("余额" + getBalance());

        lock.unlock();
    }

    public static void main(String[] args) {
        AccountTest2 at2 = new AccountTest2(1000);
        at2.start();
        AccountTest2 at22 = new AccountTest2(1000);
        at22.start();

        try {
            at2.join();
            at22.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术很low的瓜贼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值