精华内容
下载资源
问答
  • java如何实现线程同步
    千次阅读
    2020-09-14 09:30:19

    一、什么是线程的同步

        线程有自己的私有数据,比如栈和寄存器,同时与其它线程共享相同的虚拟内存和全局变量等资源。  在一般情况下,创建一个线程是不能提高程序的执行效率的,所以要创建多个线程。但是当多个线程同时读写同一份共享资源的时候,会引起冲突,例如在多个线程同时对同一个内存地址进行写入,由于CPU时间调度上的问题,写入数据会被多次的覆盖,所以就要使线程同步。这时候就需要引入线程同步机制使各个线程排队一个一个的对共享资源进行操作,而不是同时进行。

        简单的说就是,在多线程编程里面,一些数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。

    二、为什么需要线程间的通信

        1. 多个线程并发执行时, 在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务,并且我们希望他们有规律的执行, 那么多线程之间需要一些协调通信,以此来帮我们达到多线程共同操作一份数据。

       2.当然如果我们没有使用线程通信来使用多线程共同操作同一份数据的话,虽然可以实现,但是在很大程度会造成多线程之间对同一共享变量的争夺,那样的话势必为造成很多错误和损失!

       3.所以,我们才引出了线程之间的通信,多线程之间的通信能够避免对同一共享变量的争夺。

    三、实现线程同步的方式

    本篇博客暂时讲解两种方法通过synchronized关键字和lock锁

    四、线程间的通信方式

    Object类中相关的方法有notify方法和wait方法。因为wait和notify方法定义在Object类中,因此会被所有的类所继承。这些方法都是**final**的,即它们都是不能被重写的,不能通过子类覆写去改变它们的行为。

     1.wait()   

      ①wait()方法:** 让当前线程进入等待,并释放锁。
       ②wait(long)方法:** 让当前线程进入等待,并释放锁,不过等待时间为long,超过这个时间没有对当前线程进行唤醒,将**自动唤醒**。

    2.notify()

    ③notify()方法:** 让当前线程通知那些处于等待状态的线程,当前线程执行完毕后释放锁,并从其他线程中唤醒其中一个继续执行。
     ④notifyAll()方法:** 让当前线程通知那些处于等待状态的线程,当前线程执行完毕后释放锁,将唤醒所有等待状态的线程。

    3.wait()与sleep()比较

     当线程调用了wait()方法时,它会释放掉对象的锁。
     Thread.sleep(),它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的。

    五、synchronized简单介绍

    synchronized 是java语言关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。

    1.实现原理

    jvm基于进入和退出Monitor对象来实现方法同步和代码块同步。

    方法级的同步是隐式,即无需通过字节码指令来控制的,它实现在方法调用和返回操作之中。JVM可以从方法常量池中的方法表结构(method_info Structure) 中的 ACC_SYNCHRONIZED 访问标志区分一个方法是否同步方法。当方法调用时,调用指令将会 检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先持有monitor(虚拟机规范中用的是管程一词), 然后再执行方法,最后再方法完成(无论是正常完成还是非正常完成)时释放monitor。

    代码块的同步是利用monitorenter和monitorexit这两个字节码指令。它们分别位于同步代码块的开始和结束位置。当jvm执行到monitorenter指令时,当前线程试图获取monitor对象的所有权,如果未加锁或者已经被当前线程所持有,就把锁的计数器+1;当执行monitorexit指令时,锁计数器-1;当锁计数器为0时,该锁就被释放了。如果获取monitor对象失败,该线程则会进入阻塞状态,直到其他线程释放锁。

    六、synchronized 关键字使用

     synchronized 关键字,主要有两种用法,分别是同步方法和同步代码块。也就是synchronized 方法和 synchronized 块。

     1.同步方法

      pubilc synchronized void test() {  

        System.out.println("方法运行");
    }

    修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;  

    2.同步代码块

    public void test() {
             synchronized(obj) {
                  System.out.println("===");
             }
        }
        ``` 
    - 被`synchronized`修饰的代码块及方法,在同一时间,只能被单个线程访问。

    修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;

    六、synchronized实现

    需求是,模拟银行的存钱,取钱操作,多个线程存,多个线程取

    1.synchronized同步方法

    银行账户account对象

    package s1;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @ClassName: Account
     * @Author: tanp
     * @Description: 账户
     * @Date: 2020/9/10 10:21
     */
    public class Account {
    
        private String name;
        private int money;
        private boolean isAwait;
    
        public Account(String name, int money, boolean isAwait) {
            this.money = money;
            this.name = name;
            this.isAwait = isAwait;
        }
    
        public String getName() {
            return name;
        }
    
        /**
         * @Description 存钱
         * @Date 2020/9/10 10:30
         * @Author tanp
         */
        public synchronized void saveMoney(int value, int threadId, String name) {
            System.out.println("在线程" + threadId + "运行存钱方法时" + name + "账户有" + money + "元");
            if (value > 0) {
                money = money + value;
            }
            //如果当前用户上的余额高于0元,说明可以取钱了,通知那些等待在this对象上的线程如取钱线程,可以醒过来了
            if (isAwait) {
                this.notify();
                System.out.println(name + "账户余额充足,线程" + threadId + "调用notify方法");
                isAwait = false;
            }
            try {
                //休息一秒钟
                System.out.println("线程" + threadId + "存钱" + value + "元到" + name + "账户,现有余额" + money + "元");
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * @Description 取钱
         * @Date 2020/9/10 10:36
         * @Author tanp
         */
        public synchronized void fechMoney(int value, int threadId, String name) {
            try {
                System.out.println("在线程" + threadId + "运行取钱方法时" + name + "账户有" + money + "元");
                //如果当前所要取的钱,大于银行卡余额,让占有this的取钱线程,暂时释放对this的占有,并等待
                while (value > money) {
                    System.out.println(name + "账户余额不足,线程" + threadId + "调用wait方法");
                    isAwait = true;
                    this.wait();
                }
                money = money - value;
                System.out.println("线程" + threadId + "取钱" + value + "元," + name + "账户现有余额" + money + "元");
                //休眠一秒钟
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
    
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(15, 30, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
            Account account = new Account("王先生", 23, false);
            for (int i = 0; i < 5; i++) {
                SaveThread saveThread = new SaveThread(account, i);
                threadPoolExecutor.execute(saveThread);
                FetchThread fetchThread = new FetchThread(account, i);
                threadPoolExecutor.execute(fetchThread);
            }
            Account account1 = new Account("李先生", 48, false);
            for (int i = 0; i < 2; i++) {
                SaveThread saveThread = new SaveThread(account1, i);
                threadPoolExecutor.execute(saveThread);
                FetchThread fetchThread = new FetchThread(account1, i);
                threadPoolExecutor.execute(fetchThread);
            }
        }
    }
    

    取钱线程

    package s1;
    
    import java.util.Random;
    
    /**
     * @ClassName: FetchThread
     * @Author: tanp
     * @Description: 取钱线程
     * @Date: 2020/9/10 10:56
     */
    public class FetchThread extends Thread {
    
        private Account account;
    
        private int threadId;
    
        private Random r = new Random();
    
        public FetchThread(Account account,int threadId) {
            this.threadId = threadId;
            this.account = account;
        }
    
        @Override
        public void run() {
            while (true) {
                account.fechMoney( r.nextInt(50),threadId,account.getName());
            }
        }
    }
    

    存钱线程

    package s1;
    
    import java.util.Random;
    
    /**
     * @Package: s1
     * @ClassName: SaveThread
     * @Author: tanp
     * @Description: 存钱线程
     * @Date: 2020/9/10 11:54
     */
    public class SaveThread extends Thread{
    
        private Account account;
        private int threadId;
    
        private Random r = new Random();
    
        public SaveThread(Account account,int threadId) {
            this.account = account;
            this.threadId = threadId;
        }
    
        @Override
        public void run() {
            while (true) {
                account.saveMoney(r.nextInt(50),threadId,account.getName());
            }
        }
    
    }
    
    
    

    2.synchronized同步代码块

    account对象

    package s2;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @ClassName: Account
     * @Author: tanp
     * @Description: 账户
     * @Date: 2020/9/10 10:21
     */
    public class Account {
    
        private String name;
        private int money;
        private boolean isAwait;
    
        public Account(String name, int money, boolean isAwait) {
            this.money = money;
            this.name = name;
            this.isAwait = isAwait;
        }
    
        public String getName() {
            return name;
        }
    
        /**
         * @Description 存钱
         * @Date 2020/9/10 10:30
         * @Author tanp
         */
        public  void saveMoney(int value, int threadId, String name) {
            System.out.println("在线程" + threadId + "运行存钱方法时" + name + "账户有" + money + "元");
            if (value > 0) {
                money = money + value;
            }
            //如果当前用户上的余额高于0元,说明可以取钱了,通知那些等待在this对象上的线程如取钱线程,可以醒过来了
            if (isAwait) {
                this.notify();
                System.out.println(name + "账户余额充足,线程" + threadId + "调用notify方法");
                isAwait = false;
            }
            try {
                //休息一秒钟
                System.out.println("线程" + threadId + "存钱" + value + "元到" + name + "账户,现有余额" + money + "元");
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * @Description 取钱
         * @Date 2020/9/10 10:36
         * @Author tanp
         */
        public  void fechMoney(int value, int threadId, String name) {
            try {
                System.out.println("在线程" + threadId + "运行取钱方法时" + name + "账户有" + money + "元");
                //如果当前所要取的钱,大于银行卡余额,让占有this的取钱线程,暂时释放对this的占有,并等待
                while (value > money) {
                    System.out.println(name + "账户余额不足,线程" + threadId + "调用wait方法");
                    isAwait = true;
                    this.wait();
                }
                money = money - value;
                System.out.println("线程" + threadId + "取钱" + value + "元," + name + "账户现有余额" + money + "元");
                //休眠一秒钟
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
    
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(15, 30, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
            Account account = new Account("王先生", 23, false);
            for (int i = 0; i < 5; i++) {
                SaveThread saveThread = new SaveThread(account, i);
                threadPoolExecutor.execute(saveThread);
                FetchThread fetchThread = new FetchThread(account, i);
                threadPoolExecutor.execute(fetchThread);
            }
            Account account1 = new Account("李先生", 48, false);
            for (int i = 0; i < 2; i++) {
                SaveThread saveThread = new SaveThread(account1, i);
                threadPoolExecutor.execute(saveThread);
                FetchThread fetchThread = new FetchThread(account1, i);
                threadPoolExecutor.execute(fetchThread);
            }
        }
    }
    

    与前面同步方法的account对象相比,就是去掉了方法上的synchronized关键字

    取钱线程

    package s2;
    
    
    import java.util.Random;
    
    /**
     * @ClassName: FetchThread
     * @Author: tanp
     * @Description: 取钱线程
     * @Date: 2020/9/10 10:56
     */
    public class FetchThread extends Thread {
    
        private Account account;
    
        private int threadId;
    
        private Random r = new Random();
    
        public FetchThread(Account account, int threadId) {
            this.threadId = threadId;
            this.account = account;
        }
    
        @Override
        public void run() {
            while (true) {
                synchronized (account) {
                    account.fechMoney( r.nextInt(50),threadId,account.getName());
                }
            }
        }
    }
    

    与前面的取钱线程相比就是在线程run方法里加了synchronized (account)代码

    存钱线程

    package s2;
    
    import java.util.Random;
    
    /**
     * @Package: s1
     * @ClassName: SaveThread
     * @Author: tanp
     * @Description: 存钱线程
     * @Date: 2020/9/10 11:54
     */
    public class SaveThread extends Thread {
    
        private Account account;
        private int threadId;
    
        private Random r = new Random();
    
        public SaveThread(Account account, int threadId) {
            this.account = account;
            this.threadId = threadId;
        }
    
        @Override
        public void run() {
            while (true) {
                synchronized (account) {
                    account.saveMoney(r.nextInt(50), threadId, account.getName());
                }
            }
        }
    
    }
    
    
    

    与前面的存钱线程相比就是在线程run方法里加了synchronized (account)代码

    七、lock锁实现

    需求跟前面的一样,也就是代码也也就是更改了一点

    1.使用ReentrantLock实现同步

    lock()方法:上锁

    unlock()方法:释放锁

    trylock():synchronized 是不占用到手不罢休的,会一直试图占用下去。与 synchronized 的钻牛角尖不一样,Lock接口还提供了一个trylock方法。

    2.使用Condition实现等待/通知

    使用synchronized方式进行线程交互,用到的是同步对象的wait,notify和notifyAll方法

    Lock也提供了类似的解决办法,首先通过lock对象得到一个Condition对象,然后分别调用这个Condition对象的:await, signal,signalAll 方法

    3.代码案例

    account对象

    package r;
    
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @Package: r
     * @ClassName: Account
     * @Author: tanp
     * @Description: ${description}
     * @Date: 2020/9/10 15:12
     */
    public class Account {
    
        private String name;
        private int money;
        boolean isAwait;
    
    
        public Account(String name, int money, boolean isAwait) {
            this.money = money;
            this.name = name;
            this.isAwait = isAwait;
        }
    
        public String getName() {
            return name;
        }
    
        /**
         * @Description 存钱
         * @Date 2020/9/10 10:30
         * @Author tanp
         */
        public void saveMoney(int value, int threadId, String name,Condition condition) {
            System.out.println("在线程" + threadId + "运行存钱方法时" + name + "账户有" + money + "元");
            if (value > 0) {
                money = money + value;
            }
            //如果当前有线程在等待,存钱之后唤醒等的线程
            if (isAwait) {
                condition.signal();
                System.out.println(name+"账户余额充足,线程"+threadId+"调用signal()方法");
                //线程唤醒之后,将是否有等待线程的标志设置为false
                isAwait = false;
            }
            try {
                //休息一秒钟
                System.out.println("线程" + threadId + "存钱" + value + "元到" + name + "账户,现有余额" + money + "元");
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * @Description 取钱
         * @Date 2020/9/10 10:36
         * @Author tanp
         */
        public void fechMoney(int value, int threadId, String name,Condition condition ) {
            try {
                System.out.println("在线程" + threadId + "运行取钱方法时" + name + "账户有" + money + "元");
                //如果当前所要取的钱,大于银行卡余额,让释放当前取钱线程的占有,并等待
                while (value > money) {
                    System.out.println(name+"账户余额不足,线程"+threadId+"调用await方法");
                    //将是否有等待线程标志设置为true
                    isAwait = true;
                    condition.await();
                }
                money = money - value;
                System.out.println("线程" + threadId + "取钱" + value + "元," + name + "账户现有余额" + money + "元");
                //休眠一秒钟
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
    
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(15, 30, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
            Lock lock = new ReentrantLock();
            Condition condition = lock.newCondition();
            Account account = new Account("王先生", 3,false);
    
            for (int i = 0; i < 5; i++) {
                SaveThread saveThread = new SaveThread(account, i, lock,condition);
                threadPoolExecutor.execute(saveThread);
                FetchThread fetchThread = new FetchThread(account, i, lock,condition);
                threadPoolExecutor.execute(fetchThread);
            }
            Account account1 = new Account("李先生", 48,false);
            for (int i = 0; i < 2; i++) {
                SaveThread saveThread = new SaveThread(account1, i, lock,condition);
                threadPoolExecutor.execute(saveThread);
                FetchThread fetchThread = new FetchThread(account1, i, lock,condition);
                threadPoolExecutor.execute(fetchThread);
            }
        }
    }
    

    取钱线程

    package r;
    
    
    import java.util.Random;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    
    /**
     * @Package: r
     * @ClassName: FetchThread
     * @Author: tanp
     * @Description: ${description}
     * @Date: 2020/9/10 15:40
     */
    public class FetchThread extends Thread {
    
        private Account account;
    
        private int threadId;
    
        private Lock lock;
    
        private Condition condition;
    
        private Random r = new Random();
    
        public FetchThread(Account account, int threadId, Lock lock, Condition condition) {
            this.threadId = threadId;
            this.account = account;
            this.lock = lock;
            this.condition = condition;
        }
    
        @Override
        public void run() {
            while (true) {
                try {
                    lock.lock();
                    account.fechMoney(r.nextInt(50), threadId, account.getName(),condition);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    System.out.println("休眠失败");
                }
            }
        }
    }
    

    存钱线程

    package r;
    
    
    import java.util.Random;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    
    /**
     * @Package: r
     * @ClassName: SaveThread
     * @Author: tanp
     * @Description: ${description}
     * @Date: 2020/9/10 15:36
     */
    public class SaveThread extends Thread {
        private Account account;
        private int threadId;
        private Lock lock;
        private Condition condition;
    
        private Random r = new Random();
    
        public SaveThread(Account account, int threadId, Lock lock, Condition condition) {
            this.account = account;
            this.threadId = threadId;
            this.lock = lock;
            this.condition = condition;
        }
    
        @Override
        public void run() {
            while (true) {
                try {
                    lock.lock();
                    account.saveMoney(r.nextInt(5), threadId, account.getName(),condition);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
                try {
                    //休眠让其他线程可以抢占到锁
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    System.out.println("休眠失败");
                }
    
            }
        }
    }
    

    4.何时用ReentrantLock

    - 适用场景:
        - 时间锁等候、可中断锁等候、无块结构锁、多个条件变量或者锁投票 
        - 在确实需要一些synchronized所没有的特性的时候,比如时间锁等候、可中断锁等候、无块结构锁、多个条件变量或者锁投票。
    - 如何选用
        - ReentrantLock 还具有可伸缩性的好处,应当在高度争用的情况下使用它,但是请记住,大多数 synchronized 块几乎从来没有出现过争用,所以可以把高度争用放在一边。我建议用 synchronized 开发,直到确实证明 synchronized 不合适,而不要仅仅是假设如果使用 ReentrantLock “性能会更好”。请记住,这些是供高级用户使用的高级工具。(而且,真正的高级用户喜欢选择能够找到的最简单工具,直到他们认为简单的工具不适用为止。)。一如既往,首先要把事情做好,然后再考虑是不是有必要做得更快。 

    八、ReentrantLock和synchronized使用分析

    - ReentrantLock是Lock的实现类,是一个互斥的同步器,在多线程高竞争条件下,ReentrantLock比synchronized有更加优异的性能表现。
    - 1 用法比较
        - Lock使用起来比较灵活,但是必须有释放锁的配合动作
        - Lock必须手动获取与释放锁,而synchronized不需要手动释放和开启锁
        - Lock只适用于代码块锁,而synchronized可用于修饰方法、代码块等
    - 2 特性比较
        - ReentrantLock的优势体现在:
            - 具备尝试非阻塞地获取锁的特性:当前线程尝试获取锁,如果这一时刻锁没有被其他线程获取到,则成功获取并持有锁
            - 能被中断地获取锁的特性:与synchronized不同,获取到锁的线程能够响应中断,当获取到锁的线程被中断时,中断异常将会被抛出,同时锁会被释放
            - 超时获取锁的特性:在指定的时间范围内获取锁;如果截止时间到了仍然无法获取锁,则返回
    - 3 注意事项
        - 在使用ReentrantLock类的时,一定要注意三点:
            - 在finally中释放锁,目的是保证在获取锁之后,最终能够被释放
            - 不要将获取锁的过程写在try块内,因为如果在获取锁时发生了异常,异常抛出的同时,也会导致锁无故被释放。
            - ReentrantLock提供了一个newCondition的方法,以便用户在同一锁的情况下可以根据不同的情况执行等待或唤醒的动作。

    更多相关内容
  • Java线程间同步互斥

    2021-03-17 17:53:21
    Java线程间同步互斥,在实际的编程中,经常要处理线程的同步互斥问题。Java 语言内在强大的多线程支持使得处理这类问题变得相对来说比较简单。本例将模仿经典的线程同步互斥例子——生产者和消费者问题,来演示  ...
  • 同步控制是并发程序必不可少的重要手段,本文我们将通过重入锁、读写锁、信号量、倒计数器和循环栅栏以及他们的实例来介绍Java并发程序中的同步控制。 目录线程安全 Thread Safety重入锁 ReentrantLock读写锁 ...
  • 当使用多个线程来访问同一个数据时,非常容易出现线程安全问题,所以我们用同步机制来解决这些问题,本文将详细介绍,需要的朋友可以参考下
  • 主要介绍了java线程同步几种方法的相关资料,这里提供5种方法,需要的朋友可以参考下
  • 主要介绍了Java线程同步以及线程通信,多线程编程是Java学习中的重点和难点,需要的朋友可以参考下
  • 主要介绍了Java利用ExecutorService实现同步执行大量线程,ExecutorService可以维护我们的大量线程在操作临界资源时的稳定性。
  • java线程同步的实现方式

    万次阅读 多人点赞 2019-03-08 01:47:21
    这里抛砖引玉,为何要使用同步?...下面总结一些java线程实现同步方式,大致有下面几种: 1.同步方法 使用 synchronized关键字,可以修饰普通方法、静态方法,以及语句块。由于java的每个对象都有一个内置锁...

    这里抛砖引玉,为何要使用同步?

    当多个线程同时操作一个可共享的资源时会出现线程安全问题,将会导致数据不一致,因此使用同步锁来防止该操作执行完之前不许被其他线程执行,从而保证了该变量的唯一性和准确性。下面总结一些java线程实现同步方式,大致有下面几种:

    1.同步方法 
        使用 synchronized关键字,可以修饰普通方法、静态方法,以及语句块。由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。需要注意的是调用静态方法时,锁住的不是对象,锁住的是类。

     //修饰普通方法
     public synchronized void add(){}
    
     //修饰语句块
     synchronized(object){ 
        
     }
     //修饰静态方法
     public static synchronized int increase(){
    
     }

    同步是一种高开销的操作,因此应该尽量减少同步的内容。 通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。 
          
    2.使用特殊域变量(volatile)实现线程同步

    • volatile关键字为域变量的访问提供了一种免锁机制, 
    • 使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新, 
    • 因此每次使用该域就要重新计算,而不是使用寄存器中的值 
    • volatile不会提供任何原子操作,它也不能用来修饰final类型的变量 

        多线程中的非同步问题主要出现在对域的读写上,如果让域自身避免这个问题,则就不需要修改操作该域的方法。 
        用final域,有锁保护的域和volatile域可以避免非同步的问题。 
        
    3.使用重入锁实现线程同步

        在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。 ReentrantLock类是可重入、互斥、实现了Lock接口的锁, 
        它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力。
        ReenreantLock类的常用方法有:

    •  ReentrantLock() : 创建一个ReentrantLock实例 
    •  lock() : 获得锁 
    •  unlock() : 释放锁 

     ReentrantLock()还有一个可以创建公平锁的构造方法,但由于能大幅度降低程序运行效率,不推荐使用 
       关于Lock对象和synchronized关键字的选择: 
            a.最好两个都不用,使用一种java.util.concurrent包提供的机制, 
                能够帮助用户处理所有与锁相关的代码。 
            b.如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码 
            c.如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁 
            
    4.使用局部变量实现线程同步 
           如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本, 副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。

       ThreadLocal 类的常用方法

    •     ThreadLocal() : 创建一个线程本地变量 
    •     get() : 返回此线程局部变量的当前线程副本中的值 
    •     initialValue() : 返回此线程局部变量的当前线程的"初始值" 
    •     set(T value) : 将此线程局部变量的当前线程副本中的值设置为value

        ThreadLocal与同步机制 
            a.ThreadLocal与同步机制都是为了解决多线程中相同变量的访问冲突问题。 
            b.前者采用以"空间换时间"的方法,后者采用以"时间换空间"的方式

    5.使用阻塞队列实现线程同步

    上面的实现方式都是在底层实现的线程同步,但是我们在实际开发当中,应当尽量远离底层结构。 使用javaSE5.0版本中新增的java.util.concurrent包将有助于简化开发。 例如使用LinkedBlockingQueue<E>来实现线程的同步 。
        LinkedBlockingQueue<E>是一个基于已连接节点的,范围任意的blocking queue。 
        队列是先进先出的顺序(FIFO),关于队列以后会详细讲解~ 
        LinkedBlockingQueue 类常用方法 
        LinkedBlockingQueue() : 创建一个容量为Integer.MAX_VALUE的LinkedBlockingQueue 
        put(E e) : 在队尾添加一个元素,如果队列满则阻塞 
        size() : 返回队列中的元素个数 
        take() : 移除并返回队头元素,如果队列空则阻塞

    BlockingQueue<E>定义了阻塞队列的常用方法,尤其是三种添加元素的方法,我们要多加注意,当队列满时:

     

    • add()方法会抛出异常
    • offer()方法返回false
    • put()方法会阻塞

    6.使用原子变量实现线程同步
         原子操作就是指将读取变量值、修改变量值、保存变量值看成一个整体来操作,即-这几种行为要么同时完成,要么都不完成。在java的util.concurrent.atomic包中提供了创建了原子类型变量的工具类,使用该类可以简化线程同步。其中AtomicInteger 表可以用原子方式更新int的值,可用在应用程序中(如以原子方式增加的计数器),但不能用于替换Integer;可扩展Number,允许那些处理机遇数字类的工具和实用工具进行统一访问。
    AtomicInteger类常用方法:

    • AtomicInteger(int initialValue) : 创建具有给定初始值的新的AtomicInteger
    • addAddGet(int dalta) : 以原子方式将给定值与当前值相加
    • get() : 获取当前值

    代码实例:

     

     1 class Bank {
     2         private AtomicInteger account = new AtomicInteger(100);
     3 
     4         public AtomicInteger getAccount() {
     5             return account;
     6         }
     7 
     8         public void save(int money) {
     9             account.addAndGet(money);
    10         }
    11 } 

     

    补充--原子操作主要有:
      对于引用变量和大多数原始变量(long和double除外)的读写操作;
      对于所有使用volatile修饰的变量(包括long和double)的读写操作。

    展开全文
  •  b)使用runnable可以解决性和不能共享的问题(不是说使用runnable解决了共享问题,只是相对于创建Thread来说,它可以算的上是共享了,为了获得更精确的共享问题,它必须的使用线程同步操作)。实现了runnable接口...
  • 在之前,已经学习到了线程的创建和状态控制,但是每个线程之间几乎都没有什么太大的联系。可是有的时候,可能存在多个线程多同一个数据进行操作,这样,可能就会引用各种奇怪的问题。现在就来学习多线程对数据访问的...
  • 下面小编就为大家带来一篇java 线程之对象的同步和异步(实例讲解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • java线程同步

    2014-08-26 18:04:06
    java.util.concurrent 包含许多线程安全、测试良好、高性能的并发构建块。不客气地说,创建 java.util.concurrent 的目的就是要实现 Collection 框架对数据结构所执行的并发操作。通过提供一组可靠的、高性能并发...
  • 主要介绍了java线程如何按指定顺序同步执行,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 上一章,我们通过三个例子(点击跳转)来介绍了线程安全问题,说到线程安全就不得不提到线程同步,它是解决线程安全的一种重要方法。本章就来简单的介绍一下何为线程同步。 从上一章的学习我们知道,当多个线程...

            嗨喽~小伙伴们我来了,

            上一章,我们通过几个例子(点击跳转)介绍了线程安全问题,而说到线程安全就不得不提到线程同步,它是解决线程安全的一种重要方法。本章就来简单地介绍一下线程同步

            从上一章的学习我们知道,当多个线程操作一个资源的时候,有可能由于线程的不确定切换出现数据不一致的安全问题,因此,我们要解决这个问题,就得想办法使得资源在某个时间戳只能被一个线程访问。基于这样的思想,我们提出了“队列与锁”的策略:

            通俗理解,就是将所有线程排成一个队列,给要共享的资源上把锁,只有线程获得该资源的锁之后,才能访问该资源

            这也是线程同步的基本思想。所谓线程同步,是指同一进程中的多个线程相互协调工作从而达到一致性。使用线程同步,在解决线程安全问题的同时还能提高程序的性能。

            基于“队列与锁”的思想,JDK中封装了一些用于实现线程同步的类和关键字。我们主要介绍synchronized 和 lock 两种。

    一. synchronized

            我们知道,在java中,每个对象都有一把唯一的锁,这也是synchronized实现线程同步的基础。总的来说,synchronized实现线程同步主要有三种形式:

    形式特点
    实例同步方法锁的是当前实例对象,执行同步代码前必须获得当前实例的锁
    静态同步方法锁的是当前类的class对象,执行同步代码前必须获得当前类对象的锁
    同步代码块锁的是括号里的对象,对给定对象加锁,执行同步代码块必须获得给定对象的锁

            事实上,当两个线程同时对一个对象的某个方法进行调用时,只有一个线程能够抢到该对象的锁,因为一个对象只有一把锁。抢到该对象的锁之后,其他线程就无法访问该对象的所有synchronized方法,但仍可以访问该对象中的非synchronized方法

            下面,我们用代码来演示synchronized的三种用法。为了突出synchronized在线程安全方面的作用,我们采用对比(有synchronized和无synchronized)的方式来介绍。

            第一种,修饰实例同步方法。首先,来看一个简单的程序:

    
    
    /**
     * @author sixibiheye
     * @date 2021/8/31
     * @apiNote synchronized用法举例
     */
    
    public class Synchronized implements Runnable{
        //公共资源
        static int count = 0;
        public void increase(){
            count++;
        }
        @Override
        public void run() {
            for (int i = 0; i < 10000; i++) {
                increase();
            }
        }
        public static void main(String[] args) throws InterruptedException {
            new Thread(new Synchronized(),"A").start();
            new Thread(new Synchronized(),"B").start();
            Thread.sleep(2000);
            System.out.println(count);
        }
    }
    
    

            如果小伙伴们学习了上一章的内容,应该很容易看出这个程序是存在线程安全问题的,它的输出结果如下:

            如果小伙伴们不太理解的话,可以去看我的上一篇博文。 

            上一篇博文中说到,对于“count++”,JVM是这样处理的:

    1. 某线程从内存中读取count值到寄存器

    2. 某线程在寄存器中修改count的值

    3. 某线程将修改后的count值写入内存

            简单解释一下,我们开启了两个线程去执行increase()方法,如果没有任何保护机制,假设,当count的值累加到1000时,A线程从主内存中读取到寄存器的count值为1000,执行完“count++”操作后,寄存器中的count值为1001,刚想写入内存,B线程正好抢到了CPU的使用权,开始执行run()方法,由于未写入内存,B线程从内存中读取到的count值为仍为1000,执行完“count++”操作后,B线程成功地将1001写入内存,接着A线程将自己寄存器中的count值1001写入内存(覆盖了B线程的1001),由此导致,虽然执行了两个线程,但count的值只累加了一次,这样的情况多发生几次,计算结果自然就低于20000了。

            为了避免以上情况发生,我们给increase()方法加上修饰符synchronized,使得两个线程无法同时调用increase()方法,以保证上面的三步中的任何一步都不会被另外一个线程打断。

             这样,“count++”操作就永远不会因线程切换而出错。代码如下:

    
    
    /**
     * @author sixibiheye
     * @date 2021/8/31
     * @apiNote synchronized用法举例
     */
    
    public class Synchronized implements Runnable{
        //公共资源
        static int count = 0;
        //synchronized实现线程同步
        public synchronized void increase(){
            count++;
        }
        @Override
        public void run() {
            for (int i = 0; i < 10000; i++) {
                increase();
            }
        }
        public static void main(String[] args) throws InterruptedException {
            new Thread(new Synchronized(),"A").start();
            new Thread(new Synchronized(),"B").start();
            Thread.sleep(2000);
            System.out.println(count);
        }
    }
    
    

            现在来看运行结果:

            没有任何问题。

            此外,使用synchronized时,有几个需要注意的地方,请看下面的代码:

    
    /**
     * @author sixibiheye
     * @date 2021/8/31
     * @apiNote synchronized用法举例
     */
    
    public class Synchronized implements Runnable{
        public synchronized void running() throws InterruptedException {
            System.out.println("1");
            Thread.sleep(1000);
            System.out.println("2");
        }
        @Override
        public void run() {
            try {
                running();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        public static void main(String[] args) throws InterruptedException {
            //用同一个类创建两个对象
            Synchronized sync1 = new Synchronized();
            new Thread(sync1).start();
            Synchronized sync2 = new Synchronized();
            new Thread(sync2).start();
        }
    }
    
    

            如果我们使用不同的对象访问,那么结果可能是不同步的

            因为synchronized修饰实例方法时锁的对象是this对象,而使用两个对象去访问,不是同一把锁。如果我们用同一对象访问:

    //只创建一个对象
    Synchronized sync = new Synchronized();
    new Thread(sync).start();
    new Thread(sync).start();

            那结果是同步的:

              接着,我们来看synchronized修饰静态方法的例子:

    
    /**
     * @author sixibiheye
     * @date 2021/8/31
     * @apiNote synchronized用法举例
     */
    
    public class Synchronized implements Runnable{
        public static synchronized void running() throws InterruptedException {
            System.out.println("1");
            Thread.sleep(1000);
            System.out.println("2");
        }
        @Override
        public void run() {
            try {
                running();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        public static void main(String[] args) throws InterruptedException {
            //用同一个类创建两个对象
            Synchronized sync1 = new Synchronized();
            new Thread(sync1).start();
            Thread.sleep(2000);
            Synchronized sync2 = new Synchronized();
            new Thread(sync2).start();
        }
    }
    
    

            虽然有sync1,sync2两个对象,但是它们是同一个类对象(Synchronized.class)产生的,而synchronized修饰静态方法时锁的是 Synchronized.class,因此两个线程仍然是同步的:

             最后一种,synchronzied修饰同步代码块,它可以锁任何指定的对象,语法示例如下:

    synchronized (this){
          System.out.println("1");
          Thread.sleep(1000);
          System.out.println("2");
    }

            this指代当前实例对象,可以换为任何对象。

            那么为什么要使用同步代码块呢?

            在某些情况下,我们编写的方法体可能比较庞大,同时又有一些耗时的操作,如果对整个方法体进行同步,效率会大大降低,所以我们希望能够只同步必要的代码块,对于一些不需要同步的或者耗时较长的操作,放到同步代码块之外,比如:

    
    /**
     * @author sixibiheye
     * @date 2021/8/31
     * @apiNote synchronized用法举例
     */
    
    public class Synchronized implements Runnable{
        public void running() throws InterruptedException {
            for (int i = 0; i < 5; i++) {
                System.out.println("这是耗时操作。");
            }
            //需要同步的代码块写下面
            synchronized (this){
                System.out.println("1");
                Thread.sleep(1000);
                System.out.println("2");
            }
        }
        @Override
        public void run() {
            try {
                running();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        public static void main(String[] args) throws InterruptedException {
            Synchronized sync = new Synchronized();
            new Thread(sync).start();
            new Thread(sync).start();
        }
    }
    
    

            运行结果如下:

            再运行一次:

            结果表明,需要同步的代码块确实实现了同步。 

    二.  lock         

            前面使用的synchronized关键字可以实现多线程间的同步互斥,其实,在JDK1.5后新增的ReentrantLock 类同样可以实现这个功能,而且在使用上比 synchronized ​​​​​​​更为灵活。

            翻阅源码,可以看到 ReentrantLock 类实现了Lock接口:

             下面我们用ReentrantLock类来实现简单的线程同步:

    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @author sixibiheye
     * @date 2021/8/31
     * @apiNote ReentrantLock实现线程同步
     */
    public class LockDemo implements Runnable{
        private Lock lock = new ReentrantLock();
        /**
         * 
         * 按照规范,
         * lock()后面应紧跟try代码块,
         * 并将unlock()放到finally块的第一行
         * 
         */
        @Override
        public void run() {
            //上锁
            lock.lock();
            try {
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName() + "线程中的i=" + i);
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //释放锁
                lock.unlock();
            }
        }
        public static void main(String[] args) 
            new Thread(new LockDemo(),"A").start();
            new Thread(new LockDemo(),"B").start();
        }
    }
    
    

            其实关于Lock 和 ReentrantLock,还有许多其他的用法,限于篇幅,就不一一介绍了,有兴趣的小伙伴们可以查阅相关资料。

            了解了synchronized和ReentrantLock,对于上一章提出的三个线程安全问题,便可以轻松地解决了。下面提供使用synchronized的解决方式:

    
    /**
     * @author sixibiheye
     * @date 2021/8/28
     * @apiNote 线程安全问题一-------取钱问题
     */
    public class UnsafeBank {
    
        public static void main(String[] args) {
            //账户
            Account account = new Account(100,"买房基金");
            //你和你的妻子都要取钱
            Drawing you = new Drawing(account,50,"你");
            Drawing girlFriend = new Drawing(account,100,"妻子");
            you.start();
            girlFriend.start();
        }
    }
    
    //账户
    class Account{
        int money; //余额
        String name; //卡名
        public Account(int money,String name){
            this.money = money;
            this.name = name;
        }
    }
    
    //银行
    class Drawing extends Thread{
        Account account; //账户
        int drawingMoney; //取的钱
        int nowMoney; //现手上有的钱
        public Drawing(Account account,int drawingMoney,String name){
            super(name);
            this.account = account;
            this.drawingMoney = drawingMoney;
        }
        @Override
        public void run() {
            //注意锁的是account对象,而不是this
            synchronized (account){
                if(account.money - drawingMoney < 0){
                    System.out.println(Thread.currentThread().getName() + "钱不够了,取不了!");
                    return;
                }
                try {
                    sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //卡内余额
                account.money = account.money - drawingMoney;
                //手里的钱
                nowMoney = nowMoney + drawingMoney;
                //打印
                System.out.println(account.name + "余额为:" + account.money);
                System.out.println(Thread.currentThread().getName() + "手里的钱:" + nowMoney);
            }
            }
    }
    
    
    
    /**
     * @author sixibiheye
     * @date 2021/8/27
     * @apiNote 线程安全问题一-------买票问题
     */
    public class UnsafeBuyTicket {
        public static void main(String[] args) {
            BuyTicket buyTicket = new BuyTicket();
            new Thread(buyTicket,"小红").start();
            new Thread(buyTicket,"小白").start();
            new Thread(buyTicket,"小黑").start();
        }
    }
    
    class BuyTicket implements Runnable{
        //票数
        private int tickets = 10;
        //线程停止的标志位
        private boolean flag = true;
        //直接同步实例方法即可
        private synchronized void buy() throws InterruptedException {
            //判断是否有票
            if(tickets <= 0){
                flag = false;
                return;
            }
            //模拟延时,保证多人都能买到票
            Thread.sleep(20);
            //买票
            System.out.println(Thread.currentThread().getName() + "拿到了第" + tickets-- +"张票");
    
        }
        @Override
        public void run() {
            while (flag){
                try {
                    buy();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @author sixibiheye
     * @date 2021/8/28
     * @apiNote 线程安全问题一-------列表问题
     */
    
    public class UnsafeList {
        public static void main(String[] args) throws InterruptedException {
            List<String> list = new ArrayList<String>();
            for (int i = 0; i < 10000; i++) {
                new Thread( () -> {
                    //同步需要修改的list对象
                    synchronized (list){
                        list.add(Thread.currentThread().getName());
                    }
                }).start();
            }
            //sleep保证上述for循环跑完再输出
            Thread.sleep(3000);
            //输出列表大小
            System.out.println(list.size());
        }
    }
    

            小伙伴们可以思考一下如果使用ReentrantLock应该如何解决。

            下一章,我们将着重介绍Thread类中的一些常用方法(点击跳转),好啦~本章的内容到这就结束了,喜欢的小伙伴们点个赞鼓励支持一下吧~ 

             

    展开全文
  • 主要介绍了java线程编程同步器Future和FutureTask解析及代码示例,对二者进行了详细介绍,分析了future的源码,最后展示了相关实例代码,具有一定参考价值 ,需要的朋友可以了解下。
  • 主要介绍了Java实现线程同步方法及原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • 本篇文章是对Java线程同步锁的选择方法进行了详细的分析介绍,需要的朋友参考下
  • 本文主要介绍java线程-同步块的知识,这里整理了相关的详细资料及简单示例代码,有兴趣的小伙伴可以参考下
  • 主要介绍了Java线程同步器代码详解,文章分别介绍了是CountDownLatch,Semaphore,Barrier和Exchanger以及其相关代码示例,具有一定参考价值,需要的朋友可以了解下。
  • 主要介绍了Java线程间通信不同步问题,结合实例形式分析了java线程间通信不同步问题的原理并模拟实现了线程通信不同步情况下的异常输出,需要的朋友可以参考下
  • 线程同步机制包括哪些什么是多线程同步机制,多线程同步机制包括:1。临界段用于实现“独占占有”;2.信号量用于跟踪有限的资源;3.互斥是核心对象,可以实现不同线程之间的“独占占有”;4.事件通常用于设计一些...

    多线程同步机制包括哪些什么是多线程同步机制,多线程同步机制包括:1。临界段用于实现“独占占有”;2.信号量用于跟踪有限的资源;3.互斥是核心对象,可以实现不同线程之间的“独占占有”;4.事件通常用于设计一些自定义的同步对象。

    d1e6d0f7acc6e3e26f6ed33423afa255.png

    操作环境:Windows7系统,戴尔G3电脑。

    多线程同步机制包括:

    1、关键部分

    临界段用来实现“独占占有”。应用范围在单个进程的线程之间。它是:

    局部对象,不是核心对象。

    快速高效。

    不能有多个关键部分同时等待。

    无法检测它是否已被线程放弃。

    可以实现线程间互斥,但不能用来实现同步。

    2、信号量

    信号量用于跟踪有限的资源。它是:

    核心对象。

    没有主人。

    可以命名,所以可以被其他进程打开。

    它可以被任何线程释放。

    既能实现线程间的互斥,又能实现线程间的同步。

    跨进程使用时,如果拥有信号量的线程意外结束,则不会通知其他进程。

    3、互斥

    互斥是一个核心对象,可以实现不同线程之间的“独占占有”,即使那些线程属于不同的进程。它是:

    核心对象。

    如果拥有互斥体的线程结束,将会产生一个“已放弃”的错误消息。

    我可以被命名,所以它可以被其他进程打开。

    只能由拥有它的线程释放。

    当跨进程使用时,如果带有互斥体的进程意外结束,其他进程将收到WAIT _ ABANDOEND消息。

    4、事件

    事件对象通常用于重叠输入/输出,或者设计一些自定义同步对象。它是:

    核心对象。

    但是用来实现线程的互斥和同步。

    可以命名,所以可以被其他进程打开。

    当跨进程使用时,如果拥有信号量的线程意外结束,则不会通知其他进程。

    注意:临界区和互斥体都有“线程所有权”的概念,所以不能用来实现线程间的同步,只能用来实现互斥。原因是创建临界段或互斥体的线程可以无条件进入被保护程序段,而不需要等待LeaveCriticalSection(),因为它有这个权限。此外,互斥体可以很好地处理放弃操作。如果线程在没有释放对象的情况下意外终止,其他线程可以等待WAIT _ ADOPTED _ 0。但是无论是事件还是信号量都做不到。

    事件和信号量可以实现线程和进程之间的互斥和同步。

    就使用效率而言,关键区域的效率最高,因为它不是内核对象,而其他三个都是核心对象,需要通过操作系统来实现,效率相对较低。

    但是如果你想跨进程使用它,你仍然需要互斥体、事件对象和信号量。

    总之:设计的时候,一开始尽量不要用全局变量,如果不行,考虑用Inter.首先是()函数,然后是临界区对象,最后是事件、互斥体和信号量。以上是多线程同步机制的详细内容。请多关注其他关于Lei.com PHP知识的相关文章!

    展开全文
  • 主要介绍了 Java多线程 线程同步与死锁的相关资料,需要的朋友可以参考下
  • 主要介绍了Java线程同步Lock同步锁代码示例,首先介绍了Java线程同步的原理,然后对lock同步锁作了简要阐述,分享了代码示例,具有一定参考价值,需要的朋友可以了解下。
  • java实现多线程并发中的读者与写者问题,能够实现多线程对临界资源的同步有序访问。 具体实现为: 给定一个队列A[1-10][1-100000]、元素编号1-10,其中每个元素包含10万个随机数。创建若干个线程,各循环100次;...
  • Java线程同步论文.doc
  • 主要介绍了java多线程之线程同步七种方式代码示例,具有一定参考价值,需要的朋友可以了解下。
  • 主要介绍了浅谈Java线程编程中Boolean常量的同步问题,主要针对线程之间同步了不同的布尔对象的问题,需要的朋友可以参考下
  • 主要介绍了Java中的线程同步与ThreadLocal无锁化线程封闭实现,Synchronized关键字与ThreadLocal变量的使用是Java中线程控制的基础,需要的朋友可以参考下
  • java线程同步的7种方法

    千次阅读 2017-10-25 09:12:14
     将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,   从而保证了该变量的唯一性和准确性。 1.同步方法   即有synchronized关键字修饰的方法。...
  • Java实现线程同步的五种方法

    千次阅读 2021-01-04 22:03:34
    一.使用synchronized关键字 由于每个java对象都有一个内置锁,用synchronized修饰方法或者代码块时...由于同步是一种高开销的工作,所以尽量减少同步的内容,只需同步代码块就可以。 1.修饰方法 在这里插入代码片 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 580,032
精华内容 232,012
关键字:

java线程之间的同步

java 订阅