0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁
什么是同步
- 在上一篇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(多线程)一样
0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁的更多相关文章
- 线程同步 synchronized 同步代码块 同步方法 同步锁
一 同步代码块 1.为了解决并发操作可能造成的异常,java的多线程支持引入了同步监视器来解决这个问题,使用同步监视器的通用方法就是同步代码块.其语法如下: synchronized(obj){ // ...
- 0023 Java学习笔记-面向对象-初始化代码块
初始化代码块 在18篇-类的基本要素中说到,类的三大成员:成员变量.构造方法.方法,初始化代码块是类的第4个成员 初始化块用于对类或者对象的初始化, 一个类的初始化块可以有0-多个,按先后顺序执行 跟 ...
- Java学习笔记 - 类方法与代码块的执行顺序
类的初始化顺序 使用一个简单的父子类例子来做示范,代码执行顺序在代码后有标注. class Parent { public static String p_StaticField = "父类 ...
- JAVA之旅(十三)——线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this
JAVA之旅(十三)--线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this 我们继续上个篇幅接着讲线程的知识点 一.线程的安全性 当我们开启四个窗口(线程 ...
- Java学习笔记-多线程-创建线程的方式
创建线程 创建线程的方式: 继承java.lang.Thread 实现java.lang.Runnable接口 所有的线程对象都是Thead及其子类的实例 每个线程完成一定的任务,其实就是一段顺序执行 ...
- 线程的同步机制:同步代码块&同步方法
解决存在的线程安全问题:打印车票时出现重票,错票 使用同步代码块的解决方案 TestWindow2 package com.aff.thread; /* 使用实现Runnable接口的方式,售票 存在 ...
- java学习笔记 --- 多线程(线程安全问题——同步代码块)
1.导致出现安全问题的原因: A:是否是多线程环境 B:是否有共享数据 C:是否有多条语句操作共享数据 2.解决线程安全问题方法: 同步代码块: synchronized(对象){ 需要同步的代码; ...
- 0038 Java学习笔记-多线程-传统线程间通信、Condition、阻塞队列、《疯狂Java讲义 第三版》进程间通信示例代码存在的一个问题
调用同步锁的wait().notify().notifyAll()进行线程通信 看这个经典的存取款问题,要求两个线程存款,两个线程取款,账户里有余额的时候只能取款,没余额的时候只能存款,存取款金额相同 ...
- JAVA学习笔记 -- 多线程之共享资源
在多线程程序执行过程中,可能会涉及到两个或者多个线程试图同一时候訪问同一个资源.为了防止这样的情况的发生,必须在线程使用共享资源时给资源"上锁",以阻挡其他线程的訪问. 而这样的机 ...
随机推荐
- Entity Framework 6 Recipes 2nd Edition(13-5)译 -> 使POCO的修改追踪更高
问题 你正在使用POCO,你想提高修改跟踪的性能,同时使内存消耗更少.另外,你想通过EF的CodeFirst方式来实现. 解决方案 假设你有一个关于Account(帐户)和相关的Payments(支付 ...
- AKKA 笔记 - 有限状态机 -2
AKKA 笔记 - 有限状态机 -2 原文地址: http://rerun.me/2016/05/22/akka-notes-finite-state-machines-2/ 在上一节的Akka FS ...
- ajax基础学习
AJAX即"Asynchronous JavaScript and XML",意思是异步JavaScript和XML,是指一种创建交互式网页的网页开发技术. 虽然现在很少有人去自己 ...
- Android-TextView跑马灯效果
要实现跑马灯还是比较简单的. 同时有几个需要注意的点,先上代码: public class MTView extends TextView { public MTView(Context contex ...
- 最近做了了解java基础的一些题,整理自己用到的一些函数和了解的一些名词
[程序1]题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子对数为多少? 程序分析: 兔子的规律为数列1,1,2, ...
- Qt5 开发 iOS 应用之访问 SQLite 数据库
开发环境: macOS 10.12.1 Xcode 8.1 Qt 5.8 iPhone 6S+iOS 10.1.1 源代码: 我在 Qt 程序里指定了数据库的名称来创建数据库,在 Win10.An ...
- Hyper-V1:创建和管理虚拟机
Hyper-V是微软的管理虚拟机(Virtual Machine)的服务,在安装Hyper-V功能之后,系统自动安装可视化的虚拟机管理工具:Hyper-V Manager.在同一台物理机上,能够使用H ...
- LATEX数学公式基本语法
TEX 是Donald E. Knuth 编写的一个以排版文章及数学公式为目标的计算机程序.TEX的版本号不断趋近于π,现在为3.141592.由Pascal 语言写成,特点: 免费.输出质量高.擅长 ...
- 《JS设计模式笔记》构造函数和工厂模式创建对象
工厂模式 function createPerson (name,age,job) { var o=new Object(); o.name=name; o.age=age; o.job=job; o ...
- ElasticSearch 5学习(7)——分布式集群学习分享2
前面主要学习了ElasticSearch分布式集群的存储过程中集群.节点和分片的知识(ElasticSearch 5学习(6)--分布式集群学习分享1),下面主要分享应对故障的一些实践. 应对故障 前 ...