zoukankan      html  css  js  c++  java
  • JAVA多线程之Synchronized、wait、notify实例讲解

    一、Synchronized

    synchronized中文解释是同步,那么什么是同步呢,解释就是程序中用于控制不同线程间操作发生相对顺序的机制,通俗来讲就是2点,第一要有多线程,第二当多个线程同时竞争某个资源的时候会有先后顺序。在java中有三种写synchronized的方式:

    第一种:写在普通方法的前面,这种表示对实例对象加锁第二种:写在静态方法前面,这种表示对类对象加锁第三种:写在代码块中,锁是Synchonized括号里配置的对象(可能是实例对象,也可能是类对象)

    总体说来就2种,一种就是锁实例对象,一种锁类对象。

    锁实例对象就是当多个线程同时操作这个实例对象(针对的是特定的实例对象)的时候必须先获取锁,如果无法获取锁,则必须处于等待状态,而和锁类对象区别是,当多个线程同时操作的时候,任何以这个类对象实例化的对象都要获取锁才能操作。

    看下面的例子:

    public class Synchronized_Test {
        public static void main(String[] args){
            Thread t1=new Thread(new MyRunnable());
            Thread t2=new Thread(new MyRunnable());
            t1.start();
            t2.start();
        }
    }
    class MyRunnable implements Runnable{
        private synchronized void synchMethodTest(){
            for(int i=0;i<10;i++)
                System.out.println(Thread.currentThread().getName()+"synchMethodTest: "+i);
        }
        public void run() {
            synchMethodTest();
        }
    }

    其输出结果为:

    Thread-0synchMethodTest: 0
    Thread-1synchMethodTest: 0
    Thread-0synchMethodTest: 1
    Thread-1synchMethodTest: 1
    Thread-0synchMethodTest: 2
    Thread-1synchMethodTest: 2
    Thread-0synchMethodTest: 3
    Thread-1synchMethodTest: 3
    Thread-0synchMethodTest: 4
    Thread-1synchMethodTest: 4
    Thread-0synchMethodTest: 5
    Thread-1synchMethodTest: 5
    Thread-0synchMethodTest: 6
    Thread-1synchMethodTest: 6
    Thread-0synchMethodTest: 7
    Thread-1synchMethodTest: 7
    Thread-0synchMethodTest: 8
    Thread-1synchMethodTest: 8
    Thread-0synchMethodTest: 9
    Thread-1synchMethodTest: 9

          因为synchronized是写在普通方法前,是对特定的实例对象加锁,t1,t2为两个不同的实例,
    所以他们在执行synchronized方法时并不会互相阻塞对方。

          如果将synchronized方法改成static方法,那么就是针对类对象加锁,任何以这个类对象实例化的对象都要获取锁才能操作,
    t1,t2虽然是两个不同的实例,但都是同一个类对象的实例,所以当t1取得锁开始执行synchronized方法后,就会阻塞t2,
    t2需要取得锁之后才能执行,如下:

    public class Synchronized_Test {
        public static void main(String[] args){
            Thread t1=new Thread(new MyRunnable());
            Thread t2=new Thread(new MyRunnable());
            t1.start();
            t2.start();
        }
    }
    class MyRunnable implements Runnable{ private synchronized static void synchMethodTest(){ for(int i=0;i<10;i++) System.out.println(Thread.currentThread().getName()+"synchMethodTest: "+i); } public void run() { synchMethodTest(); } }

    输出结果为:

    Thread-0synchMethodTest: 0
    Thread-0synchMethodTest: 1
    Thread-0synchMethodTest: 2
    Thread-0synchMethodTest: 3
    Thread-0synchMethodTest: 4
    Thread-0synchMethodTest: 5
    Thread-0synchMethodTest: 6
    Thread-0synchMethodTest: 7
    Thread-0synchMethodTest: 8
    Thread-0synchMethodTest: 9
    Thread-1synchMethodTest: 0
    Thread-1synchMethodTest: 1
    Thread-1synchMethodTest: 2
    Thread-1synchMethodTest: 3
    Thread-1synchMethodTest: 4
    Thread-1synchMethodTest: 5
    Thread-1synchMethodTest: 6
    Thread-1synchMethodTest: 7
    Thread-1synchMethodTest: 8
    Thread-1synchMethodTest: 9

    t1执行完成,释放synchronized锁后,t2才能执行。如果在代码块中对类对象也是一样:

    class MyRunnable implements Runnable{
        private void synchMethodTest(){
            synchronized (MyRunnable.class){
                for(int i=0;i<10;i++)
                    System.out.println(Thread.currentThread().getName()+"synchMethodTest: "+i);
            }
        }
        public void run() {
            synchMethodTest();
        }
    }

    注意:
          类方法中,synchronized锁住的是对象this,只有调用同一个对象的方法才需要获取锁。同时,
    同一个对象中所有加了synchronize的方法只能一次调用一个;
          静态方法中,synchronized锁的是整个类对象,类似于(X.class),该类中所有加了synchronized的静态方法,一次只能调用一个。
    比较下面两个例子:

    public class Synchronized_Test {
        public static void main(String[] args){
            Method method=new Method();
            Thread t1=new Thread(new MyRunnable1(method));
            Thread t2=new Thread(new MyRunnable2(method));
            t1.start();
            t2.start();
        }
    }
    class Method{
        public synchronized void Method1(){
            for(int i=0;i<10;i++)
                System.out.println(Thread.currentThread().getName()+" Method1: "+i);
        }
        public synchronized void Method2(){
            for(int i=0;i<10;i++)
                System.out.println(Thread.currentThread().getName()+" Method2 "+i);
        }
    }
    class MyRunnable1 implements Runnable{
        Method method;
        MyRunnable1(Method method){
            this.method=method;
        }
        public void run() {
            method.Method1();
        }
    }
    class MyRunnable2 implements Runnable{
        Method method;
        MyRunnable2(Method method){
            this.method=method;
        }
        public void run(){
            method.Method2();
        }
    }

    输出为:

    Thread-0 Method1: 0
    Thread-0 Method1: 1
    Thread-0 Method1: 2
    Thread-0 Method1: 3
    Thread-0 Method1: 4
    Thread-0 Method1: 5
    Thread-0 Method1: 6
    Thread-0 Method1: 7
    Thread-0 Method1: 8
    Thread-0 Method1: 9
    Thread-1 Method2 0
    Thread-1 Method2 1
    Thread-1 Method2 2
    Thread-1 Method2 3
    Thread-1 Method2 4
    Thread-1 Method2 5
    Thread-1 Method2 6
    Thread-1 Method2 7
    Thread-1 Method2 8
    Thread-1 Method2 9

          因为锁住的整个Method对象,在t1执行method1时,给method对象加锁,当t2要执行method2时,因为时synchronized方法,
    所以首先要取得method对象的锁,才能执行。

    public class Synchronized_Test {
        public static void main(String[] args){
            Method method=new Method();
            Thread t1=new Thread(new MyRunnable1(method));
            Thread t2=new Thread(new MyRunnable2(method));
            t1.start();
            t2.start();
        }
    }
    class Method{
        public synchronized void Method1(){
            for(int i=0;i<10;i++)
                System.out.println(Thread.currentThread().getName()+" Method1: "+i);
        }
        public void Method2(){
            for(int i=0;i<10;i++)
                System.out.println(Thread.currentThread().getName()+" Method2 "+i);
        }
    }
    class MyRunnable1 implements Runnable{
        Method method;
        MyRunnable1(Method method){
            this.method=method;
        }
        public void run() {
            method.Method1();
        }
    }
    class MyRunnable2 implements Runnable{
        Method method;
        MyRunnable2(Method method){
            this.method=method;
        }
        public void run(){
            method.Method2();
        }
    }

    输出为:

    Thread-0 Method1: 0
    Thread-1 Method2 0
    Thread-0 Method1: 1
    Thread-1 Method2 1
    Thread-0 Method1: 2
    Thread-1 Method2 2
    Thread-0 Method1: 3
    Thread-1 Method2 3
    Thread-0 Method1: 4
    Thread-1 Method2 4
    Thread-0 Method1: 5
    Thread-1 Method2 5
    Thread-0 Method1: 6
    Thread-1 Method2 6
    Thread-1 Method2 7
    Thread-1 Method2 8
    Thread-1 Method2 9
    Thread-0 Method1: 7
    Thread-0 Method1: 8
    Thread-0 Method1: 9

          从结果可以看到,t1并没有阻塞t2的运行,因为t2执行的method2方法不带synchronized,所以在执行时并不需要先获得method对象的锁,执行的过程中也就不存在阻塞的情况。

    二、wait、notify和notifyAll

           wait、notify、notifyAll是Object对象的属性,并不属于线程。我们先解释这三个的一个很重要的概念

           wait:使持有该对象的线程把该对象的控制权交出去,然后处于等待状态(这句话很重要,也就是说当调用wait的时候会释放锁并处于等待的状态)notify:通知某个正在等待这个对象的控制权的线程可以继续运行(这个就是获取锁,使自己的程序开始执行,最后通过notify同样去释放锁,并唤醒正在等待的线程)notifyAll:会通知所有等待这个对象控制权的线程继续运行(和上面一样,只不过是唤醒所有等待的线程继续执行)Obj.wait()与Obj.notify()必须要与synchronized(Obj)一起使用,也就是wait与notify是针对已经获取了Obj锁的对象来进行操作

           下面来看一个生产者消费者模型,他们有一个缓冲区,缓冲区有最大限制,当缓冲区满的时候,生产者是不能将产品放入到缓冲区里面的,当然,当缓冲区是空的时候,消费者也不能从中拿出来产品,这就涉及到了在多线程中的条件判断,java中提供了wait和notify方法,他们可以在线程不满足要求的时候让线程让出来资源等待,当有资源的时候再notify他们让他们继续工作。

    import java.util.Date;
    import java.util.LinkedList;
    import java.util.List;
    class EventStorage {
        private int maxSize;
        private List<date> storage;
        public EventStorage() {
            maxSize = 10;
            storage = new LinkedList<date>();
        }
        public synchronized void set() {
            while(storage.size() == maxSize) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            storage.add(new Date());
            System.out.println("Set: "+storage.size());
            notifyAll();
        }
        public synchronized void get() {
            while(storage.size() == 0) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("Get: "+storage.size()+" "+((LinkedList<date>)storage).poll());
            notifyAll();
        }
    }
    class Producer implements Runnable {
        private EventStorage storge;
        public Producer(EventStorage storage) {
            this.storge = storage;
        }
        public void run() {
            for(int i = 0; i < 100; i++) {
                storge.set();
            }
        }
    }
    class Consumer implements Runnable {
        private EventStorage storage;
        public Consumer(EventStorage storage) {
            this.storage = storage;
        }
        public void run() {
            for (int i = 0; i < 100; i++) {
                storage.get();
            }
        }
    }
    public class ProducerAndConsumer {
        public static void main(String[] args){
            EventStorage eventStorage=new EventStorage();
            Thread t1=new Thread(new Producer(eventStorage));
            Thread t2=new Thread(new Consumer(eventStorage));
            t1.start();
            t2.start();
        }
    }
  • 相关阅读:
    欧拉公式
    isap的一些想法
    错误合集
    Hello World
    PAT (Advanced Level) Practice 1068 Find More Coins
    PAT (Advanced Level) 1087 All Roads Lead to Rome
    PAT (Advanced Level) 1075 PAT Judge
    PAT (Advanced Level) 1067 Sort with Swap(0, i)
    PAT (Advanced Level) 1017 Queueing at Bank
    PAT (Advanced Level) 1025 PAT Ranking
  • 原文地址:https://www.cnblogs.com/rinack/p/8950145.html
Copyright © 2011-2022 走看看