• 线程同步以及yield()、wait()、Notify()、Notifyall()


    一、线程同步

    1、线程同步的目的是为了保护多个线程访问一个资源时对资源的破坏。
    2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他同步方法。
    二、实现同步机制的两个方法
    1。同步代码块: 
    synchronized(同一个数据){} 同一个数据:就是N条线程同时访问一个数据。 
    2。 
    同步方法: 
    public synchronized 数据返回类型 方法名(){} 
    就是使用 synchronized 来修饰某个方法,则该方法称为同步方法。对于同步方法而言,无需显示指定同步监视器,同步方法的同步监视器是 this 也就是该对象的本身(这里指的对象本身有点含糊,其实就是调用该同步方法的对象)通过使用同步方法,可非常方便的将某类变成线程安全的类,具有如下特征: 
    1,该类的对象可以被多个线程安全的访问。 
    2,每个线程调用该对象的任意方法之后,都将得到正确的结果。 
    3,每个线程调用该对象的任意方法之后,该对象状态依然保持合理状态。 
    注:synchronized关键字可以修饰方法,也可以修饰代码块,但不能修饰构造器,属性等。 
    实现同步机制注意以下几点: 安全性高,性能低,在多线程用。性能高,安全性低,在单线程用。 
     
    关键代码如下:
    编写打印机类:Printer  定义两个方法
    package cn.d.happy;
    
    public class Printer {
    
        Object o=new Object();
        
        //或在void前加synchronized
        public void print1(){
            //同步代码块
             synchronized (o){
            System.out.print("线");
            System.out.print("程");
            System.out.print("同");
            System.out.print("步");
            System.out.println();
        }
        }
        public void print2(){
            synchronized (o){
            System.out.print("噢");
            System.out.print("呵");
            System.out.println();
        }
        }
    }

    定义两个线程类 并重写run方法。继承Thread 和 实现Runnable接口 通过for循环遍历次数

    package cn.d.happy;
    
    public class MyThread extends Thread{
    
        public Printer print;
        
        @Override
    public void run() {
        //必须有该类的对象实例
        for (int i = 1; i <=10; i++) {
            print.print1();
        }
    }
    }
    package cn.d.happy;
    
    public class MyThread2 implements Runnable{
        public  Printer print;
        @Override
        public void run() {
             for (int i = 1; i <=10; i++) {
                 print.print2();
            }
            
        }
    
    }

    测试类 创建打印机对象  以及两个线程对象并进行赋值

    package cn.d.happy;
    
    public class Test {
    public static void main(String[] args) {
        //购买一个打印机
        Printer p=new Printer();
        
        //创建第一个线程对象  并且给属性赋值
        MyThread t1=new MyThread();
        t1.print=p;
        t1.start();
        
        
        //03.创建第二个线程对象 并且给属性赋值
          MyThread2 t2=new MyThread2();
          t2.print=p;
          Thread tt=new Thread(t2);
          tt.start();
    }
    }

    实现效果:


    三、Java多线程之yield()、wait()、Notify()、Notifyall()

    yield()、

    1)    通过yield ()函数,可使线程进入可执行状态,排程器从可执行状态的线程中重新进行排程。所以调用了yield()的函数也有可能马上被执行。
    2)    当调用yield ()函数后,线程不会释放它的“锁标志”。

    class TestThreadMethod extends Thread{
            public static int shareVar = 0;
            public TestThreadMethod(String name){
                super(name);
            }
            public synchronized void run(){
                for(int i=0; i<4; i++){
                    System.out.print(Thread.currentThread().getName());
                    System.out.println(" : " + i);
                    Thread.yield();
                }
            }
        }
        public class TestThread{
            public static void main(String[] args){
                TestThreadMethod t1 = new TestThreadMethod("t1");
                TestThreadMethod t2 = new TestThreadMethod("t2");
                t1.start();
                t1.start(); //(1)
                //t2.start(); (2)
            }

    运行结果为:
    t1 : 0
    t1 : 1
    t1 : 2
    t1 : 3
    t1 : 0
    t1 : 1
    t1 : 2
    t1 : 3
    从结果可知调用yield()时并不会释放对象的“锁标志”。
    如果把代码(1)注释掉,并去掉代码(2)的注释,结果为:
    t1 : 0
    t1 : 1
    t2 : 0
    t1 : 2
    t2 : 1
    t1 : 3
    t2 : 2
    t2 : 3
    从结果可知,虽然t1线程调用了yield(),但它马上又被执行了。 

     wait与notify是java同步机制中重要的组成部分。结合与synchronized关键字使用,可以建立很多优秀的同步模型。
     synchronized(this){ }等价于publicsynchronized void method(){.....}
     同步分为类级别和对象级别,分别对应着类锁和对象锁。类锁是每个类只有一个,如果static的方法被synchronized关键字修饰,则在这个方法被执行前必须获得类锁;对象锁类同。
       首先,调用一个Object的wait与notify/notifyAll的时候,必须保证调用代码对该Object是同步的,也就是说必须在作用等同于synchronized(obj){......}的内部才能够去调用obj的wait与notify/notifyAll三个方法,否则就会报错:
      java.lang.IllegalMonitorStateException:current thread not owner
      在调用wait的时候,线程自动释放其占有的对象锁,同时不会去申请对象锁。当线程被唤醒的时候,它才再次获得了去获得对象锁的权利。
      所以,notify与notifyAll没有太多的区别,只是notify仅唤醒一个线程并允许它去获得锁,notifyAll是唤醒所有等待这个对象的线程并允许它们去获得对象锁,只要是在synchronied块中的代码,没有对象锁是寸步难行的。其实唤醒一个线程就是重新允许这个线程去获得对象锁并向下运行。

       notifyAll,虽然是对每个wait的对象都调用一次notify,但是这个还是有顺序的,每个对象都保存这一个等待对象链,调用的顺序就是这个链的顺序。其实启动等待对象链中各个线程的也是一个线程,在具体应用的时候,需要注意一下。

      wait(),notify(),notifyAll()不属于Thread类,而是属于Object基础类,也就是说每个对像都有wait(),notify(),notifyAll()的功能。因为都个对像都有锁,锁是每个对像的基础,当然操作锁的方法也是最基础了。

    wait():

    等待对象的同步锁,需要获得该对象的同步锁才可以调用这个方法,否则编译可以通过,但运行时会收到一个异常:IllegalMonitorStateException。

    调用任意对象的 wait() 方法导致该线程阻塞,该线程不可继续执行,并且该对象上的锁被释放。

    notify():

    唤醒在等待该对象同步锁的线程(只唤醒一个,如果有多个在等待),注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。

    调用任意对象的notify()方法则导致因调用该对象的 wait()方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

    notifyAll():

    唤醒所有等待的线程,注意唤醒的是notify之前wait的线程,对于notify之后的wait线程是没有效果的。

     

    通常,多线程之间需要协调工作:如果条件不满足,则等待;当条件满足时,等待该条件的线程将被唤醒。在Java中,这个机制的实现依赖于wait/notify。等待机制与锁机制是密切关联的。

    例如:

    synchronized(obj) {
      while(!condition) {
      obj.wait();
      }
      obj.doSomething();
      }

    当线程A获得了obj锁后,发现条件condition不满足,无法继续下一处理,于是线程A就wait()。
    在另一线程B中,如果B更改了某些条件,使得线程A的condition条件满足了,就可以唤醒线程A :

    synchronized(obj) {
      condition = true;
      obj.notify();
      }

    synchronized和wait()、notify()等的关系: 

    1.有synchronized的地方不一定有wait,notify

    2.有wait,notify的地方必有synchronized.这是因为wait和notify不是属于线程类,而是每一个对象都具有的方法,而且,这两个方法都和对象锁有关,有锁的地方,必有synchronized。

    另外,注意一点:如果要把notify和wait方法放在一起用的话,必须先调用notify后调用wait,因为如果调用完wait,该线程就已经不是currentthread了。

     

     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    三数之和
    罗马数字与整数
    Oracle 开启或关闭归档
    Oracle RMAN scripts to delete archivelog
    Oracle check TBS usage
    Oracle kill locked sessions
    场景9 深入RAC运行原理
    场景7 Data Guard
    场景4 Data Warehouse Management 数据仓库
    场景5 Performance Management
  • 原文地址:https://www.cnblogs.com/WJ-163/p/5770304.html
Copyright © 2020-2023  润新知