前言:关于synchronized、wait、notify已经notifyAll大家应该不陌生,现在我大致说一下我的理解。

一:synchronized

synchronized中文解释是同步,那么什么是同步呢,解释就是程序中用于控制不同线程间操作发生相对顺序的机制,通俗来讲就是2点,第一要有多线程,第二当多个线程同时竞争某个资源的时候会有先后顺序。在java中有三种写synchronized的方式

  • 第一种:

    • 写在普通方法的前面,这种表示对实例对象加锁。
  • 第二种:
    • 写在静态方法前面,这种表示对类对象加锁
  • 第三种:
    • 写在代码块中,锁是Synchonized括号里配置的对象(可能是实例对象,也可能是类对象)

总体说来就2种,一种就是锁实例对象,一种锁类对象。

锁实例对象就是当多个线程同时操作这个实例对象的时候必须先获取锁,如果无法获取锁,则必须处于等待状态,而和锁类对象区别是,当多个线程同时操作的时候,任何以这个类对象实例化的对象都要获取锁才能操作。举个简单例子

比如一个群人去打饭,只要是人就必须排队等待,一个个的打饭。不管是谁,但是吃完饭之后把盘子送回原地,但是这个时候不同的人可能吃饭快慢不同,但是肯定先吃饭后送盘子。现在写段代码我们比对一下。

public class RunnableTest implements Runnable {

    private synchronized  void testSyncMethod() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getId() + "testSyncMethod:" + i);
}
} public void run() {
testSyncMethod();
} public static void main(String[] args) {
ExecutorService exec = Executors.newFixedThreadPool(2);
RunnableTest rt = new RunnableTest();
RunnableTest rt1 = new RunnableTest();
exec.execute(rt);
exec.execute(rt1);
exec.shutdown();
}

按照我们的理论输出结果肯定是无序排列的。如图

public class RunnableTest implements Runnable {
private void testSyncBlock() {
synchronized (RunnableTest.class) {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getId()+"testSyncBlock:" + i);
}
}
}
public void run() {
testSyncBlock();
}
public static void main(String[] args) {
ExecutorService exec = Executors.newFixedThreadPool(2);
RunnableTest rt = new RunnableTest();
RunnableTest rt1 = new RunnableTest();
exec.execute(rt);
exec.execute(rt1);
exec.shutdown();
}
}

而这段代码输入结果肯定是有序的。如下

那么我们在思考一个问题,如果类A有2个方法,如果我们在其中一个方法前面加入了synchronized,哪意味着我们别的线程调用这个类的另一个方法也需要获取锁才可以执行,也是另一个方法只是读,这样一来性能就大大的降低,所以我们在实际开发中尽量少在方法前加入synchronized,那么我们应该怎么做呢,既然是实际对象我们只需要加入一个类,锁定此类,只需要让类的一个方法进行锁定即可。ok下面代码如下

public class A {
private Object obj="123";
public void a(){
synchronized (obj) {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread() + "a:" + i);
}
}
}
public void b(){
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread() + "b:" + i);
} }
}
public class B implements Runnable{
private A a;
public B(A a){
this.a=a;
} public void run() {
a.b();
}
}
public class C implements Runnable {
private A a;
public C(A a){
this.a=a;
}
public void run() {
a.a();
}
}
public class E implements Runnable{
private A a;
public E(A a){
this.a=a;
}
public void run() {
a.a();
}
}
public class D {
public static void main(String[] args) {
A a=new A();
ExecutorService executorService= Executors.newCachedThreadPool();
executorService.execute(new E((a)));
executorService.execute(new B(a));
executorService.execute(new C(a));
executorService.shutdown();
}
}

按照我们理论这段代码执行顺序是第一个线程和第二个线程无序,第三个线程必须等待第一个线程执行完毕才可以,测试结果也论证了我们的理论如下

二:wait、notify已经notifyAll

wait、notify、notifyAll是Object对象的属性,并不属于线程。我们先解释这三个的一个很重要的概念

wait:使持有该对象的线程把该对象的控制权交出去,然后处于等待状态(这句话很重要,也就是说当调用wait的时候会释放锁并处于等待的状态)

notify:通知某个正在等待这个对象的控制权的线程可以继续运行(这个就是获取锁,使自己的程序开始执行,最后通过notify同样去释放锁,并唤醒正在等待的线程)

notifyAll:会通知所有等待这个对象控制权的线程继续运行(和上面一样,只不过是唤醒所有等待的线程继续执行)

这个就好了,从上面的解释我们可以看出通过wait和notify可以做线程之间的通信,当A线程处理完毕通知B线程执行,B线程执行完毕以后A线程可以继续执行。ok我们使用例子来说明。

public class Temp {
int count=0;
public void waiter() throws InterruptedException {
synchronized (this) {
System.out.println("等待");
wait();
System.out.println(this.count);
}
}
public void notifyer() throws InterruptedException {
synchronized (this){
TimeUnit.SECONDS.sleep(1);
System.out.println("唤醒");
for (int i=0;i<10;i++){
System.out.println(Thread.currentThread()+"notifyer:"+i);
count+=i;
}
notify();
}
} public class Waiter implements Runnable{
private Temp temp;
public Waiter(Temp temp){
this.temp=temp;
}
public void run() {
try {
temp.waiter();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public class Notifyer implements Runnable{
private Temp temp;
public Notifyer(Temp temp){
this.temp=temp;
}
public void run() {
try {
temp.notifyer();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) {
Temp temp=new Temp();
ExecutorService executorService= Executors.newCachedThreadPool();
executorService.execute(new Waiter(temp));
executorService.execute(new Notifyer(temp));
executorService.shutdown();
}

其中在notify中加入休眠1s目的是让线程waiter先执行更能看明白

我们在举一个例子,比如说我们经常提到的客户端请求和服务器响应,当客户端发送请求后就处于等待服务端的响应,而服务端会等待客户端端请求然后响应客户端请求,下面我们看看怎么去写代码

首先我们写一个对象Handler来专门处理客户端和服务端的,对于客户端有2个方法就是发送请求和等待服务端响应,对于服务端同样2个方法那就是等待客户端请求和响应客户端

public class Handler {
private boolean isClientRequest=false;
public void sendRequest(){
synchronized (this){
isClientRequest=true;
this.notifyAll();
}
}
public void waitResponse() throws InterruptedException {
synchronized (this){
while (isClientRequest){
this.wait();
}
}
} public void receiveRequest(){
synchronized (this) {
isClientRequest = false;
this.notifyAll();
}
}
public void waitRequest() throws InterruptedException {
synchronized (this){
while (!isClientRequest){
this.wait();
}
}
}
}

现在我们写客户端代码,客户端肯定先发送请求,但是先等待1s为了让服务端处于等待的效果,发送请求后就处于等待状态直到服务端的响应

public class Client implements Runnable {
private Handler handler; public Client(Handler handler) {
this.handler = handler;
} public void run() {
try {
while (!Thread.interrupted()) {
System.out.println("客户端发送请求");
TimeUnit.SECONDS.sleep(1);
this.handler.sendRequest();//第二步
System.out.println("等待服务端的响应");
this.handler.waitResponse();//第三步
}
} catch (InterruptedException e) { }
System.out.println("客户端已经完成请求");
}

然后我们写服务端代码,服务端首先处于等待状态,收到客户端请求后立马进行处理,处理完毕之后再次等待客户端的请求

public class Server implements Runnable {
public Handler handler; public Server(Handler handler) {
this.handler = handler;
} public void run() {
try {
while (!Thread.interrupted()) {
System.out.println("等待客户端请求");
this.handler.waitRequest();//第一步
System.out.println("处理客户端请求");
TimeUnit.SECONDS.sleep(1);
this.handler.receiveRequest();//第四步
}
} catch (InterruptedException e) { }
System.out.println("服务端处理已经完成");
}
}

从上面我们预测肯定是等待客户端请求,发送请求,等待响应,处理客户端请求这样循环的结果。如下

在说一下wait和sleep的区别

区别1:在wait期间对象锁使释放的

区别2:可以通过notify和notifyAll,或者玲命令到期,从wait中恢复执行。如果wait不接受任何参数,这种wait将无线的等待下去,直到线程收到notify或notifyall的消息

关于synchronized、wait、notify已经notifyAll的使用的更多相关文章

  1. Java多线程---------同步与死锁:synchronized;等待与唤醒:wait、notify、notifyAll;生命周期

    1.问题的引出 class MyThread implements Runnable{ private int ticket = 5 ; // 假设一共有5张票 public void run(){ ...

  2. 005-线程sleep、join、yield、wait、notify、notifyAll、run、start、synchronized

    一.线程sleep join yield wait 1.sleep() 使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁.也就是说如果有synchroni ...

  3. 为什么wait,notify和notifyAll要与synchronized一起使用?

    https://blog.csdn.net/qq_39907763/article/details/79301813 Object.wait(),Object.notify(),Object.noti ...

  4. 如何在 Java 中正确使用 wait, notify 和 notifyAll(转)

    wait, notify 和 notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视.本文对这些关键字的使用进行了描述. 在 Java 中可以用 wait ...

  5. 线程同步以及 yield() wait()和notify()、notifyAll()

    1.yield() 该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会. 2.wait()和notify().notifyAll() 这 ...

  6. Java Thread wait, notify and notifyAll Example

    Java Thread wait, notify and notifyAll Example Java线程中的使用的wait,notify和nitifyAll方法示例. The Object clas ...

  7. Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

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

    一.线程同步 1.线程同步的目的是为了保护多个线程访问一个资源时对资源的破坏. 2.线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对 ...

  9. 最简实例说明wait、notify、notifyAll的使用方法

    wait().notify().notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态. 这三个方法最终调用的都是jvm级的native方法.随着jvm运行平台的不同可能有些 ...

随机推荐

  1. php 实例说明 socket通信机制

    php 实例说明 socket通信机制 张映 发表于 2010-04-24 分类目录: php 一,socket是什么 什么是socket 所谓socket通常也称作"套接字",用 ...

  2. 【转】Android ListView加载不同的item布局

    原创教程,转载请保留出处:http://www.eoeandroid.com/thread-72369-1-1.html     最近有需求需要在listView中载入不同的listItem布局,开始 ...

  3. [HNOI2004]Language L语言

    2777: [HNOI2004]Language L语言 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 10  Solved: 5[Submit][S ...

  4. 字符集 ISO-8859-1(2)

    HTML 支持的数学符号 结果 描述 实体名称 实体编号 ∀ for all ∀ ∀ ∂ part ∂ ∂ ∃ exists &exists; ∃ ∅ empty ∅ ∅ ∇ nabla ∇ ...

  5. ResultSet遍历过程中修改自身数据,不会改变循环的过程

    ResultSet遍历过程中修改自身数据,不会改变循环的过程: import java.sql.Connection; import java.sql.PreparedStatement; impor ...

  6. android 快捷技巧

    快捷方式 <!--[if !supportLists]-->0. Ctrl + 1 (快速修复) <!--[if !supportLists]-->1. Ctrl + D (删 ...

  7. Flex Socket 安全沙箱问题解决

    Flex使用Socket与C++通讯时遇到了安全沙箱问题,NND,折腾我半天,这是我的解决方法: 1):策略文件与主套接字在同一端口,只需调用 Socket.connect() 或 XMLSocket ...

  8. js冒泡排序及计算其运行时间

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  9. TCP协议滑动窗口(一)——控制数据传输速率

    窗口大小:TCP头中一个16位的域,表示当前可用接受缓冲区大小.在每个TCP对等段连接初始化时,告诉对方自己的窗口大小(不一定是满额,假如满额65201字节,可能暂时通告5840字节).若客户端接受数 ...

  10. 模式字符串匹配问题(KMP算法)

    这两天又看了一遍<算法导论>上面的字符串匹配那一节,下面是实现的几个程序,可能有错误,仅供参考和交流. 关于详细的讲解,网上有很多,大多数算法及数据结构书中都应该有涉及,由于时间限制,在这 ...