什么是同步

  • 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条线程访问,一条线程在执行一个循环的过程中被中断,下一个线程则出现错误
  • 因此,线程任务中可能引起错误的地方应当被一次执行完毕

同步代码块

  • 用同步代码块改写上面的代码
package testpack;public class Test1  { public static void main(String[] args){ System.out.println("现在是主线程: "+Thread.currentThread()); System.out.println("下面新建两个线程");A a=new A(500); new Thread(a,"线程A").start(); new Thread(a,"线程B").start();new Thread(a,"线程C").start();}
}
class A implements Runnable{private int tickets;A (int tick){tickets=tick;}private Object obj=new Object();              //同步监视器public void run() {synchronized(obj){                        //同步代码块for (;tickets>0;tickets--) {System.out.println("当前线程:"+Thread.currentThread()+" 卖出第 "+tickets+" 张票。");try{Thread.sleep(1);              //让当前线程暂停1毫秒,其他线程也不能执行该同步代码块}catch(InterruptedException ex){ex.printStackTrace();}if (tickets==1){System.out.println("票已卖完,当前线程是: "+Thread.currentThread());}}}}
}
  • 同步监视器,就是一个普通的对象,就像一把锁,只有获得了同步监视器的线程才能执行同步代码块。
  • 同步代码块执行一次完毕后,将会释放锁,接下来是这条线程拿到同步锁,还是其他其他线程,则不一定,根据线程调度而定,但是在同步代码块执行过程中,不会被中断,一个同步任务会被一次执行完毕

同步方法

  • 在同步代码块中,synchonized关键字在run()方法内部,修饰的是一段代码,也可以用来修饰run()方法,也就是同步方法
  • synchronized不只可以修饰run()方法,还可以修饰其他方法,只要是需要一次同步完成的任务,然后再在run()方法中被调用
  • 同步方法中有一个隐式的同步监视器,就是this,也就是调用run()方法(或同步方法)的这个对象
  • 还是上面的实例,用同步方法改写
package testpack;public class Test1  { public static void main(String[] args){ System.out.println("现在是主线程: "+Thread.currentThread()); System.out.println("下面新建两个线程");A a=new A(500);new Thread(a,"线程A").start();new Thread(a,"线程B").start();new Thread(a,"线程C").start();}
}
class A implements Runnable{private int tickets;A (int tick){tickets=tick;}public synchronized void run() {                                    //同步方法for (;tickets>0;tickets--) {System.out.println("当前线程:"+Thread.currentThread()+" 卖出第 "+tickets+" 张票。");try{Thread.sleep(1);}catch(InterruptedException ex){ex.printStackTrace();}if (tickets==1){System.out.println("票已卖完,当前线程是: "+Thread.currentThread());}}}
}

释放同步监视器

  • 当前线程的同步任务(同步方法、同步代码块)执行完毕
  • 在同步任务中,遇到break、return,终止了同步任务
  • 在同步任务中,出现Error、Exception等,导致同步任务结束
  • 在同步任务中,执行了同步监视器对象的wait()方法,则当前线程暂停,并释放同步监视器
  • 不会释放同步监视器的情况:
    • 同步任务中,调用Thread.sleep()、Thread.yield()方法来暂停当前线程的执行
    • 同步任务中,其他线程调用了该线程的suspend()方法将该线程挂起

同步锁

  • 除了可以用new Object()和this作同步监视器往外,还可以定义专门的同步锁,且功能更加强
  • Lock接口
    • ReentrantLock实现类
  • ReadWriteLock
    • ReentrantReadWriteLock实现类
    • ReentrantReadWriteLock.ReadLock
    • ReentrantReadWriteLock.WriteLock
  • StampedLock
  • 示例:用ReentrantLock改写上面的代码
package testpack;import java.util.concurrent.locks.ReentrantLock;public class Test1  { public static void main(String[] args){ System.out.println("现在是主线程: "+Thread.currentThread()); System.out.println("下面新建两个线程");A a=new A(50);new Thread(a,"线程A").start();new Thread(a,"线程B").start();new Thread(a,"线程C").start();}
}
class A implements Runnable{private final ReentrantLock lock=new ReentrantLock();    //定义一个同步锁private int tickets;A (int tick){tickets=tick;}public void run() {lock.lock();                                     //加锁for (;tickets>0;tickets--) {System.out.println("当前线程:"+Thread.currentThread()+" 卖出第 "+tickets+" 张票。");if (tickets==1){System.out.println("票已卖完,当前线程是: "+Thread.currentThread());}}lock.unlock();                                    //释放锁}
}

死锁

  • 两个线程各拿一把锁,下一步运行都需要对方手里那把锁,但都拿不到,则造成死锁,程序不能继续执行
package testpack;
public class Test1  { public static void main(String[] args){ DeadLock dl=new DeadLock();new Thread(dl).start();dl.init();}
}
class DeadLock implements Runnable {A a=new A();B b=new B();public void init(){a.a1(b);System.out.println("进入主线程");}public void run(){b.b1(a);System.out.println("进入子线程");}
}
class A {public synchronized void a1(B b){System.out.println("当前线程是:"+Thread.currentThread().getName()+" ,正在执行a1()");try{Thread.sleep(10);}catch(InterruptedException ex){ex.printStackTrace();}System.out.println("当前线程是:"+Thread.currentThread().getName()+" ,准备调用b2()");b.b2();   //b2方法是同步方法,调用该方法要对调用的对象b加锁}public synchronized void a2(){System.out.println("这是a2()方法");}
}
class B{public synchronized void b1(A a){System.out.println("当前线程是:"+Thread.currentThread().getName()+" ,正在执行b1()");try{Thread.sleep(10);}catch(InterruptedException ex){ex.printStackTrace();}System.out.println("当前线程是:"+Thread.currentThread().getName()+" ,准备调用a2()");a.a2();   //a2方法是同步方法,调用该方法要对调用的对象a加锁}public synchronized void b2(){System.out.println("这是b2()方法");}
}
  • 上面在调用a.a2()和b.b2()方法时,分别要对a对象和b对象加锁,但这时,a、b对象的锁都在对方手里,造成两个线程阻塞

其他

  • 可变类的线程安全是以降低程序的运行效率为代价的
  • 不要对线程安全类的所有方法都进行同步,只对那些改变共享资源的方法进行同步
  • 如果一个类有单线程和多线程运行环境,那么应该提供两种版本,就是StringBuilder(单线程)和StringBuffer(多线程)一样

转载于:https://www.cnblogs.com/sonng/p/6134444.html

0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁相关推荐

  1. Java学习笔记---多线程并发

    Java学习笔记---多线程并发 (一)认识线程和进程 (二)java中实现多线程的三种手段 [1]在java中实现多线程操作有三种手段: [2]为什么更推荐使用Runnable接口? [3][补充知 ...

  2. Java学习笔记---多线程同步的五种方法

    一.引言 前几天面试,被大师虐残了,好多基础知识必须得重新拿起来啊.闲话不多说,进入正题. 二.为什么要线程同步 因为当我们有多个线程要同时访问一个变量或对象时,如果这些线程中既有读又有写操作时,就会 ...

  3. 多线程(同步代码块和同步函数)

    线程安全问题 当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,     另一个线程参与进来执行.导致共享数据的错误. 解决办法:     对多条操作共享数据的语句 ...

  4. Java学习笔记 --- 多线程

    一.线程相关概念 程序 程序是为完成特定任务,用某种语言编写的一组指令的集合.简单的说就是我们写的代码 进程 1.进程是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存 ...

  5. java锁方法和锁代码块_java的同步方法和同步代码块,对象锁,类锁区别

    /** * @author admin * @date 2018/1/12 9:48 * 做用在同一个实例对象上讨论 * synchronized同步方法的测试 * 两个线程,一个线程调用synchr ...

  6. 0040 Java学习笔记-多线程-线程run()方法中的异常

    run()与异常 不管是Threade还是Runnable的run()方法都没有定义抛出异常,也就是说一条线程内部发生的checked异常,必须也只能在内部用try-catch处理掉,不能往外抛,因为 ...

  7. java学习笔记 多线程(一)创建多线程,线程常用方法

    首先是进程和线程的区别,进程就是像打开csgo.exe就是一个进程,然后打开LOL.exe又是另外一个进程了. 而线程呢,就是在同一进程内部,发生的事情. 那么就开始了解线程! 创建多线程: 线程有三 ...

  8. java学习笔记 --- 多线程(多线程的控制)

    1.线程休眠    public static void sleep(long millis) public class ThreadSleep extends Thread {@Overridepu ...

  9. 谈谈你对同步代码块中同步监视器和共享数据的理解及各自要求。

    同步监视器:俗称锁. ①任何一个类的对象都可以充当锁. ②多个线程共用同一把锁. 共享数据:多个线程共同操作的数据,即为共享数据.需要使用同步机制将操作共享数据的代码包起来.不能包多了,也不能包少了.

最新文章

  1. java打印6个偶数_Java编写一个应用程序,打印所有偶数从2到100
  2. IOS设置导航栏的背景图片和文字
  3. 来自网页的消息服务器繁处理忙,EventSource 对象用于接收服务器发送事件通知,是网页自动获取来自服务器的更新...
  4. 云原生时代|分布式系统设计知识图谱(内含22个知识点)
  5. 常见php面试题,常见的 PHP 面试题和答案分享
  6. mysql sequence java_MySQL增加Sequence管理功能
  7. 【转载】javascript,声明变量和导入时,大括号的特殊用法
  8. td设置自动隐藏,hover事件触发全部显示,table列表不用担心信息太长导致界面不美观
  9. win10 当前操作环境不支持支付宝控件 完美解决办法
  10. Log4j2 Zero Day 漏洞 Apache Flink 应对指南(二)
  11. 有道词典java下载电脑版下载手机版下载安装_网易有道词典下载-网易有道词典 安卓版v8.3.4-PC6安卓网...
  12. 共轭梯度法python实现
  13. fba4droid android,fba4droid模拟器
  14. 77. 组合 - 递归
  15. Android Gradle plugin requires Java 11 to run. You are currently using Java 1.8.
  16. linux 前台和后台,Linux前台进程与后台进程的区别
  17. Caused by: java.io.IOException: The temporary upload location [......] is not valid
  18. hdu1429 胜利大逃亡(续)
  19. JUC Striped64
  20. 00 Linux到底是什么?

热门文章

  1. 设置Clover默认进入Windows,按快捷键F8可选择不同的引导
  2. 重学java基础第十三课:java帝国的诞生
  3. 前端学习(3260):js高级教程(4)instanceof
  4. 前端学习(3002):vue+element今日头条管理--模块介绍
  5. [css] 举例说明实现圆角的方式有哪些?
  6. [css] css的哪个属性可以把所有元素或其父元素的属性重置呢?
  7. [css] 你用过css的tab-size属性吗?浏览器默认显示tab为几个空格?
  8. [css] 如何让表格单元格等宽显示
  9. [css] 举例说明BFC会与float元素相互覆盖吗?为什么?
  10. [vue] 你知道v-model的原理吗?说说看