IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> JAVA模拟多线程并发Demo及其解决方案 -> 正文阅读

[Java知识库]JAVA模拟多线程并发Demo及其解决方案

Account类:

package ThreadSafe;

public class Account {

    private String account;

    private double balance;

    public Account() {
    }

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

    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 void moneyOut(double money) {
        double bal_before = this.getBalance();
        double bal_after = bal_before - money;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        this.setBalance(bal_after);
    }
}

线程类

package ThreadSafe;

import java.io.IOException;

public class AccountThread implements Runnable {
    private Account act;

    public AccountThread() {
    }

    public AccountThread(Account act) {
        this.act = act;
    }

    public Account getAct() {
        return act;
    }

    public void setAct(Account act) {
        this.act = act;
    }

    @Override
    public void run() {
        this.act.moneyOut(5000);
        System.out.println(Thread.currentThread().getName() + "取出成功,余额:" + this.act.getBalance());
    }

}

调用方法:

public static void test28() {
        Account act = new Account("act-1", 10000);
        Thread t1 = new Thread(new AccountThread(act));
        t1.setName("xxxxx");
        Thread t2 = new Thread(new AccountThread(act));
        t2.setName("yyyy");

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

解决方案:

public void moneyOut(double money) {
        // 以下代码必须是线程同步的,不能并发;
        // 一个线程把这里的代码全部执行完后,另一个线程才能进来

        /**
         * 线程同步机制的语法是
         * synchronized(){
         * 线程同步代码块
         * }
         * synchronized后面小括号中的是相当关键的,
         * 这个数据必须是多线程共享的,才能达到多线程排队
         * 假设有t1,t2,t3,t4,t5,5个线程,
         * 想让t1,t2,t3,3个线程排队,t4,t5不需要排队,
         * 小括号中一定要写一个对t1,t2,t3是共享的对象,这个对象对t4,t5是不共享的
         * 这里的共享对象是账户对象,就是this
         */
        synchronized (this) {
            double bal_before = this.getBalance();
            double bal_after = bal_before - money;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            this.setBalance(bal_after);
        }
    }

原理说明:

? ? ? ? 在JAVA语言中,任何一个对象都有一把锁,其实这个锁就是标记(只是叫做锁);

假设t1和t2线程并发,开始执行以上代码的时候,肯定会有一个先执行一个后执行,假设t1先执行了,遇到了synchronized,这个时候t1就会自动找到后面共享对象的对象锁,之后占有这把锁,然后执行同步代码块中的程序,直到同步代码块中的程序执行完毕,释放这把锁,期间一直占用这把锁;

假设t1已经占有这把锁,t2也遇到了synchronized关键字,也会去占有后面这个共享对象的对象锁,此时t1已经占有这把锁,t2只能在同步代码块外面等待,直到t1把同步代码块执行完毕,t1归还这把锁后,t2才能占有这把锁,之后t2才能执行同步代码块中的程序,这样就达到了线程排队执行;

其中这个共享对象是最关键的,必须是需要排队的线程对象所共享的;

例子中的共享对象也可以是account实例里面的任何一个元素,synchronized只要传入线程的共享对象,就能触发它的占用锁机制;

//这样写就不是线程安全了,因为会new多个对象,不是同一个
Object obj2 = new Object();
synchronized (obj2) {
    ....
}

//这样写是线程安全的,因为abc在常量池里面,只存在一个,是共享对象,但是会导致所有线程同步
synchronized ("abc") {
    ...
}

t1与t2同步,与t3不同步

        synchronized (this) {
            double bal_before = this.getBalance();
            double bal_after = bal_before - money;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            this.setBalance(bal_after);
        }


public static void test28() {
        Account act = new Account("act-1", 10000);
        Account act2 = new Account("act-2", 10000);

        Thread t1 = new Thread(new AccountThread(act));
        t1.setName("xxxxx");
        Thread t2 = new Thread(new AccountThread(act));
        t2.setName("yyyy");
        //此时t1与t2同步,与t3不同步
        Thread t3 = new Thread(new AccountThread(act2));
        t2.setName("zzzzz");

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

//如果传入的是abc,那么t1,t2,t3是同步的
synchronized ("abc") {}

synchronized范围越大,效率越低;

下面这样写也可以,但是会降低效率;

public void run() {
        synchronized (act) {
            this.act.moneyOut(5000);
            System.out.println(Thread.currentThread().getName() + "取出成功,余额:" + this.act.getBalance());
        }
    }

?实例方法上的synchronized,此时共享对象必须是this,不能是其他对象,缺乏灵活,并且synchronized出现在方法体上,表示整个方法需要同步,可能会无故扩大同步范围,导致程序的执行效率降低,所以这种方式不常用;

如果共享对象是this,并且整个方法需要同步,此时可以用synchronized修饰方法体,可以节简代码;

public synchronized void moneyOut(double money) {
    ...
}

?synchronized修饰在静态变量上,就表示占用类锁,虽然对象不同,但是对象都属于同一个类,类锁被占用了,其他对象都要等待锁的释放:

public class ClassTest {
    public synchronized static void doSome() {
        System.out.println("doSome begin");

        try {
            Thread.sleep(5 * 1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("doSome end");
    }

    public synchronized static void doOther() {
        System.out.println("doOther begin");
        System.out.println("doOther end");
    }
}


public class ClassThreadTest implements Runnable {
    ClassTest ct;

    public ClassThreadTest(ClassTest ct) {
        this.ct = ct;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub
        if (Thread.currentThread().getName() == "t1") {
            this.ct.doSome();
        } else {
            this.ct.doOther();
        }
    }
}

public static void test29() {
        ClassTest ct = new ClassTest();
        ClassTest ct2 = new ClassTest();
        Thread t1 = new Thread(new ClassThreadTest(ct));
        t1.setName("t1");
        Thread t2 = new Thread(new ClassThreadTest(ct2));
        t2.setName("t2");

        t1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        t2.start();
    }

以上的锁也称为排他锁;

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-18 17:27:33  更:2022-04-18 17:30:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:33:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码