一、概念

1、多个线程之间访问同一资源,进行协调的过程是线程同步。例如两个人同时操作同一银行账户。解决方法:加锁

2、Java种引入对象互斥锁的概念,保证共享数据操作的完整性。每个对象都对应于一个可称为“互斥锁”的标记,这个标记保证在任一时刻,只能有一个线程访问该对象。关键字synchronized来与对象的互斥锁联系。

  当synchronized修饰方法时,是指当前调用该方法的对象被锁定。当synchronized(*)修饰代码块时,*指定的对象被锁定。如果*为this,则与synchronized修饰方法达到的效果一致。

public class Test extends Thread{
public static void main(String args[]){
Test t = new Test();
Test t2 = new Test();
t.start();
t.m2();
}
public synchronized void m1(){
System.out.println("m1");
try{
Thread.sleep(10000);
}catch(Exception e){ }
}
public void m2(){
System.out.println("m2");
try{
Thread.sleep(10000);
}catch(Exception e){
}
}
public void run(){
m1();
}
}

输出:m2m1

public class Test extends Thread{
public static void main(String args[]){
Test t = new Test();
Test t2 = new Test();
t.start();
t.m2();
}
public synchronized void m1(){
System.out.println("m1");
try{
Thread.sleep(10000);
}catch(Exception e){ }
}
public synchronized void m2(){
System.out.println("m2");
try{
Thread.sleep(10000);
}catch(Exception e){
}
}
public void run(){
m1();
}
}

输出:m2 m1

public class Test extends Thread{
public static void main(String args[]){
Test t = new Test();
Test t2 = new Test();
t.start();
t2.start();
t.m2();
t2.m2();
}
public synchronized void m1(){
System.out.println("m1");
try{
Thread.sleep(10000);
}catch(Exception e){ }
}
public synchronized void m2(){
System.out.println("m2");
try{
Thread.sleep(10000);
}catch(Exception e){
}
}
public void run(){
m1();
}
}

输出:m1m1 m2 m2

public class Test extends Thread{
static Object lock=new Object();
public static void main(String args[]){
Test t = new Test();
Test t2 = new Test();
t.start();
t2.start();
t.m2();
t2.m2();
}
public void m1(){
synchronized(lock){
System.out.println("m1");
try{
Thread.sleep(10000);
}catch(Exception e){ }
}
}
public void m2(){
synchronized(lock){
System.out.println("m2");
try{
Thread.sleep(10000);
}catch(Exception e){
}
}
}
public void run(){
m1();
}
}

输出:m1 m1 m2 m2

二、举例

t1,t2两个线程同时访问同一个对象test。

 public class TestSync implements Runnable{
Timer timer = new Timer();
public static void main(String args[]){
TestSync test = new TestSync();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
public void run(){
timer.add(Thread.currentThread().getName());
}
} class Timer{
private static int num = 0 ;
public void add(String name){
synchronized(this){
num++;
try{
Thread.sleep(1);//改成 wait(100);不行,因为wait释放锁
}catch(InterruptedException e){
}
System.out.println(name+",你是第"+num+"个使用timer的线程");
}
}
}

输出:

t1,你是第1个使用timer的线程
t2,你是第2个使用timer的线程

如果去掉synchronized则输出错误:

t2,你是第2个使用timer的线程
t1,你是第2个使用timer的线程

错误原因:一个线程的执行过程中,被另一个线程打断了。

解决方法一:在20行加入synchoronized互斥锁(上面的代码)。因为synchoronized锁定的是当前对象,而

Thread t1 = new Thread(test);
Thread t2 = new Thread(test);

所以能够达到同步效果,因为t1,t2是同一个test对象,对应的timer也是同一个。如果

Thread t1 = new Thread(test1);
Thread t2 = new Thread(test2);

则不能达到同步效果,因为t1,t2不是同一对象,对应的timer不是同一个。

package test2;
public class TestSync implements Runnable{
static Timer timer = new Timer();
public static void main(String args[]){
TestSync test = new TestSync();
TestSync test2 = new TestSync();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test2);
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
public void run(){
timer.add(Thread.currentThread().getName());
}
} class Timer{
private static int num = 0 ;
public synchronized void add(String name){
num++;
try{
Thread.sleep(3000);//改成 wait(100);不行,因为wait释放锁
}catch(InterruptedException e){
}
System.out.println(name+",你是第"+num+"个使用timer的线程");
}
}

输出正确,因为timer是同一个对象。

解决方法二:或者在19行加入同步方法。如下:

public class TestSync implements Runnable{
Timer timer = new Timer();
public static void main(String args[]){
TestSync test = new TestSync();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
public void run(){
timer.add(Thread.currentThread().getName());
}
} class Timer{
private static int num = 0 ;
public synchronized void add(String name){
//synchronized(this){
num++;
try{
Thread.sleep(1);
}catch(InterruptedException e){
}
System.out.println(name+",你是第"+num+"个使用timer的线程");
//}
}
}

注意调用sleep的线程不释放锁,但调用wait的线程释放锁。

三、死锁的模拟

public class TestDeadLock implements Runnable{
public int flag = 1 ;
static Object o1 = new Object();
static Object o2 = new Object();
public void run(){
System.out.println("flag="+flag);
if(flag==1){
synchronized(o1){
try{
Thread.sleep(500);
}catch(Exception e){
e.printStackTrace();
}
synchronized(o2){
System.out.println("1");
}
}
}
if(flag==0){
synchronized(o2){
try{
Thread.sleep(500);
}catch(Exception e){
e.printStackTrace();
}
synchronized(o1){
System.out.println("0");
}
}
}
}
public static void main(String[] args){
TestDeadLock t1 = new TestDeadLock();
TestDeadLock t2 = new TestDeadLock();
t1.flag = 1 ;
t2.flag = 0 ;
Thread t11 = new Thread(t1);
Thread t22 = new Thread(t2);
t11.start();
t22.start();
}
}

死锁的条件(四个同时满足):互斥、部分锁定、循环等待、不可剥夺

解决方法:锁的粒度加粗(一次性锁定全部需要的资源,破坏部分锁定)

     规定顺序(破坏循环等待)

       可以剥夺(破坏不可剥夺)

四、synchronized面试题

(1)

问:上面程序中一个线程调用m1方法时,另一个线程可以同时调用m2方法吗?

答案:可以同时执行。main()函数运行输出1000,b=1000,说明可以同时执行两个方法。(如果输出100,b=1000则说明不可以)。如果将m2方法改为:

结果又是什么呢?答案:可以同时执行。输出2000,b=2000。

执行过程:m1修改b为1000,之后睡眠,m2修改b为2000,main中打印2000,m1睡眠结束打印b=2000。

注:那个方法加了synchronized只是说明该方法同时可以被一个线程调用,但是其他线程仍然可以自由调用非同步的方法(可能对同步方法产生影响,例如涉及到该同步方法中的变量)。

(2)正确做法:m1,m2都加同步

所以说,b相当于一个资源,如何控制该资源能够被正确访问呢?这就需要把涉及到该资源b的所有方法都考虑到!哪些方法是不是要设置为synchronized,所以本题中需要把m1,m2方法都加锁,即用synchronized修饰为同步方法。如下:

结果又是什么?答案:输出1000,b=1000

执行过程:m1,m2不能同时执行。运行m2方法,b的值改为2000,之后运行m1方法输出b改为1000,m1睡眠,main主线程输出1000,m1方法输出b=1000。

5、两个方法一个读该对象,另一个修改该对象的值。一般的情况下改的方法加锁(同步方法),因为不允许多个线程同时改;读的方法不加锁(非同步方法),因为可以同时读。所以是加只读锁。

      

JAVA笔记15-线程同步的更多相关文章

  1. java笔记--关于线程同步(7种同步方式)

    关于线程同步(7种方式) --如果朋友您想转载本文章请注明转载地址"http://www.cnblogs.com/XHJT/p/3897440.html"谢谢-- 为何要使用同步? ...

  2. java笔记--关于线程同步(5种同步方式)【转】

    为何要使用同步?     java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),      将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完 ...

  3. java笔记--关于线程同步(5种同步方式)

    转自:http://www.2cto.com/kf/201408/324061.html 为何要使用同步?     java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改 ...

  4. 关于Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇高质量的博文)

    Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文) 前言:在学习多线程时,遇到了一些问题,这里我将这些问题都分享出来,同时也分享了几篇其他博客主的博客,并且将我个人的理解也分享 ...

  5. Java中的线程同步

    Java 中的线程同步问题: 1. 线程同步: 对于访问同一份资源的多个线程之间, 来进行协调的这个东西. 2. 同步方法: 当某个对象调用了同步方法时, 该对象上的其它同步方法必须等待该同步方法执行 ...

  6. Java并发包——线程同步和锁

    Java并发包——线程同步和锁 摘要:本文主要学习了Java并发包里有关线程同步的类和锁的一些相关概念. 部分内容来自以下博客: https://www.cnblogs.com/dolphin0520 ...

  7. Java多线程 3 线程同步

    在之前,已经学习到了线程的创建和状态控制,但是每个线程之间几乎都没有什么太大的联系.可是有的时候,可能存在多个线程多同一个数据进行操作,这样,可能就会引用各种奇怪的问题.现在就来学习多线程对数据访问的 ...

  8. java中实现线程同步

    为何要使用同步? java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查), 将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他 ...

  9. java并发:线程同步机制之Volatile关键字&原子操作Atomic

    volatile关键字 volatile是一个特殊的修饰符,只有成员变量才能使用它,与Synchronized及ReentrantLock等提供的互斥相比,Synchronized保证了Synchro ...

  10. Java高级之线程同步

    本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 关于实现多线程的意义,"从业四年看并发"一文已经讲述,而本篇主要讲一下常用的设计 ...

随机推荐

  1. RTX数据表分析

    /******************************************* * UserName 做主键 **************************************** ...

  2. python线程池--threadpool

    在爬虫时,有时候解析获得了很多图片或视频地址时,如果一个个下载完成再去下载另一个,这样执行效率太慢了,此时就可用到线程池threadpool,使用基本步骤如下: 1.定于任务函数 2.创建线程池,定义 ...

  3. js --- ffmpeg rtsp 推流,websocket通信,cnavas 渲染

    首先选择正确的ffmpeg包 百度搜索ffmpeg官网,点击进入下图所示. 点击download,进入下载页面,选择window,window下面只有一个builds版本,如图红线部分圈出来的 点击b ...

  4. 第八周课程总结&实验报告

    实验六 Java异常 •实验目的 •理解异常的基本概念: •掌握异常处理方法及熟悉常见异常的捕获方法. • 实验要求 •练习捕获异常.声明异常.抛出异常的方法.熟悉try和catch子句的使用. •掌 ...

  5. for (;;) 与 while (true),哪个更快?

    Java技术栈 www.javastack.cn 优秀的Java技术公众号 在 JDK8u 的 jdk 项目下做个很粗略的搜索: mymbp:/Users/me/workspace/jdk8u/jdk ...

  6. python中迭代器和生成器。

    前言:很多python教程中,对python的解释不容易理解,本文记录自己的理解和体会,是对迭代器和生成器的初步理解. 迭代器: 迭代器的实质是实现了next()方法的对象,常见的元组.列表.字典都是 ...

  7. zookeeper 选举leader详解

    一.前言 前面学习了Zookeeper服务端的相关细节,其中对于集群启动而言,很重要的一部分就是Leader选举,接着就开始深入学习Leader选举. 二.Leader选举 2.1 Leader选举概 ...

  8. ds replicas是什么

    以上是我百度的答案.很显然,这个对于初学者很不友好,中文的每一个靠谱的,英文的都TM是how 和 why .But 我要 what! 所以只能自己搞,自己琢磨,搞完分享出来,助力后来者不掉坑,有所查. ...

  9. Mysql数据库中的输入命令各类知识总结

    1.链接数据库的命令---mysql-u root-p 回车,输入密码//在cmd上输入自己的账号密码 2.查看:show databases: 3.创建数据库:create +database+数据 ...

  10. vue项目兼容es6语法跟IE浏览器

    要安装babel-polyfill和es6-promise.用来兼容ie和es6: 附赠链接下载:https://babeljs.io/docs/en/6.26.3/babel-polyfill:ht ...