java:多线程-同步与死锁

时间:2022-11-11 17:29:15
  1. 同步:
问题的引出:
public class Test {
     public static void main(String[] args) {
       MyThread t = new MyThread();
        new Thread(t, "---001---" ).start();
        new Thread(t, "---002---" ).start();
        new Thread(t, "---003---" ).start();
    }
}
class MyThread implements Runnable {
     int num = 5;
     public void run() {
        for ( int i = 0; i < 10; i++) {
            if ( num > 0) {
               try {
                  Thread. sleep (1000);
              } catch (Exception e) {
                  e.printStackTrace();
              }
              System. out .println(Thread. currentThread ().getName() + " : num= " + num --);
           }
       }
    }
}  

运行结果:
java:多线程-同步与死锁
 
那么看结果出现了负数,并且数据是三个三个出现的,为什么呢?
再看程序,在判断num>0以后又出现了延迟,那么在延迟中,其他的进程可能正在执行,执行的时候将num-1,那么在打印后就出现了负数的情况。
那么这里就出现了同步的状况,为了让其延迟的时间里,其他的进程不再执行,那么需要将进程锁住,执行完了再让其他进程执行。
  1. 同步的概念:
同步是指多个操作在同一个时间段内只能有一个线程进行,其它线程要等待此线程完成后才可以继续执行。
同步可以解决资源共享时的问题,如果两个或多个线程共同享有一个对象,那么当我们执行修改对象的操作时,我们就需要将这些操作同步(锁)起来。
我们可以给共享资源加一把锁,这把锁只有一把钥匙。哪个线程获取了这把钥匙,才有权利访问该共享资源。
锁的原理:
Java中每个对象都有一个内置锁
当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对象上锁定或在对象上同步。
当程序运行到synchronized同步方法或代码块时才该对象锁才起作用。
一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。
用哪个对象的锁:
一般使用正在执行代码类的当前实例(this)的锁,也可以用别的对象的锁,但要确保共享资源的线程们用的是同一把锁(这也说明:锁本身也一定是线程们之间的共享对象)。
错误例子如下: 
public void f() {
              private Object obj = new Object ();
              synchronized ( obj ) { 。。。。 }
}
上面这段代码没有任何意义。因为那个同步锁是在函数体内部产生的。每个线程调用这段代码的时候,都会产生一个新的同步锁。那么多个线程之间,使用的是不同的同步锁。根本达不到同步的目的。 
    为了确保线程用的是同一把锁,你可以把同步对象声明为 static
         P ublic static final Object obj = new Object ();
public void f() {
              synchronized ( obj ) { 。。。。 }
}
 
  1. 同步的方法:
通过同步代码的方式进行代码的加锁操作,同步方式有两种:
  • 同步代码块
  • 同步方法
 
  1. 同步代码块:
使用synchronized关键字进行同步代码块的声明,但在使用此操作时必须明确的指出到底要锁定的是哪个对象,一般都是以当前对象(this)为主。
synchronized ( 同步对象 ){
需要同步的代码
}

看如下代码:
public class Test {
     public static void main(String[] args) {
       MyThread t = new MyThread();
        new Thread(t, "---001---" ).start();
        new Thread(t, "---002---" ).start();
        new Thread(t, "---003---" ).start();
    }
}
class MyThread implements Runnable {
     int num = 5;
     public void run() {
        for ( int i = 0; i < 10; i++) {
            synchronized ( this ) {
               if ( num > 0) {
                   try {
                     Thread. sleep (300);
                  } catch (Exception e) {
                     e.printStackTrace();
                  }
                  System. out .println(Thread. currentThread ().getName()
                         + "name : num= " + num --);
              }
           }
       }
    }
}  
结果如下:
java:多线程-同步与死锁
由输出可见:
数据是一个一个出来的,而且没有0和负数出现。但是其运行效率要低于异步处理的。

 
  1. 同步方法:
同步方法锁定的是正在执行代码类的当前实例(this实例)。
synchronized   返回值 方法名称(参数列表){}

将上面代码改改就变成如下程序:
class MyThread implements Runnable {
     int num = 5;
     public void run() {
        for ( int i = 0; i < 100; i++) {
           fun();
       }
    }
     public synchronized void fun() {
        if ( num > 0) {
            try {
              Thread. sleep (300);
           } catch (Exception e) {
              e.printStackTrace();
           }
           System. out .println(Thread. currentThread ().getName()
                  + "name : num= " + num --);
       }
    }
}  
执行结果和上面一样。

 
 
二、死锁:
资源共享时需要进行同步操作!
程序中过多的同步会产生死锁!
 
死锁一般就是表示进程间互相等待对方执行,现在谁也不执行的情况。
例如:
java:多线程-同步与死锁
public class Test {
     public static void main(String[] args) {
       DeadLock_Run zsRun = new DeadLock_Run(); // 控制张三
       DeadLock_Run lsRun = new DeadLock_Run(); // 控制李四
       zsRun. firstSpeak = true ;
        new Thread(zsRun).start();
        new Thread(lsRun).start();
    }
}
class DeadLock_Run implements Runnable{
     private static Zhangsan zs = new Zhangsan(); // 实例化张三对象,static很重要
     private static Lisi ls = new Lisi(); // 实例化李四对象,static很重要   
public boolean firstSpeak = false ; // 声明标志位,判断那个先说话
     public void run() {
        if ( firstSpeak ) {
            synchronized ( zs ) { // 同步张三
               zs .say();
               try {
                  Thread. sleep (500);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
               synchronized ( ls ) { // 等待李四的答复(要获取李四的锁的钥匙),让李四给他画
                   ls .give();
              }
           }
       } else {
            synchronized ( ls ) { // 同步李四
               ls .say();
               try {
                  Thread. sleep (500);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
               synchronized ( zs ) { // 等待张三的答复(要获取张三的锁的钥匙),让张三给他书
                   zs .give();
              }
           }
       }
    }
}
class Zhangsan { // 定义张三类
     public void say() {
       System. out .println( " 张三对李四说: 你给我画,我就把书给你。 " );
    }
     public void give() {
       System. out .println( " 张三给出了书。" );
    }
};
class Lisi { // 定义李四类
     public void say() {
       System. out .println( " 李四对张三说: 你给我书,我就把画给你 " );
    }
     public void give() {
       System. out .println( " 李四给出了画。" );
    }
};  

张三说了话等待李四回应,李四说了话也等待张三回应,于是。。。。。死锁了。