深入理解Java 02 内置锁与死锁

一、并发问题分析

如果一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,此时线程是安全的。

而实际中存在这样的情况:
1.存在两个或者两个以上的线程对象,而且线程之间共享着一个资源。
2.有多个语句操作了共享资源。

二、解决方案

用一个案例来说明:张刘亮和郑旭共用一包面纸,两个人相当于两个线程,每一张纸不可以两人同时抽取
方式一:同步代码块
线程类:

class PaperThread extends Thread{
        
     static int num = 100;//纸的数量  非静态的成员变量,非静态的成员变量数据是在每个对象中都会维护一份数据的。
     
     static Object o = new Object();
    
     public PaperThread (String name) {
        super(name);
     }
    
    @Override
    public void run() {
        while(true){
            //同步代码块
            synchronized ("锁") {              
                if(num>0){                   
                    System.out.println(Thread.currentThread().getName()
                    +"取走了1张纸,还剩"+(count-1)+"张"");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    num--;
                }else{
                    System.out.println("用光了..");
                    break;
                }
            }     
        }
    }     
}

测试类
    
public class Test{
    public static void main(String[] args) {
        //创建两个线程
        PaperThread thread1 = new PaperThread("张刘亮");
        PaperThread thread2 = new PaperThread("郑旭");
        //开启线程
        thread1.start();
        thread2.start();      
    } 
}

同步代码块要注意事项:

1. 任意的一个对象都可以做为锁对象。
2. 在同步代码块中调用了sleep方法并不是释放锁对象的。
3. 只有真正存在线程安全问题的时候才使用同步代码块,否则会降低效率的。
4. 多线程操作的锁对象必须是唯一共享的,否则无效。

方式二:同步函数:同步函数就是使用synchronized修饰一个函数。
线程类:

class PaperThread extends Thread{
    
    static int count = 100;
    
    public currentThread(String name){
        super(name);
    }
    
    //@Override
    public synchronized  void run() {
        getPaper();
    } 
    
    //静态的函数---->函数所属的类的字节码文件对象--->PaperThread.class 唯一的。
    public static synchronized void getPaper(){
        while(true){          
            if(count>0){
                 System.out.println(Thread.currentThread().getName()
                         +"取走了1张纸,还剩"+ (count-1)+"张");
             count= count - 1000;
            }else{
               System.out.println("纸用光了...");
               break;
            } 
        }
    } 
}
测试类:

public class Test {
 
    public static void main(String[] args) {
        //创建两个线程对象
        PaperThread thread1 = new PaperThread("张刘亮");
        PaperThread thread2 = new PaperThread("郑旭");
        //调用start方法开启线程取钱
        thread1.start();
        thread2.start();      
    } 
}
同步函数要注意的事项 :
1. 如果是一个非静态的同步函数的锁对象是this对象,如果是静态的同步函数的锁 对象是当前函数所属的类的字节码文件(class对象)。
2. 同步函数的锁对象是固定的,不能由你来指定的。

推荐使用:同步代码块。
原因:
1. 同步代码块的锁对象可以由我们随意指定,方便控制。同步函数的锁对象是固定的,不能由我们来指定。
2. 同步代码块可以很方便控制需要被同步代码的范围,同步函数必须是整个函数的所有代码都被同步了。

三、死锁

同步机制解决了线程安全问题,但是也同时引发死锁现象。
案例:张刘亮和郑旭打LOL,只有一个鼠标和一个键盘,需要同时获取鼠标和键盘才能打LOL

class DeadLock extends Thread{
    
    public DeadLock(String name){
        super(name);
    }
    
    public void run() {
        if("张刘亮".equals(Thread.currentThread().getName())){
            synchronized ("键盘") {
                System.out.println("张刘亮拿到了键盘,准备去拿鼠标");
                synchronized ("鼠标") {
                    System.out.println("张刘亮拿到了键盘和鼠标,撸了起来");
                }
            }
        }else if("郑旭".equals(Thread.currentThread().getName())){
            synchronized ("鼠标") {
                System.out.println("郑旭拿到了鼠标,准备去拿键盘");
                synchronized ("键盘") {
                    System.out.println("郑旭拿到了键盘和鼠标,撸了起来");
                }
            }     
        } 
    }
}

public class TestDeadLock {
 
    public static void main(String[] args) {
        DeadLock thread1 = new DeadLock("张刘亮");
        DeadLock thread2 = new DeadLock("郑旭");
        //开启线程
        thread1.start();
        thread2.start();      
        
    }
    
}
死锁现象出现 的根本原因:
1. 存在两个或者两个以上的线程。
2. 存在两个或者两个以上的共享资源。
死锁现象的解决方案:死锁没有解决方案,只能尽量避免。

时间: 2024-10-11 16:35:12

深入理解Java 02 内置锁与死锁的相关文章

Java多线程之内置锁与显示锁

Java中具有通过Synchronized实现的内置锁,和ReentrantLock实现的显示锁,这两种锁各有各的好处,算是互有补充,今天就来做一个总结. Synchronized 内置锁获得锁和释放锁是隐式的,进入synchronized修饰的代码就获得锁,走出相应的代码就释放锁. synchronized(list){ //获得锁      list.append(); list.count();    }//释放锁   通信 与Synchronized配套使用的通信方法通常有wait(),

认识ASP内置的对象

对象 认识ASP内置的对象 ASP内置Application,AspError,Request,Response,Session,ObjectContext,Server 等七个对象(Object), 而且每个对象有各自的属性(Property),方法(Method),集合(Collection)或事件(Event).现在我们就来介绍这几个名词的意义,好让我们更好地去理解ASP的内置对象: "对象"(Object)就像我们在日常生活中所看到的各种物体,例如计算机, 冰箱,汽车, 手机等

基础篇:认识ASP内置对象与组件

ASP内置Application,AspError,Request,Response,Session,ObjectContext,Server 等七个对象(Object), 而且每个对象有各自的属性(Property),方法(Method),集合(Collection)或事件(Event).现在我们就来介绍这几个名词的意义,好让我们更好地去理解ASP的内置对象: "对象"(Object)就像我们在日常生活中所看到的各种物体,例如计算机, 冰箱,汽车, 手机等等,而对象可能又是由许多对象

Perl中的特殊内置变量详细介绍_应用技巧

内置变量 $_:先来看一个例子: 复制代码 代码如下: #!/usr/bin/perl -w@array = qw(a b c d);foreach (@array) { print $_," ";} 例子的作用就是定义一个数组并把其中的元素打印出来,这里需要注意的是foreach循环部分,foreach循环的标准格式应该是: 复制代码 代码如下: foreach $element (@array){ ......} 其中数组@array将其中的元素依次赋值给$element,但是在上

观察者模式(Observer Pattern) Java内置使用方法

Java内置的观察者模式, 是通过继承父类, 实现观察者模式的几个主要函数: Observerable(可被观察的): 是一个父类(class),addObserver(), 添加观察者; deleteObserver(), 删除观察者; notifyObservers(), 通知观察者;setChanged(), 确认更改; Observer(观察者): 是一个接口(interface), update(), 更新观察者数据; setChanged()->notifyObservers(),

深入理解Java内存模型(五) 锁

锁的释放-获取建立的happens before 关系 锁是java并发编程中最重要的同步机制.锁除了让 临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息. 下面是锁释放-获取 的示例代码: class MonitorExample { int a = 0; public synchronized void writer() { //1 a++; //2 } //3 public synchronized void reader() { //4 int i = a; //5 -

jsp内置对象入门(3) request对象详解【02】

关于request对象的方法有很多,在jsp内置对象[03]request详解[01]中,我们一起学习了request对象中的参数接受问题,那么现在再来看一下request的其他方法: 乱码解决( setCharacterEncoding()) 乱码可是WEB开发中的问题老大哥了,我相信有不少接触过这个东西的人都对其相当的头疼. 什么是乱码? 在运行程序的时候,本来应该显示的中文竟然变成了莫名其妙的其他看不懂的字符,那我们说就碰到乱码了.我们以下面的程序为例: Request_01.jsp <%

jsp内置对象入门(2) 【02】session、application

我们继续来学习,上一篇文章中说到了request属性,那么既要想客户端和服务端都都能实现跳转的话,就用到了session. 第三种:session属性 我们接着修改一下上一篇文章中的代码来对比一下: Session_01.jsp <%@ page language="java" contentType="text/html" pageEncoding="utf-8" %> <html> <head> <t

关于Java性能监控您不知道的5件事,第2部分:利用JDK内置分析器进行Java进程

关于Java性能监控您不知道的5件事,第2部分:利用JDK内置分析器进行Java进程监控 全功能内置分析器,如 JConsole 和 VisualVM 的成本有时比它们的性能费用还要高 - 尤其是在生产软件上运行的系统中.因此,在聚焦 Java 性能监控的第 2 篇文章中,我将介绍 5 个命令行分析工具,使开发人员仅关注运行的 Java 进程的一个方面. JDK 包括很多命令行实用程序,可以用于监控和管理 Java 应用程序性能.虽然大多数这类应用程序都被标注为 "实验型",在技术上不