线程同步以及yield()、wait()、Notify()、Notifyall()

一、线程同步

1、线程同步的目的是为了保护多个线程访问一个资源时对资源的破坏。
2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他同步方法。
二、实现同步机制的两个方法
1。同步代码块: 
synchronized(同一个数据){} 同一个数据:就是N条线程同时访问一个数据。 
2。 
同步方法: 
public synchronized 数据返回类型 方法名(){} 
就是使用 synchronized 来修饰某个方法,则该方法称为同步方法。对于同步方法而言,无需显示指定同步监视器,同步方法的同步监视器是 this 也就是该对象的本身(这里指的对象本身有点含糊,其实就是调用该同步方法的对象)通过使用同步方法,可非常方便的将某类变成线程安全的类,具有如下特征: 
1,该类的对象可以被多个线程安全的访问。 
2,每个线程调用该对象的任意方法之后,都将得到正确的结果。 
3,每个线程调用该对象的任意方法之后,该对象状态依然保持合理状态。 
注:synchronized关键字可以修饰方法,也可以修饰代码块,但不能修饰构造器,属性等。 
实现同步机制注意以下几点: 安全性高,性能低,在多线程用。性能高,安全性低,在单线程用。 
关键代码如下:
编写打印机类:Printer  定义两个方法
package cn.d.happy;public class Printer {Object o=new Object();//或在void前加synchronizedpublic void print1(){//同步代码块synchronized (o){System.out.print("线");System.out.print("程");System.out.print("同");System.out.print("步");System.out.println();}}public void print2(){synchronized (o){System.out.print("噢");System.out.print("呵");System.out.println();}}
}

定义两个线程类 并重写run方法。继承Thread 和 实现Runnable接口 通过for循环遍历次数

package cn.d.happy;public class MyThread extends Thread{public Printer print;@Override
public void run() {//必须有该类的对象实例for (int i = 1; i <=10; i++) {print.print1();}
}
}
package cn.d.happy;public class MyThread2 implements Runnable{public  Printer print;@Overridepublic void run() {for (int i = 1; i <=10; i++) {print.print2();}}}

测试类 创建打印机对象  以及两个线程对象并进行赋值

package cn.d.happy;public class Test {
public static void main(String[] args) {//购买一个打印机Printer p=new Printer();//创建第一个线程对象  并且给属性赋值MyThread t1=new MyThread();t1.print=p;t1.start();//03.创建第二个线程对象 并且给属性赋值MyThread2 t2=new MyThread2();t2.print=p;Thread tt=new Thread(t2);tt.start();
}
}

实现效果:


 

三、Java多线程之yield()、wait()、Notify()、Notifyall()

yield()、

1)    通过yield ()函数,可使线程进入可执行状态,排程器从可执行状态的线程中重新进行排程。所以调用了yield()的函数也有可能马上被执行。
2)    当调用yield ()函数后,线程不会释放它的“锁标志”。

class TestThreadMethod extends Thread{public static int shareVar = 0;public TestThreadMethod(String name){super(name);}public synchronized void run(){for(int i=0; i<4; i++){System.out.print(Thread.currentThread().getName());System.out.println(" : " + i);Thread.yield();}}}public class TestThread{public static void main(String[] args){TestThreadMethod t1 = new TestThreadMethod("t1");TestThreadMethod t2 = new TestThreadMethod("t2");t1.start();t1.start(); //(1)//t2.start(); (2)}

运行结果为:
t1 : 0
t1 : 1
t1 : 2
t1 : 3
t1 : 0
t1 : 1
t1 : 2
t1 : 3
从结果可知调用yield()时并不会释放对象的“锁标志”。
如果把代码(1)注释掉,并去掉代码(2)的注释,结果为:
t1 : 0
t1 : 1
t2 : 0
t1 : 2
t2 : 1
t1 : 3
t2 : 2
t2 : 3
从结果可知,虽然t1线程调用了yield(),但它马上又被执行了。 

 wait与notify是java同步机制中重要的组成部分。结合与synchronized关键字使用,可以建立很多优秀的同步模型。
 synchronized(this){ }等价于publicsynchronized void method(){.....}
 同步分为类级别和对象级别,分别对应着类锁和对象锁。类锁是每个类只有一个,如果static的方法被synchronized关键字修饰,则在这个方法被执行前必须获得类锁;对象锁类同。
   首先,调用一个Object的wait与notify/notifyAll的时候,必须保证调用代码对该Object是同步的,也就是说必须在作用等同于synchronized(obj){......}的内部才能够去调用obj的wait与notify/notifyAll三个方法,否则就会报错:
  java.lang.IllegalMonitorStateException:current thread not owner
  在调用wait的时候,线程自动释放其占有的对象锁,同时不会去申请对象锁。当线程被唤醒的时候,它才再次获得了去获得对象锁的权利。
  所以,notify与notifyAll没有太多的区别,只是notify仅唤醒一个线程并允许它去获得锁,notifyAll是唤醒所有等待这个对象的线程并允许它们去获得对象锁,只要是在synchronied块中的代码,没有对象锁是寸步难行的。其实唤醒一个线程就是重新允许这个线程去获得对象锁并向下运行。

   notifyAll,虽然是对每个wait的对象都调用一次notify,但是这个还是有顺序的,每个对象都保存这一个等待对象链,调用的顺序就是这个链的顺序。其实启动等待对象链中各个线程的也是一个线程,在具体应用的时候,需要注意一下。

  wait(),notify(),notifyAll()不属于Thread类,而是属于Object基础类,也就是说每个对像都有wait(),notify(),notifyAll()的功能。因为都个对像都有锁,锁是每个对像的基础,当然操作锁的方法也是最基础了。

wait():

等待对象的同步锁,需要获得该对象的同步锁才可以调用这个方法,否则编译可以通过,但运行时会收到一个异常:IllegalMonitorStateException。

调用任意对象的 wait() 方法导致该线程阻塞,该线程不可继续执行,并且该对象上的锁被释放。

notify():

唤醒在等待该对象同步锁的线程(只唤醒一个,如果有多个在等待),注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。

调用任意对象的notify()方法则导致因调用该对象的 wait()方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

notifyAll():

唤醒所有等待的线程,注意唤醒的是notify之前wait的线程,对于notify之后的wait线程是没有效果的。

 

通常,多线程之间需要协调工作:如果条件不满足,则等待;当条件满足时,等待该条件的线程将被唤醒。在Java中,这个机制的实现依赖于wait/notify。等待机制与锁机制是密切关联的。

例如:

 

synchronized(obj) {while(!condition) {obj.wait();}obj.doSomething();}

 

当线程A获得了obj锁后,发现条件condition不满足,无法继续下一处理,于是线程A就wait()。
在另一线程B中,如果B更改了某些条件,使得线程A的condition条件满足了,就可以唤醒线程A :

synchronized(obj) {condition = true;obj.notify();}

synchronized和wait()、notify()等的关系: 

1.有synchronized的地方不一定有wait,notify

2.有wait,notify的地方必有synchronized.这是因为wait和notify不是属于线程类,而是每一个对象都具有的方法,而且,这两个方法都和对象锁有关,有锁的地方,必有synchronized。

另外,注意一点:如果要把notify和wait方法放在一起用的话,必须先调用notify后调用wait,因为如果调用完wait,该线程就已经不是currentthread了。

 

 

 

 

 

转载于:https://www.cnblogs.com/WJ-163/p/5770304.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/271113.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

面试:整理面试中常被问到的8种数据结构

数据结构是一种特殊的组织和存储数据的方式&#xff0c;可以使我们可以更高效地对存储的数据执行操作。数据结构在计算机科学和软件工程领域具有广泛而多样的用途。几乎所有已开发的程序或软件系统都使用数据结构。此外&#xff0c;数据结构属于计算机科学和软件工程的基础。当…

python绘制繁花曲线代码_使用python和pygame绘制繁花曲线的方法

前段时间看了一期《最强大脑》&#xff0c;里面各种繁花曲线组合成了非常美丽的图形&#xff0c;一时心血来潮&#xff0c;想尝试自己用代码绘制繁花曲线&#xff0c;想怎么组合就怎么组合。真实的繁花曲线使用一种称为繁花曲线规的小玩意绘制&#xff0c;繁花曲线规由相互契合…

Axure原型制作规范

一、 名词定义&#xff1a; Sitemap 导航图Widgets 组件Master 库Label 控件名Interactions 交互动作Annotations 注释Location and size 位置和尺寸二、 Widgets规范 本站常用widgets规范&#xff1a;命名规范&#xff1a;制定命名规范从而方便搜索和升级。 a. 全站使用&#…

系统测试相关知识笔记

1、系统测试的意义系统测试是为了发现系统中的错误而执行程序的过程&#xff0c;发现系统中存在的问题&#xff0c;及时处理掉&#xff0c;从而规避项目后续阶段顺利推进的风险、和高质量的软件交付给客户。2、系统测试的目的希望以最少的人力和时间发现潜在的各种错误和缺陷。…

最小生成树练习1(克鲁斯卡尔算法Kruskal)

今天刷一下水题练手入门&#xff0c;明天继续。 poj1861 Network&#xff08;最小生成树&#xff09;新手入门题。 题意&#xff1a;输出连接方案中最长的单根网线长度&#xff08;必须使这个值是所有方案中最小的&#xff09;&#xff0c;然后输出方案。 题解&#xff1a;本题…

java变量不声明可以直接使用吗_我们可以在不使用Java进行初始化的情况下声明最终变量吗?...

在Java中&#xff0c;final是可与字段类和方法一起使用的access修饰符。当一个方法为final时&#xff0c;它不能被覆盖。当变量为最终变量时&#xff0c;其值无法进一步修改。当类结束时&#xff0c;不能扩展。无需初始化即可声明最终变量如果稍后声明了最终变量&#xff0c;则…

系统测试:单元测试相关知识笔记

一、单元测试概念单元测试也成为模块测试&#xff0c;在模块编写完成且无编译错误后就可以进行。单元测试侧重模块中的内部处理逻辑和数据结构。如果采用机器测试&#xff0c;一般用白盒测试法。二、单元测试检查模块特征1、模块接口模块接口保证了测试模块数据流可以正确地流入…

跨网段远程调试vs_如何提高后台服务应用问题的排查效率?日志 VS 远程调试

转眼间&#xff0c;距离Jerry最近一篇文章推送已经过去了一个多月的时间了。公众号更新的频率降低&#xff0c;不是因为Jerry偷懒&#xff0c;而是由于从春节过后&#xff0c;我所在的SAP成都研究院数字创新空间整个团队&#xff0c;一直在忙一个5月份需要交付的项目上。Jerry每…

计算机硬件知识:BIOS、EFI与UEFI详解!

本文估计很多小白看不懂&#xff0c;但是还是建议你硬着头皮看完&#xff0c;这篇文章主要讲解了这几种“BIOS”的启动方式&#xff0c;对电脑启动问题判断的理解会有益处。BIOS是个程序&#xff0c;存储在BIOS芯片中&#xff0c;而现在的新式电脑用的基本都是UEFI启动&#xf…

java pdf 导出下载_Java+PDF模板导出成pdf文件,并下载

1&#xff0c;根据前人经验&#xff0c;熟悉完成基础操作&#xff1a;https://www.cnblogs.com/wangpeng00700/p/8418594.html?tdsourcetags_pcqq_aiomsg2&#xff0c;根据链接中操作完成之后&#xff0c;在本地生成pdf文件已经没有问题了。但如果放到&#xff0c;Linux服务器…

在db2数据库上模拟死锁场景 还是z上的

如果条件允许&#xff0c;起两个线程互相抢资源就行了&#xff0c;但问题是&#xff0c;时间上还需要同步&#xff0c;要做到完美控制&#xff0c;还得加其他逻辑&#xff0c;忒费事&#xff0c;所以可以用下面的办法&#xff1a; 在目标表上直接加个锁……简单&#xff0c;粗暴…

条件随机场 python_用条件随机场做网络小说命名实体识别

一直想用统计学习方法来改善拨云搜索&#xff0c;这次先在命名实体上小小尝试一下。线性链条件随机场对于无向图中的节点&#xff0c;定义一组特征函数&#xff0c;使其状态仅受邻近节点和观测序列的影响。在标注任务中&#xff0c;节点只有前后两个邻近节点&#xff0c;即线性…

项目开发基础:常用测试方法介绍

1、集成测试集成测试就是把模块按照设计说明书的要求组合起来进行测试。1.1、集成测试方法&#xff1a;a、分别测试各个模块&#xff0c;再把这些模块组合起来进行整体测试&#xff0c;也就是非增量式集成。特点&#xff1a;可以对模块进行并行测试&#xff0c;能充分利用人力&…

java 多数据源处理_java – 用于处理多个数据源的Spring事务管理

这可能是一个重复的问题,但我找不到(至少我无法理解)一个满意的答案,因此再次提问.我正在使用两个数据源(MySQL和Oracle).以下是执行流程&#xff1a;主方法-A调用方法-B(写入Oracle DB)然后它(方法-A)调用方法-C(写入mySQL DB)然后它(方法-A)调用方法-D(写入Oracle DB) ).如果…

MyBatis Generator

1 <?xml version"1.0" encoding"UTF-8"?>2 <!DOCTYPE generatorConfiguration3 PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"4 "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"&g…

svd奇异值分解_NCL专辑 | 奇异值分解(SVD)

奇异值分解SVD(Singular Value Decomposition)是一种矩阵分解方法&#xff0c;在气象领域中常用来分析两个气象场场之间的关系。NCL的函数库中与SVD相关的函数包括svd_lapack&#xff0c;svdcov&#xff0c;svdcov_sv&#xff0c;svdstd&#xff0c;svdstd_sv。svd_lapack&…