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学习笔记 -- 多线程之共享资源
在多线程程序执行过程中,可能会涉及到两个或者多个线程试图同一时候訪问同一个资源.为了防止这样的情况的发生,必须在线程使用共享资源时给资源"上锁",以阻挡其他线程的訪问. 而这样的机 ...
随机推荐
- myeclipse学习总结二(myeclipse安装svn插件)
1.在myeclipse中要安装svn插件,首先是下载svn插件. 下载地址:http://subclipse.tigris.org/servlets/ProjectDocumentList?fold ...
- 导入 cocoapods引入的第三方库头文件,提示找不到
解决办法: 1,Build Settings ->Header Search Paths 2, 双击 Header Search Paths 添加一个, $(PODS_ROOT), 选择项选: ...
- Maven pom文件常用配置,转载
什么是POM Project Object Model,项目对象模型.通过xml格式保存的pom.xml文件.作用类似ant的build.xml文件,功能更强大.该文件用于管理:源代码.配置文件.开发 ...
- WCF学习之旅—WCF服务配置(十四)
一.概述 我们在前面章节中讲了寄宿,在前面的实例中也用到了配置文件,这一篇主要讲讲如何在应用配置文件,提高WCF程序的灵活性.在编写WCF服务应用程序时,编写配置项也是其中一项主要工作,在前面的几个示 ...
- ZooKeeper安装与配置
一. 单机安装.配置: 1. 下载zookeeper二进制安装包下载地址:http://apache.dataguru.cn/zookeeper/zookeeper-3.4.3/zookeeper-3 ...
- JavaScript的同步与异步
1.手绘一张图说明. 2.为什么JavaScript是单线程(这里引用阮一峰老师的话) JavaScript的单线程,与它的用途有关. 作为浏览器脚本语言,JavaScript的主要用途是与用户互动, ...
- MVC5 网站开发之三 数据存储层功能实现
数据存储层在项目Ninesky.DataLibrary中实现,整个项目只有一个类Repository. 目录 奔跑吧,代码小哥! MVC5网站开发之一 总体概述 MVC5 网站开发之二 创建项目 ...
- 深入分析Spring 与 Spring MVC容器
1 Spring MVC WEB配置 Spring Framework本身没有Web功能,Spring MVC使用WebApplicationContext类扩展ApplicationContext, ...
- .NET Core下的日志(2):日志模型详解
NET Core的日志模型主要由三个核心对象构成,它们分别是Logger.LoggerProvider和LoggerFactory.总的来说,LoggerProvider提供一个具体的Logger对象 ...
- iOS引入JavaScriptCore引擎框架(二)
为何放弃第一种方案 UIWebView的JSContext获取 上篇中,我们通过简单的kvc获取UIWebVIew的JSContext,但是实际上,apple并未给开发者提供访问UIWebVi ...