JAVA多线程(5)——Synchronized、wait、notify

5人阅读 评论(0) 收藏 举报
分类:

一、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();
    }
}

查看评论

java核心篇 第三章 反射、多线程

主要内容: 第一讲 Java反射 第二讲 Java反射 - 访问构造方法 第三讲 Java反射 - 访问成员变量 第四讲 Java反射 - 访问成员方法 第五讲 线程-线程简介 第六讲 线程-生命周期 等
  • 2017年05月10日 13:13

JAVA多线程中synchronized,wait和notify的关系

synchronized是和一个锁对象关联的,synchronized限制static方法的时候, 锁对象为这个类的class变量,相当于XXXClass.class. synchronized限...
  • kkdelta
  • kkdelta
  • 2012-07-24 13:50:19
  • 15616

java多线程中的synchronized和wait/notify用法总结

自学java也有一段时间了,自我感觉java中的多线程还是比较重要的。至于线程的概念,也没有必要说的那么玄乎,引用马士兵老师的话就很好理解:线程就是程序执行的路径。多线程,也就不难理解了,程序执行的多...
  • gtkknd
  • gtkknd
  • 2016-01-21 15:42:54
  • 639

Java wait、notify与synchronized的奇妙之处

synchronized是Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。synchronized就是针对内存区块申请内存锁。(1)JA...
  • chy555chy
  • chy555chy
  • 2016-08-22 19:26:57
  • 2686

Java线程(初级)——synchronized、死锁、wait、notify详解

线程可以在任意对象的监视器(锁)上阻塞(wait,前提是获取到该对象的锁),也可以在唤醒任意一个wait在某个对象的监视器上的线程(notify,前提是获取到该对象的锁)。“获取到某个对象的锁”,就像...
  • MyTroy
  • MyTroy
  • 2014-08-03 20:01:20
  • 3007

Android synchronized wait notify 多线程同步

项目中碰到问题,记录一下 ThreadB Thread = new ThreadB();  syncThread.start();   synchronized (handler) {    ...
  • kufeiyun
  • kufeiyun
  • 2011-02-26 18:14:00
  • 10563

Java多线程--同步与死锁:synchronized;等待与唤醒:wait、notify、notifyAll;生命周期

1、问题的引出 class MyThread implements Runnable{ private int ticket = 5 ; // 假设一共有5张票 public void ru...
  • waldmer
  • waldmer
  • 2013-10-12 09:34:17
  • 7654

java线程通讯——使用Lock和Condition代替synchronized 和 wait, notify notifyAll()

Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。 此实现允许更灵活的结构,可以具有差别很大的属性,可以支持多个相关的 Condition 对象。 ...
  • SmuEdward
  • SmuEdward
  • 2016-05-06 14:30:35
  • 1375

synchronized , sleep ,wait ,notify,等的理解

1.  sleep是Thread类的静态方法,谁调用谁去睡觉。sleep是占用cpu去睡觉,而wait是放弃cpu去睡觉, sleep没有释放锁,而wait释放了锁,sleep不会让出cpu资源,wa...
  • tianyeming
  • tianyeming
  • 2015-01-26 11:34:39
  • 3316

Java同步机制:synchronized,wait,notify

/** * 假设A,B两个线程同时运行。但A需要某项前置条件必须满足(data数组装满)后才可以正常运转。 这里让B线程去为满足A的条件去干活。 */ public class SYN { i...
  • zhangphil
  • zhangphil
  • 2015-02-13 21:13:27
  • 976
    个人资料
    等级:
    访问量: 75
    积分: 21
    排名: 210万+
    文章分类
    文章存档
    最新评论