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();
        }
    }
  • 相关阅读:
    performance lazy-initialization
    Blazor项目文件分析
    Ubuntu 16.04重启Nautilus
    Ubuntu下查看APT安装的软件安装路径和版本
    Ubuntu 16.04搭建原始Git服务器
    Java原始封装常用HttpRequest
    CentOS 5/6安装后的必备设置(转)
    CentOS通过日志反查入侵(转)
    CentOS下防御或减轻DDoS攻击方法(转)
    通过LoadBalancerClient获取所有服务列表的IP
  • 原文地址:https://www.cnblogs.com/rinack/p/8950145.html
Copyright © 2011-2022 走看看