zoukankan      html  css  js  c++  java
  • Java进程-同步与异步

    java synchronized详解

    Java中Synchronized的用法

    知识点1、Synchronized同步静态方法和非静态方法总结

    1、synchronized修饰代码块

    1、两个并发的线程访问同一个对象中的synchronized(this)同步代码块时,同一时间内只有一个线程执行,另外一个线程需要等到当前线程结束之后才能执行。

    package MapTest;
    
    public class Thread1 implements Runnable{
        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see Thread#run()
         */
        /**
         * 一旦一个对象实现了runable接口,那么就意味着创建了一个线程,启动线程的时候该对象的run()方法
         * 也就被启动
         */
        @Override
        public void run() {
           synchronized (this){
               for(int i = 0; i < 10; i++){
                   System.out.println(Thread.currentThread().getName() + "===" + i);
               }
           }
        }
    
        public static void main(String[] args){
            //实例化
            Thread1 thread1 = new Thread1();
            //创建两个线程
            Thread a = new Thread(thread1,"A");
            Thread b = new Thread(thread1,"B");
            //先启动b线程,再启动a线程
            b.start();
            a.start();
        }
        /**
         * A===0
         A===1
         A===2
         A===3
         A===4
         A===5
         A===6
         A===7
         A===8
         A===9
         B===0
         B===1
         B===2
         B===3
         B===4
         B===5
         B===6
         B===7
         B===8
         B===9
         */
    
    
    }

    2、当一个线程访问对象中的同步代码块(synchronized(this)),另外一个线程仍然可以访问该对象中的非同步代码块。

    package MapTest;
    
    public class Thread2 {
    
        //synchronized代码块
        public void fun1() throws InterruptedException {
            synchronized (this) {
                for(int i = 0; i < 5; i++){
                    System.out.println(Thread.currentThread().getName() + "==" + i);
                    Thread.sleep(100);
                }
            }
        }
    
        //非synchronized代码块
        public void fun2() throws InterruptedException {
            for(int i = 0; i < 5; i++){
                System.out.println(Thread.currentThread().getName() + "==" + i);
                Thread.sleep(100);
            }
        }
        
        public static void main(String[] args){
            //实例化一个对象
            Thread2 thread2 = new Thread2();
    
            //创建一个进程a
            Thread a = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        thread2.fun1();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"a");
    
            //创建一个进程b
            //因为该对象没有继承runable类,所以实例化该对象的时候,创建线程并不会call里面的方法
            //所以需要new一个runable(),通过对象来调用方法(因为是静态方法调用非静态方法)
            Thread b = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        thread2.fun2();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },"b");
    
            a.start();
            b.start();
        }
    
        /**
         * a==0
         b==0
         b==1
         a==1
         a==2
         b==2
         a==3
         b==3
         b==4
         a==4
         */
    }
    

    3、当一个线程访问对象的同步代码块(synchronized(this)),另外一个线程对该对象中的其他同步代码块(synchronized(this))的访问将被阻塞。

        //非synchronized代码块===》改成synchronized代码块
        public void fun2() throws InterruptedException {
            synchronized (this) {
                for(int i = 0; i < 5; i++){
                    System.out.println(Thread.currentThread().getName() + "==" + i);
                    Thread.sleep(100);
                }
            }
        }
    b==0
    b==1
    b==2
    b==3
    b==4
    a==0
    a==1
    a==2
    a==3
    a==4
    需要注意的是哪个线程先被执行,不是看线程调用start方法的顺序,也不是创建线程的顺序。而是由CPU自动随机调用哪个线程先执行,当然如果针对一个线程组得话,就有先后顺序了,如下面得存钱和取前得操作

    4、存钱和取钱的操作

    account对象

    package MapTest;
    
    public class Account {
        private String name;
        private float amount;
    
        public Account(String name, float amount) {
            this.name = name;
            this.amount = amount;
        }
    
        //存钱
        public void deposit(float amt) throws InterruptedException {
            amount += amt;
            Thread.sleep(1000);
        }
    
        //取钱
        public void withdraw(float amt) throws InterruptedException {
            amount -= amt;
            Thread.sleep(1000);
        }
    
        public float show(){
            return amount;
        }
    
    }

    账户操作对象类

    package MapTest;
    
    public class AccountOperator implements Runnable {
    
        private Account account;
    
        public AccountOperator(Account account) {
            this.account = account;
        }
    
        /**
         * When an object implementing interface <code>Runnable</code> is used
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see Thread#run()
         */
        @Override
        public void run() {
            try {
                synchronized (this) {
                    account.deposit(1000);
                    account.withdraw(100);
                    System.out.println(Thread.currentThread().getName() + "==" + account.show());
                }
                /**
                 * thread1==1400.0
                 thread4==2300.0
                 thread3==3200.0
                 thread2==4100.0
                 thread0==5000.0
                 */
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
    
        }
    }

    创建线程组操作

    package MapTest;
    
    public class AccountTest {
    
        public static void main(String[] args){
            //实例化一个账户
            Account zs = new Account("zs",500);
            //创建一个账户操作对象
            AccountOperator zsOperator = new AccountOperator(zs);
    
            //创建五个线程来操作该账户
            final int THREAD_NUM = 5;
            //创建一个线程组
            Thread[] threads = new Thread[THREAD_NUM];
            for(int i = 0; i < THREAD_NUM; i++){
                //利用账户操作对象创建线程
                threads[i] = new Thread(zsOperator,"thread" + i);
                threads[i].start();
            }
        }
    }

    最后打印的结果如下

    thread0==1400.0
    thread3==2300.0
    thread4==3200.0
    thread2==4100.0
    thread1==5000.0
    可见每个线程都是在前一个线程所有操作结束的基础之上才进行操作,也就是说我每次存1000取500的操作都是原子性的。都必须处理完才能进行第二次操作。因为这里的存和取得操作都属于同步代码块synchronized,用官方一点得说法就是说一个线程访问该对象得同步代码块时,其他进程对该代码块得访问将被阻塞。

    2、synchronized修饰方法

    1、修饰非静态方法

    package MapTest;
    
    public class SynFun {
        //创建一个synchronized修饰得方法
        public synchronized void fun() throws InterruptedException {
            for(int i = 0; i < 5; i++){
                System.out.println(Thread.currentThread().getName() + "==" + i);
                Thread.sleep(1000);
            }
        }
    }
    
    package MapTest;
    
    public class TestFun {
        public static void main(String[] args){
    
            SynFun synFun = new SynFun();
            Thread a = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        synFun.fun();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "A");
    
            Thread b = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        synFun.fun();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "B");
    
            b.start();
            a.start();
    
        }
        /**
         * A==0
         A==1
         A==2
         A==3
         A==4
         B==0
         B==1
         B==2
         B==3
         B==4
        这里和同步代码块得意思差不多
         */
    }

    对于非静态方法,实际上是对该方法得对象加锁,也就是“对象锁”。也就是说同一个对象中所有得非静态方法共用一把对象锁,不同得进程不能同时访问该对象中得所有非静态方法。这时候访问该对象静态方法得所有进程是互斥得。其实和代码块也差不多。只有在两个进程访问不同对象中的非静态方法时,才不是互斥的,因为这时有两把对象锁。

    2、修饰静态方法

    package MapTest;
    
    public class SynFun {
        //创建一个synchronized修饰得方法
        public synchronized static void  fun1() throws InterruptedException {
            for(int i = 0; i < 5; i++){
                System.out.println(Thread.currentThread().getName() + "=静态=" + i);
                Thread.sleep(1000);
            }
        }
    
        //创建一个synchronized修饰得方法
        public synchronized  void  fun2() throws InterruptedException {
            for(int i = 0; i < 5; i++){
                System.out.println(Thread.currentThread().getName() + "=非静态=" + i);
                Thread.sleep(1000);
            }
        }
    }
    
    package MapTest;
    
    public class TestFun {
        public static void main(String[] args){
    
            SynFun synFun = new SynFun();
            //访问静态方法得进程A1
            Thread a = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        synFun.fun1();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "A1");
    
            //访问静态方法得进程A2
            Thread b = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        synFun.fun1();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "A2");
    
            //访问非静态方法得进程B1
            Thread c = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        synFun.fun2();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "B1");
    
            //访问非静态方法得进程B2
            Thread d = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        synFun.fun2();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, "B2");
    
            b.start();
            a.start();
            c.start();
            d.start();
    
        }
        /**
         *A2=静态=0
         B2=非静态=0
         B2=非静态=1
         A2=静态=1
         A2=静态=2
         B2=非静态=2
         A2=静态=3
         B2=非静态=3
         A2=静态=4
         B2=非静态=4
         A1=静态=0
         B1=非静态=0
         A1=静态=1
         B1=非静态=1
         B1=非静态=2
         A1=静态=2
         A1=静态=3
         B1=非静态=3
         B1=非静态=4
         A1=静态=4
         */
    }
    可见,不同的进程在访问同一个对象中得静态与非静态方法之间不是互斥的,因为一个使用的时对象锁,一个使用的是类对象锁。不同的进程在访问同一个对象的非静态方法是互斥的,因为synchronized修饰静态方法实际上是对类对象枷锁。也就是永远只有一把锁,因为一个对象只对应一个类对象(.class)。



    欢迎关注我的公众号:小秋的博客 CSDN博客:https://blog.csdn.net/xiaoqiu_cr github:https://github.com/crr121 联系邮箱:rongchen633@gmail.com 有什么问题可以给我留言噢~
  • 相关阅读:
    Linux篇---ftp服务器的搭建
    【Spark篇】---SparkStreaming+Kafka的两种模式receiver模式和Direct模式
    【Spark篇】---Spark故障解决(troubleshooting)
    【Spark篇】---Spark解决数据倾斜问题
    【Spark篇】---Spark调优之代码调优,数据本地化调优,内存调优,SparkShuffle调优,Executor的堆外内存调优
    【Redis篇】Redis持久化方式AOF和RDB
    【Redis篇】Redis集群安装与初始
    【Redis篇】初始Redis与Redis安装
    Git提示“warning: LF will be replaced by CRLF”
    Git 忽略特殊文件
  • 原文地址:https://www.cnblogs.com/flyingcr/p/10326901.html
Copyright © 2011-2022 走看看