zoukankan      html  css  js  c++  java
  • 关于synchronized关键字

    ********************************非静态同步方法****************************************************

    个人观点,或许是想通了:

    *****不加 synchronized 关键字,Example 对象根本上就没有锁,各个线程就可以随便进出访问这个对象了;两个线程同时执行execute()方法,输出是两组并发的。

    *****一旦加上 synchronized 关键字,Example 对象就被第一个访问的线程锁住了,第二个线程就处于BLOCKED:SYNCHRONIZED(自己理解的,这个状态是还没有获得锁的,一旦获得锁之后,就会变成RUNNABLE状态,等待某某某的调度);会先输出一组0到9,然后再输出下一组,说明两个线程是顺次执行的。

    *****当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。

    *****Java中的每个对象都有一个锁(lock),或者叫做监视器(monitor),当一个线程访问某个对象的synchronized方法时,将该对象上锁其他任何线程都无法再去访问该对象的synchronized方法了(这里是指所有的同步方法,而不仅仅是同一个方法),直到之前的那个线程执行方法完毕后(或者是抛出了异常),才将该对象的锁释放掉,其他线程才有可能再去访问该对象的synchronized方法。

    *****注意这时候是给对象上锁,如果是不同的对象,则各个对象之间没有限制关系。

    *****尝试在代码中构造第二个线程对象时传入一个新的Example对象,则两个线程的执行之间没有什么制约关系。

    public class Main2 {
        public static void main(String[] args) {
            Example example = new Example();
    
            Thread t1 = new Thread1(example);
            Thread t2 = new Thread1(example);
    
            t1.start();
            t2.start();
        }
    
    }
    
    class Example {
        public synchronized void execute() {
            for (int i = 0; i < 10; ++i) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Hello: " + i);
            }
        }
    
    }
    
    class Thread1 extends Thread {
        private Example example;
    
        public Thread1(Example example) {
            this.example = example;
        }
    
        @Override
        public void run() {
            example.execute();
        }
    
    }

     再看如下例子:因为线程t1访问execute()方法的时候,因为这个是synchronized方法(同步方法),所以就会给Example对象上锁了,这个时候,线程t2也无法访问execute22()这个同步方法(直到线程t1访问完execute()方法之后)

    但是,如果一旦execute22()方法没有synchronized关键字之后,线程2就可以随机访问这个execute22()方法了,不受t1访问同步execute()方法的影响;

    说白了,如果其中一个方法没有关键字,另外一个方法有关键字,被锁的对象压根就奈何不了非同步方法;

    public class Main2 {
        public static void main(String[] args) {
            Example example = new Example();
    
            Thread t1 = new Thread1(example);
            Thread t2 = new Thread2(example);
    
            t1.start();
            t2.start();
        }
    
    }
    
    class Example {
        public synchronized void execute() {
            for (int i = 0; i < 20; ++i) {
                try {
                    Thread.sleep((long) Math.random() * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();    
                }
                System.out.println("Hello: " + i);
            }
        }
    
        public synchronized void execute22() {
            for (int i = 0; i < 20; ++i) {
                try {
                    Thread.sleep((long) Math.random() * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("World: " + i);
            }
        }
    
    }
    
    class Thread1 extends Thread {
        private Example example;
    
        public Thread1(Example example) {
            this.example = example;
        }
    
        @Override
        public void run() {
            example.execute();
        }
    
    }
    
    class Thread2 extends Thread {
        private Example example;
    
        public Thread2(Example example) {
            this.example = example;
        }
    
        @Override
        public void run() {
            example.execute22();
        }
    
    }

    再看下面那个例子,因为线程t1获取的是example1 对象锁,线程t2获取的是example2对象锁,不同的对象,他们之间一毛钱关系都没有

    package test;
    
    public class Main2 {
        public static void main(String[] args) {
            Example example1 = new Example();
            Example example2 = new Example();
    
            Thread t1 = new Thread1(example1);
            Thread t2 = new Thread2(example2);
    
            t1.start();
            t2.start();
        }
    
    }
    
    class Example {
        public synchronized void execute() {
            for (int i = 0; i < 20; ++i) {
                try {
                    Thread.sleep((long) Math.random() * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();    
                }
                System.out.println("Hello: " + i);
            }
        }
    
        public synchronized void execute22() {
            for (int i = 0; i < 20; ++i) {
                try {
                    Thread.sleep((long) Math.random() * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("World: " + i);
            }
        }
    
    }
    
    class Thread1 extends Thread {
        private Example example;
    
        public Thread1(Example example) {
            this.example = example;
        }
    
        @Override
        public void run() {
            example.execute();
        }
    
    }
    
    class Thread2 extends Thread {
        private Example example;
    
        public Thread2(Example example) {
            this.example = example;
        }
    
        @Override
        public void run() {
            example.execute22();
        }
    
    }

     ********************************静态同步方法****************************************************

    一个synchronized关键字修饰的方法同时又被static修饰,之前说过,非静态的同步方法会将对象上锁,但是静态方法不属于对象,而是属于类,它会将这个方法所在的类的Class对象上锁

    一个类不管生成多少个对象,它们所对应的是同一个Class对象。

    所以如果是静态方法的情况(execute()和execute2()都加上static关键字),即便是向两个线程传入不同的Example对象,这两个线程仍然是互相制约的,必须先执行完一个,再执行下一个。

    public class Main2 {
        public static void main(String[] args) {
            
            Example example1 = new Example();
            Example example2 = new Example();
            // 此处即便传入不同的对象,静态方法同步仍然不允许多个线程同时执行
            Thread t1 = new Thread1(example1);
            Thread t2 = new Thread2(example2);
    
            t1.start();
            t2.start();
        }
    
    }
    
    class Example {
        public synchronized static void execute() {
            for (int i = 0; i < 20; ++i) {
                try {
                    Thread.sleep((long) Math.random() * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Hello: " + i);
            }
        }
    
        public synchronized static void execute2() {
            for (int i = 0; i < 20; ++i) {
                try {
                    Thread.sleep((long) Math.random() * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("World: " + i);
            }
        }
    
    }
    
    class Thread1 extends Thread {
        private Example example;
    
        public Thread1(Example example) {
            this.example = example;
        }
    
        @Override
        public void run() {
            Example.execute();
        }
    
    }
    
    class Thread2 extends Thread {
        private Example example;
    
        public Thread2(Example example) {
            this.example = example;
        }
    
        @Override
        public void run() {
            Example.execute2();
        }
    
    }

      结论:

      如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在的类所对应的Class对象。Java中,无论一个类有多少个对象,这些对象会对应唯一一个Class对象,因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,它们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始。

    (个人观点:如果两个方法只有一个是同步方法,明显是两个方法同步输出;如果两个方法只有一个是静态方法,输出结果也是并发输出,个人理解是一个是锁具体对象,一个是类所对应的Class对象,锁的东东不一样)从其它地方看到的:静态和非静态方法的锁互不干预

    **********************************************synchronized块**************************************

    线程t1访问execute()方法时,在synchronized块中,对象 object 被锁了。因为线程t2访问的execute2()方法里面的synchronized块中对象 object 也被锁了,所以直到线程t1释放object锁之前,线程t2都无法访问execute2()方法里面的synchronized(注意是:execute2()方法里面的synchronized,而不是execute2()方法

    public class Main2 {
        public static void main(String[] args) {
            Example example = new Example();
    
            Thread t1 = new Thread1(example);
            Thread t2 = new Thread2(example);
    
            t1.start();
            t2.start();
        }
    
    }
    
    class Example {
        private Object object = new Object();
    
        public void execute() {
            synchronized (object) {   // 如果换成Example.class,它和同步的static函数产生的效果是一样的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。
                for (int i = 0; i < 20; ++i) {
                    try {
                        Thread.sleep((long) Math.random() * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("Hello: " + i);
                }
    
            }
    
        }
    
        public void execute2() {
            synchronized (object) { // 如果换成Example.class,它和同步的static函数产生的效果是一样的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。
                for (int i = 0; i < 20; ++i) {
                    try {
                        Thread.sleep((long) Math.random() * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("World: " + i);
                }
    
            }
    
        }
    
    }
    
    class Thread1 extends Thread {
        private Example example;
    
        public Thread1(Example example) {
            this.example = example;
        }
    
        @Override
        public void run() {
            example.execute();
        }
    
    }
    
    class Thread2 extends Thread {
        private Example example;
    
        public Thread2(Example example) {
            this.example = example;
        }
    
        @Override
        public void run() {
            example.execute2();
        }
    
    }

     留意前后两个打印*************************以及&&&&&&&&&&&&&&&&&&&&&&&&&

    public class Main2 {
        public static void main(String[] args) {
            Example example = new Example();
    
            Thread t1 = new Thread1(example);
            Thread t2 = new Thread2(example);
    
            t1.start();
            t2.start();
        }
    
    }
    
    class Example {
        private Object object = new Object();
    
        public void execute() {
            synchronized (object) {
                for (int i = 0; i < 20; ++i) {
                    try {
                        Thread.sleep((long) Math.random() * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("Hello: " + i);
                }
    
            }
    
        }
    
        public void execute2() {
            System.out.println("*************");// 在第一位输出:估计是线程t1获取object对象锁的时候,需要时间,所以在此之前,打印了这个了
            synchronized (object) {
                for (int i = 0; i < 20; ++i) {
                    try {
                        Thread.sleep((long) Math.random() * 1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("World: " + i);
                }
    
            }
            // 这个在最后输出:估计是因为在此之前,打印完*************,再打印hello(打印hello区间线程t2在同步阻塞区,不往下走),然后打印world,最后打印&&&&&&&&&&&&&
            System.out.println("&&&&&&&&&&&&&");
    
        }
    
    }
    
    class Thread1 extends Thread {
        private Example example;
    
        public Thread1(Example example) {
            this.example = example;
        }
    
        @Override
        public void run() {
            example.execute();
        }
    
    }
    
    class Thread2 extends Thread {
        private Example example;
    
        public Thread2(Example example) {
            this.example = example;
        }
    
        @Override
        public void run() {
            example.execute2();
        }
    
    }

    输出:

    *************
    Hello: 0
    Hello: 1
    Hello: 2
    Hello: 3
    Hello: 4
    Hello: 5
    Hello: 6
    Hello: 7
    Hello: 8
    Hello: 9
    Hello: 10
    Hello: 11
    Hello: 12
    Hello: 13
    Hello: 14
    Hello: 15
    Hello: 16
    Hello: 17
    Hello: 18
    Hello: 19
    World: 0
    World: 1
    World: 2
    World: 3
    World: 4
    World: 5
    World: 6
    World: 7
    World: 8
    World: 9
    World: 10
    World: 11
    World: 12
    World: 13
    World: 14
    World: 15
    World: 16
    World: 17
    World: 18
    World: 19
    &&&&&&&&&&&&&

    例子程序4所达到的效果和例子程序2的效果一样,都是使得两个线程的执行顺序进行,而不是并发进行,当一个线程执行时,将object对象锁住,另一个线程就不能执行对应的块。

    synchronized方法实际上等同于用一个synchronized块包住方法中的所有语句,然后在synchronized块的括号中传入this关键字。当然,如果是静态方法,需要锁定的则是class对象。 

    可能一个方法中只有几行代码会涉及到线程同步问题,所以synchronized块比synchronized方法更加细粒度地控制了多个线程的访问,只有synchronized块中的内容不能同时被多个线程所访问,方法中的其他语句仍然可以同时被多个线程所访问(包括synchronized块之前的和之后的)。

    注意:被synchronized保护的数据应该是私有的

    结论:

    synchronized方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法;

    synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、synchronized块之外的其他代码是可以被多个线程同时访问到的

    public class Main2 {
        public static void main(String[] args) {
            Example example = new Example();
            Thread t1 = new Thread1(example);
            t1.start();
        }
    }
                                                                                                 
    class Example {                                                                           class Example {                                             
        public void execute() {                                                                   public synchronized void execute() {                     
            synchronized (this) {                                                                     for (int i = 0; i < 20; ++i) {                       
                for (int i = 0; i < 20; ++i) {                                                            try {                                            
                    try {                                                                                     Thread.sleep((long) Math.random() * 1000);   
                        Thread.sleep((long) Math.random() * 1000);                                        } catch (InterruptedException e) {               
                    } catch (InterruptedException e) {                                                        e.printStackTrace();                         
                        e.printStackTrace();                                                              }                                                
                    }                                                                                     System.out.println("Hello: " + i);               
                    System.out.println("Hello: " + i);                                                }                                                    
                }                                                                                 }                                                        
            }                                                                                 }                                                        
        }                                                                                        
    }                                                                      类似于:                                                    
                                                                                                 
    class Thread1 extends Thread {                                                              class Thread1 extends Thread {        
        private Example example;                                                                    private Example example;           
        public Thread1(Example example) {                                                           public Thread1(Example example) {  
            this.example = example;                                                                     this.example = example;        
        }                                                                                           }                                  
        @Override                                                                                   @Override                          
        public void run() {                                                                         public void run() {                
            example.execute();                                                                          example.execute();            
        }                                                                                           }                                 
    }                                                                                           } 

     线程的通信的例子:

    class NumberHolder {
        private int number;                                                            //in******Thread-0 ,(1)线程0抢得锁
                                                                                       //1Thread-0  (1)number为0并自增为1,打印
        public synchronized void increase() {                                          //in******Thread-0 (2)线程0抢得锁,number为1,线程0释放锁,并进入wait状态,其它线程重新争夺锁
            System.out.println("in******"+Thread.currentThread().getName());           //de******Thread-1 (3)线程1抢得锁
            while (0 != number) {                                                      //0Thread-1(3)number为1并自减为0,并唤醒所有的线程(明显这里指唤醒了线程0)
                try {                                                                  //de******Thread-1(4)线程1抢得锁,number为0所以进入wait状态,并释放锁,线程023抢锁
                    wait();                                                            //in******Thread-2(5)线程2抢得锁,number为0
                } catch (InterruptedException e) {                                     //1Thread-2(5)number自增为1,并唤醒其它锁
                    e.printStackTrace();                                               //in******Thread-2
                }                                                                      //de******Thread-3
            }                                                                          //0Thread-3
                                                                                       //1Thread-2
            // 能执行到这里说明已经被唤醒                                                  //0Thread-1
            // 并且number为0                                                             //de******Thread-1
            number++;                                                                  //1Thread-0
            System.out.println(number+Thread.currentThread().getName());               //0Thread-1
                                                                                       //in******Thread-2
            // 通知在等待的线程                                                        //1Thread-2
            this.notifyAll();                                                          //in******Thread-2
        }                                                                              //de******Thread-3
                                                                                       //0Thread-3
        public synchronized void decrease() {                                          //1Thread-2
            System.out.println("de******"+Thread.currentThread().getName());           //in******Thread-2
            while (0 == number) {                                                      //de******Thread-1
                try {                                                                  //0Thread-1
                    wait();                                                            //de******Thread-1
                } catch (InterruptedException e) {                                     //in******Thread-0
                    e.printStackTrace();                                               //1Thread-0
                }                                                                      //0Thread-1
                                                                                       //de******Thread-1
            }                                                                          //1Thread-2
                                                                                       //in******Thread-2
            // 能执行到这里说明已经被唤醒                                                  //de******Thread-3
            // 并且number不为0                                                          //0Thread-3
            number--;                                                                  //de******Thread-3
            System.out.println(number+Thread.currentThread().getName());               //1Thread-2
            notifyAll();                                                               //in******Thread-2
        }                                                                              //0Thread-1
                                                                                       //de******Thread-1
    }                                                                                  //in******Thread-0
                                                                                       //1Thread-0
    class IncreaseThread extends Thread {                                              //0Thread-1
        private NumberHolder numberHolder;                                             //de******Thread-1
                                                                                       //1Thread-2
        public IncreaseThread(NumberHolder numberHolder) {                             //0Thread-3
            this.numberHolder = numberHolder;                                          //de******Thread-3
        }                                                                              //in******Thread-2
                                                                                       //1Thread-2
        @Override                                                                      //in******Thread-2
        public void run() {                                                            //0Thread-1
            for (int i = 0; i < 20; ++i) {                                             //in******Thread-0
                // 进行一定的延时                                                      //1Thread-0
                try {                                                                  //in******Thread-0
                    Thread.sleep((long) Math.random() * 1000);                         //de******Thread-1
                } catch (InterruptedException e) {                                     //0Thread-1
                    e.printStackTrace();                                               //de******Thread-1
                }                                                                      //1Thread-2
                                                                                       //in******Thread-2
                // 进行增加操作                                                        //0Thread-3
                numberHolder.increase();                                               //1Thread-2
            }                                                                          //in******Thread-2
        }                                                                              //0Thread-1
                                                                                       //1Thread-0
    }                                                                                  //in******Thread-0
                                                                                       //de******Thread-1
    class DecreaseThread extends Thread {                                              //0Thread-1
        private NumberHolder numberHolder;                                             //1Thread-2
                                                                                       //in******Thread-2
        public DecreaseThread(NumberHolder numberHolder) {                             //de******Thread-3
            this.numberHolder = numberHolder;                                          //0Thread-3
        }                                                                              //1Thread-2
                                                                                       //in******Thread-2
        @Override                                                                      //de******Thread-1
        public void run() {                                                            //0Thread-1
            for (int i = 0; i < 20; ++i) {                                             //de******Thread-1
                // 进行一定的延时                                                         //1Thread-0
                try {                                                                  //in******Thread-0
                    Thread.sleep((long) Math.random() * 1000);                         //0Thread-1
                } catch (InterruptedException e) {                                     //de******Thread-1
                    e.printStackTrace();                                               //1Thread-2
                }                                                                      //in******Thread-2
                                                                                       //de******Thread-3
                // 进行减少操作                                                          //0Thread-3
                numberHolder.decrease();                                               //de******Thread-3
            }                                                                          //1Thread-2
        }                                                                              //in******Thread-2
                                                                                       //0Thread-1
    }                                                                                  //1Thread-0
                                                                                       //in******Thread-0
    public class Main2 {                                                               //de******Thread-1
        public static void main(String[] args) {                                       //0Thread-1
            NumberHolder numberHolder = new NumberHolder();                            //de******Thread-1
                                                                                       //1Thread-2
            Thread t1 = new IncreaseThread(numberHolder);                              //0Thread-3
            Thread t2 = new DecreaseThread(numberHolder);                              //de******Thread-3
                                                                                       //in******Thread-2
            Thread t3 = new IncreaseThread(numberHolder);                              //1Thread-2
            Thread t4 = new DecreaseThread(numberHolder);                              //in******Thread-2
                                                                                       //0Thread-1
            t1.start();                                                                //1Thread-0
            t2.start();                                                                //in******Thread-0
                                                                                       //de******Thread-1
            t3.start();                                                                //0Thread-1
            t4.start();                                                                //de******Thread-1
                                                                                       //1Thread-2
        }                                                                              //0Thread-3
                                                                                       //de******Thread-3
    }                                                                                  //in******Thread-2
                                                                                       //1Thread-2

    发多少

  • 相关阅读:
    HDU 5273 Dylans loves sequence 暴力递推
    HDU 5285 wyh2000 and pupil 判二分图+贪心
    HDU 5281 Senior's Gun 贪心
    HDU 5651 xiaoxin juju needs help 逆元
    HDU 5646 DZY Loves Partition
    HDU 5366 The mook jong
    HDU 5391Z ball in Tina Town 数论
    HDU 5418 Victor and World 允许多次经过的TSP
    HDU 5642 King's Order dp
    抽屉原理
  • 原文地址:https://www.cnblogs.com/ericguoxiaofeng/p/9154241.html
Copyright © 2011-2022 走看看