java多线程总结一(wait和notify的线程调度)

对象锁
  • 在java中每个对象都有一把锁,可以用来处理多线程的调度问题
  • 对象锁相应的关键字是synchronized(反引号真难找),既可以修饰方法,也可以修饰类,也可以修饰 一段代码,作用是:使当前调用 synchronized修饰的东西 的对象上锁。这个锁的作用是:只有先用synchronized的线程可以使用这个对象。举个栗子:

    package 多线程练习;  
    import java.lang.*;
    
    public class ThreadTest implements Runnable {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            ThreadTest ta = new ThreadTest();
            ThreadTest tb = new ThreadTest();
    
            Thread Tha = new Thread(ta, "线程1");
            Thread Thb = new Thread(tb, "线程2");
    
            Tha.start();
            Thb.start();
    
        }
    
        public void run() {
            // TODO Auto-generated method stub
            int i = 0;
                while(i < 10)
                {
                System.out.println(Thread.currentThread().getName()+ "正在执行");
                    i = i+1;
            }
        }
    }
    

    上面,分别建立了ta和tb,然后创建了Tha和Thb用来执行线程。这时程序的输出为:
    线程2正在执行
    线程1正在执行
    线程2正在执行
    线程1正在执行
    线程1正在执行
    线程1正在执行
    线程1正在执行
    线程1正在执行
    线程1正在执行
    线程1正在执行
    线程1正在执行
    线程1正在执行
    线程2正在执行
    线程2正在执行
    线程2正在执行
    线程2正在执行
    线程2正在执行
    线程2正在执行
    线程2正在执行
    线程2正在执行

    可见一般情况下,线程的执行并没有什么顺序。这里面创建了两个不同的对象ta和tb,所以虽然类相同,但是两者之间并没有多大联系

  • 我们来看一下线程的资源共享
    下面修改一下代码

    import java.lang.*;
    
    public class ThreadTest implements Runnable {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            ThreadTest ta = new ThreadTest();
    
            Thread Tha = new Thread(ta, "线程1");
            Thread Thb = new Thread(ta, "线程2");
    
            Tha.start();
            Thb.start();
    
        }
        int i = 0;
        @Override
        public void run() {
            // TODO Auto-generated method stub
    
        //  synchronized (this) {
                while(i < 10)
                {
                    System.out.println(Thread.currentThread().getName()+ "正在执行");
                    i = i+1;
                }       
        }
    }
    

    运行结果为:
    线程2正在执行
    线程1正在执行
    线程1正在执行
    线程1正在执行
    线程1正在执行
    线程1正在执行
    线程1正在执行
    线程1正在执行
    线程1正在执行
    线程2正在执行
    线程1正在执行

    可见此时,线程1和线程2共享同一个对象中的资源,互相争夺

  • 下面终于加入了锁机制,通过synchronized关键字,可以让需要的线程获得需要的资源,其他线程只能靠边站。只有执行完毕之后,其他线程才能获得相应的资源

    1. 首先来讲一下object.wait()和object.notify()。每个对象都有一个对象锁,线程执行到synchronized关键字的时候即可获取对象的锁(此处的对象为执行对应代码的那个对象)。拿到对象锁的线程即可执行对应的代码段。wait和notify方法只可用于synchronized(obj){……}代码段中,因为这两个方法和对象锁相关。
    2. wait()方法可以使线程进入休眠,同时释放对应的对象锁;而调用对象的notify则可以唤醒相应的线程,在synchronized(obj){……}代码段结束后运行对应的线程。

      注意,此处的wait和notify是根据对应的类来休眠和唤醒线程。

      object AAA = new object();
      object BBB = new object();
      .........
      synchronized(AAA)
      {
      AAA.wait();
      }
      

      注意,此时假设有线程X执行了以上代码,即取得了AAA的对象锁,然后进行了wait()操作。那么接下去只能通过别的线程实现AAA.nojity();把线程X唤醒。

也就是说,进行线程调度的时候,一定要有相应的对象来执行Obj.wait()和Obj.notify()。

下面通过一个经典的三线程打印ABC的问题来具体说明wait和notify的使用方法

public class MyThreadPrinter2 implements Runnable {

private String name;     
private Object prev;     
private Object self;     

private MyThreadPrinter2(String name, Object prev, Object self) {     
    this.name = name;     
    this.prev = prev;     
    this.self = self;     
}     

@Override    
public void run() {     
    int count = 10;     
    while (count > 0) {     
        synchronized (prev) {     
            synchronized (self) {     
                System.out.print(name);     
                count--;    

                self.notify();     
            }     
            try {     
                prev.wait();     
            } catch (InterruptedException e) {     
                e.printStackTrace();     
            }     
        }     

    }     
}     

public static void main(String[] args) throws Exception {     
    Object a = new Object();     
    Object b = new Object();     
    Object c = new Object();     
    MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);     
    MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);     
    MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);     


    new Thread(pa).start();  
    Thread.sleep(100);  //确保按顺序A、B、C执行  
    new Thread(pb).start();  
    Thread.sleep(100);    
    new Thread(pc).start();     
    Thread.sleep(100);    
    }     
}

程序刚开始:

  1. 线程pa执行,获取了对象c和对象a的对象锁,成功打印“A”,然后唤醒对象a上面休眠的其他线程(此时没有,所以没反应),然后让线程pa在对象c上休眠(说法不对,不过可以这么理解)。此时,对象abc的对象锁都被释放。(另外,在synchronized对应的代码段执行结束后,自动释放对应的对象锁)

  2. 100毫秒后,线程pb执行。获取了对象a和对象b的对象锁。同上,最后让线程pb咋对象a上休眠。

    此时,调用c.notify();可以唤醒线程pa,调用a.notify();可以唤醒线程pb。

  3. 100毫秒后,线程pc开始执行,获取了对象bc的对象锁,成功打印“C”。然后线程pc执行self.notify();这条语句唤醒了对象C上面休眠的线程pa,线程pa进入可执行状态。此时空闲的对象锁为a和c(c刚刚被释放),然后线程pa就会开始执行。线程pc继续休眠在对象b上。

  4. 线程pa取得对象锁ac开始执行,打印“A”,然后唤醒对象a上休眠的线程pb。以后就按这个顺序依次执行,直到循环结束。

wait和notify其实更像是对象的操作而不是线程的操作,因为用这两个方法调度线程需要相应的对象。
需要说明的是,如果需要使用对象锁,不能把相应的对象写到run()等里面。因为每执行一次run会创建一个新的对象,那么这些对象之间就是独立的,对象锁就不能起作用。
即:锁只有一把,把钥匙轮流交给需要的人
而不是:每个人来了都装一把新锁,每个人都有不同的钥匙可以开门。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值