线程安全(一)synchronize什么是线程同步?什么是线程安全?什么是线程锁?synchronized怎么用?如何理解wait()和sleep()的区别?超详细例程讲解 您所在的位置:网站首页 什么叫线程同步数据 线程安全(一)synchronize什么是线程同步?什么是线程安全?什么是线程锁?synchronized怎么用?如何理解wait()和sleep()的区别?超详细例程讲解

线程安全(一)synchronize什么是线程同步?什么是线程安全?什么是线程锁?synchronized怎么用?如何理解wait()和sleep()的区别?超详细例程讲解

2024-07-16 09:35| 来源: 网络整理| 查看: 265

文章目录 前言一、什么是【线程同步】?二、什么是线程锁?synchronized2.1 没有加锁时2.2 锁加在普通方法上时,使用同一个对象调用。2.3 锁加在普通方法上时,使用两个不同的对象调用。2.4 锁加在静态方法上时,使用两个不同的对象调用。 三、释放锁是什么样的?sleep()和wait()的区别?3.1 测试类TestSleepWait3.2 线程类OneThread3.3 重点区别类,带锁的方法Person解析 四、工程

____________. ___. .__ __. .____________. .________ .___ ___. |████████████| / \ | \ | | |████████████| | ___ \ \ \ / / | | / /\ \ | \ | | | | | | ) | \ \ / / | | / /__\ \ | . \| | | | | |___/ / \ \/ / | | / ______ \ | |\ ` | | | | ___ < \ / | | .____________. | | | | | | \ | | | | | \ \ | | |__| |████████████| /__/ \__\ |__| \__| |__| |__| \__\ |__| 前言

想写这篇文章的缘由呢,是因为我自己在了解线程锁,线程同步的这些概念,以及线程同步的这些方法。我在查的很多博客要么重复一样的东西,要么一些很不生动的理论,还有一些是直接抛一个案例。不能说不对,只是作为一个小白的时候,很难理解。所以我想尝试从一个小白的角度去介绍一下我暂时所了解的这部分内容。其实这写内容都是相互关联的,有可能看了第一点,不明白,但是看完后面的点,串起来之后才是明白到的 注意: 如果你是做卷子答题,那还是不要cv这里的内容,因为这里只是在逼逼,或者举例说明,尝试从简单的例子去介绍这样一个听起来有点复杂的东西。

一、什么是【线程同步】?

我第一次看到这几个字,我的理解是几个线程在跑, 同步 同步不就是让他们跑的一样快。很遗憾,事实不是这样。 【解释】当多个线程在跑 ,并且这些线程用到同一个资源时,需要保证资源的安全。举个很简单的例子 银行存款有一百万【共同资源】,现在有十个人【多条线程】 同时来取钱,每个人都想取十万。每个人都直接修改这个【共同资源】的数据,一瞬间全部完全取款。结果银行存款【共同资源】剩下100-10=90万(因为瞬间还没等别人处理完,都拿到这个资源,并且去修改,所以每个人拿到的原始值都是100,算出的结果都是剩下90),每个人也都拿到钱,凭空生出了九十万,这明显是不对的,这是不安全的。 在这里插入图片描述 所以需要有很多手段去保证这个【共同资源】的安全,让他们一个一个使用这个资源,第一个取完剩下九十万,第二个取完剩下八十万…最终全部取万剩下零元。 在这里插入图片描述 那么保证资源安全的这些手段,称之为线程同步。

二、什么是线程锁?synchronized

线程锁,它其实是为了保证【共享资源】安全,而出现的一个概念。我们要保证资源的安全,所以要把资源加锁,有人在用的时候锁死,不用的时候,开锁。 好比如路边只有一个公共厕所,此时有5个人都要上厕所【共享资源】,有人在上厕所的时候就得把门锁死,要是不锁门,其他四个人也冲进去,多不安全,特别是漂亮的小女子,妈呀,我这口水,那场面多刺激。用完了得开锁,不把锁开起来后面几个人难道还得拉裤子? java中如何加锁? 加锁的位置有好几个,也有的不同的关键字,这里先介绍一下synchronized 关键字有无作用在方法上的效果的一些比较。

2.1 没有加锁时

首先我们要测试没有使用锁时,同时运行同一个对象的普通方法效果如何。

/** * 没有使用锁时,同时运行同一个对象的普通方法效果如何 */ public class TsetSyn1 { public static void main(String[] args) { TsetSyn1 ts = new TsetSyn1(); new Thread(new Runnable() { @Override public void run() { ts.test1(); } }).start(); new Thread(new Runnable() { @Override public void run() { ts.test1(); } }).start(); } /** * 没有加锁的普通方法 */ public void test1() { System.out.println("普通方法执行"); try { Thread.sleep(2000);//延时使得方法没有那么快运行结束 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("普通方法结束"); } }

可以看到执行结果

普通方法执行 普通方法执行 普通方法结束 普通方法结束

由此可见,两个线程的方法是同时在跑的。虽然运行的是同一个方法,但是第二个线程运行方法是没有等待第一个线程运行完方法就开始运行了。

2.2 锁加在普通方法上时,使用同一个对象调用。

我们尝试把 synchronized 关键字加在普通方法上,使用同一个对象调用。

/** * 把 synchronized 关键字加在普通方法上,使用同一个对象调用。 */ public class TestSyn2 { public static void main(String[] args) { TestSyn2 ts = new TestSyn2(); new Thread(new Runnable() { @Override public void run() { ts.test2(); } }).start(); new Thread(new Runnable() { @Override public void run() { ts.test2(); } }).start(); } /** * 加锁的普通方法 */ public synchronized void test2() { System.out.println("普通加锁方法执行"); try { Thread.sleep(2000);//延时使得方法没有那么快运行结束 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("普通加锁方法结束"); } }

可以看到执行结果

普通加锁方法执行 普通加锁方法结束 普通加锁方法执行 普通加锁方法结束

此时我们可以看到加锁之后,同一个对象中,枷锁的方式,两个线程同时要使用时,需要等前一个线程使用结束后才可以执行这个方法。

2.3 锁加在普通方法上时,使用两个不同的对象调用。 /** * 把 synchronized 关键字加在普通方法上,使用两个不同的对象调用。 */ public class TestSyn3 { public static void main(String[] args) { TestSyn3 ts = new TestSyn3(); TestSyn3 ts2 = new TestSyn3(); new Thread(new Runnable() { @Override public void run() { ts.test2(); } }).start(); new Thread(new Runnable() { @Override public void run() { ts2.test2(); } }).start(); } /** * 加锁的普通方法 */ public synchronized void test2() { System.out.println("普通加锁方法执行"); try { Thread.sleep(2000);//延时使得方法没有那么快运行结束 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("普通加锁方法结束"); } }

可以看到执行结果

普通加锁方法执行 普通加锁方法执行 普通加锁方法结束 普通加锁方法结束

可以看到,此时的结果和使用一个对象调用方法,且没有加锁运行的结果是一样的。

2.4 锁加在静态方法上时,使用两个不同的对象调用。 /** * 把 synchronized 关键字加在static方法上,使用两个不同的对象调用。 */ public class TestSyn4 { public static void main(String[] args) { TestSyn4 ts = new TestSyn4(); TestSyn4 ts2 = new TestSyn4(); new Thread(new Runnable() { @Override public void run() { ts.test2(); } }).start(); new Thread(new Runnable() { @Override public void run() { ts2.test2(); } }).start(); } /** * 加锁的普通方法 */ public static synchronized void test2() { System.out.println("静态加锁方法执行"); try { Thread.sleep(2000);//延时使得方法没有那么快运行结束 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("静态加锁方法结束"); } }

可以看到执行结果

静态加锁方法执行 静态加锁方法结束 静态加锁方法执行 静态加锁方法结束

此时我们又可以看到,调用这个方法又需要等待了。这是因为,静态方法它是属于类本身的。所有对象共用这个方法,而不是每个对象各自拥有的。因此将它锁住,即使是其他对象,也需要等待它被释放锁的时候才可以使用。 至于其他类锁什么的,还有其他关键字,这边暂不介绍,后面有时间再来补充修改一下,这边目的是认识一下锁的概念,让我们更好地阅读下文。

三、释放锁是什么样的?sleep()和wait()的区别?

我们在搜的很多文章也都会说道sleep和wait都是让程序睡一会儿,两者区别呢是前者不会释放锁,后者会释放锁。那么什么是释放锁呢。我们在前面其实也看到了没有释放锁的样子,因为我们前面用的都是sleep。 接下来我就写一个例程来对比试验 如何看出区别,在结果中对结果和类的内容进行讲解

3.1 测试类TestSleepWait /** * 测试类 */ public class TestSleepWait { public static void main(String[] args) { Person testP = new Person(); //这个类里面有是个带锁的方法,目的是多线程时,只有一个线程能够使用 OneThread t1 = new OneThread(testP);//创建的这两个线程争夺这个资源, OneThread t2 = new OneThread(testP); t1.start(); t2.start(); } } 3.2 线程类OneThread /** * 线程中调用person类的synchronized修饰过的方法 */ public class OneThread extends Thread { private Person curPerson = null; public OneThread(Person person){ this.curPerson = person; } @Override public void run(){ System.out.println(Thread.currentThread().getName() + " call start"); curPerson.display(); System.out.println(Thread.currentThread().getName() + " call end"); } } 3.3 重点区别类,带锁的方法Person /** * 方法逻辑,展示两种方法的区别 */ public class Person { public synchronized void display(){ for(int i=1;i


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有