java:多线程-同步与死锁

本文通过示例代码详细解析了Java中的线程同步机制,包括同步代码块与同步方法的实现方式,并展示了如何避免因同步不当导致的死锁现象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  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--);
           }
       }
    }
} 

运行结果:

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

由输出可见:
数据是一个一个出来的,而且没有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--);
       }
    }
} 
执行结果和上面一样。

 
 
二、死锁:
资源共享时需要进行同步操作!
程序中过多的同步会产生死锁!
 
死锁一般就是表示进程间互相等待对方执行,现在谁也不执行的情况。
例如:

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("李四给出了画。");
    }
}; 

张三说了话等待李四回应,李四说了话也等待张三回应,于是。。。。。死锁了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值