1、多线程的同步:

1.1、同步机制:在多线程中,可能有多个线程试图访问一个有限的资源,必须预防这种情况的发生。所以引入了同步机制:在线程使用一个资源时为其加锁,这样其他的线程便不能访问那个资源了,直到解锁后才可以访问。

1.2、共享成员变量的例子:成员变量与局部变量:

成员变量:

如果一个变量是成员变量,那么多个线程对同一个对象的成员变量进行操作,这多个线程是共享一个成员变量的。

局部变量:

如果一个变量是局部变量,那么多个线程对同一个对象进行操作,每个线程都会有一个该局部变量的拷贝。他们之间的局部变量互不影响。

下面举例说明:

实现了Runnable的线程类:

class MyThread3 implements Runnable{

//两个线程操作同一个对象,共享成员变量

//int i;

@Override

public void run() {

//两个线程操作同一个对象,各自保存局部变量的拷贝

int i = 0;

while(i<100){

System.out.println(i);

i++;

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

在main方法中用两个线程操作同一个对象:

public static void main(String[] args) {

MyThread3 myThread = new MyThread3();

//下面两个线程对同一个对象(Runnable的实现类对象)进行操作

Thread thread = new Thread(myThread);

Thread thread2 = new Thread(myThread);

//各自保存局部变量的拷贝,互不影响,输出200个数字

thread.start();

thread2.start();

}

这里如果把i变成成员变量,则输出100个数字。

1.3、共享资源导致的读取错误下面举个例子,两个线程共用一个Number对象,通过Number类的getNumber方法获取数据,读取数据并改写时,发现了重复读操作:

首先创建一个Number类:

class Number{

private int number = 10;

public String getNumber(int i){

if(number > 0){

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

number -= i;

return "取出"+i+"成功,剩余数量:"+number;

}

return "取出"+i+"失败,剩余数量:"+number;

}

}

线程类,在线程类中的私有属性包含了Number类的引用:

class MyThread4 extends Thread{

//两个线程操作同一个对象,共享成员变量

Number number;

public MyThread4(Number number){

this.number = number;

}

@Override

public void run() {

System.out.println(number.getNumber(8));

}

}

在main函数中创建两个线程类,包含了同一个Number类实例的引用:

public static void main(String[] args) {

Number number = new Number();

//两个线程操作同一个对象,共享对象number的成员变量number

MyThread4 myThread = new MyThread4(number);

MyThread4 myThread2 = new MyThread4(number);

myThread.start();

myThread2.start();

}

这样,当第一个线程读取Number中的number变量时先保存下来再休眠0.1秒,然后第二个线程再读取number变量并保存,此时两个线程保存了同样的数字,在修改时,也就导致修改了同一个数字两次。

2、同步机制的实现:

在多线程并发编程中Synchronized一直是元老级角色,很多人都会称呼它为重量级锁,但是随着Java SE1.6对Synchronized进行了各种优化之后,有些情况下它并不那么重了”

Java中的每一个对象都可以作为锁。

对于同步方法,锁是当前实例对象。

对于静态同步方法,锁是当前对象的Class对象。

对于同步方法块,锁是Synchonized括号里配置的对象。

当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。

2.1、使用synchronized关键字创建synchronized方法:使用synchronized关键字,该关键字修饰的方法叫做同步方法。

Java中每个对象都有一个锁或者称为监视器,当访问某个对象的synchronized方法时,表示将该对象上锁,而不仅仅是为该方法上锁。

这样如果一个对象的synchronized方法被某个线程执行时,其他线程无法访问该对象的任何synchronized方法(但是可以调用其他非synchronized的方法)。直至该synchronized方法执行完。

静态的synchronized方法调用情况:

当调用一个对象的静态synchronized方法时,它锁定的并不是synchronized方法所在的对象,而是synchronized方法所在对象对应的Class对象。这样,其他线程就不能调用该类的其他静态synchronized方法了,但是可以调用非静态的synchronized方法。

结论:执行静态synchronized方法锁方法所在对象,执行非静态synchronized方法锁方法所在对象对应的Class对象。

下面是多线程调用静态的方法的例子,由于锁定了方法所在对象对应的Class对象,其他线程无法调用该方法所在对象其他的静态synchronized方法:

/**

* 定义一个类,包含了线程类需要调用的方法

*/

class Compute1{

//这时如果某个线程调用该方法,

//将锁定synchronized方法所在对象对应的class对象,

//而不是锁定synchronized方法所在对象

public synchronized static void execute(){

for(int i = 0; i<100; i++){

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("compute1:execute1 " + i++);

}

}

public synchronized static void execute2(){

for(int i = 0; i<100; i++){

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("compute1:execute2 " + i++);

}

}

}

main方法中两个线程分别调用同一个对象的两个static synchronized方法:

public static void main(String[] args) {

Compute1 com = new Compute1();

Thread thread1 = new Thread1(com);

Thread thread2 = new Thread2(com);

thread1.start();

thread2.start();

}

一次只能调用一个静态方法,直到执行完成。

2.2、使用synchronized创建同步代码块:通过使用synchronized同步代码块,锁定一个对象,该对象作为可执行的标志从而达到同步的效果:

/**

* 定义一个类,包含了线程类需要调用的方法

*/

class Compute1{

//通过同步代码块锁定object1对象进行锁定了其他同样的synchronized代码块

private Object object1 = new Object();

public void execute(){

synchronized(object1){

for(int i = 0; i<100; i++){

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("compute1:execute1 " + i++);

}

}

}

public synchronized void execute2(){

synchronized(object1){

for(int i = 0; i<100; i++){

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("compute1:execute2 " + i++);

}

}

}

}

如果想要使用synchronized同步代码块达到和使用synchronized方法同样的效果,可以锁定this引用:

synchronized(this){

}

2.3、synchronized方法和synchronized同步代码块的区别:synchronized同步代码块只是锁定了该代码块,代码块外面的代码还是可以被访问的。

synchronized方法是粗粒度的并发控制,某一个时刻只能有一个线程执行该synchronized方法。

synchronized同步代码块是细粒度的并发控制,只会将块中的代码同步,代码块之外的代码可以被其他线程同时访问。

java 多线程同步_详解Java多线程编程中的线程同步方法相关推荐

  1. java runnable 异常_详解Java中多线程异常捕获Runnable的实现

    详解Java中多线程异常捕获Runnable的实现 1.背景: Java 多线程异常不向主线程抛,自己处理,外部捕获不了异常.所以要实现主线程对子线程异常的捕获. 2.工具: 实现Runnable接口 ...

  2. java 代码块_详解java中的四种代码块

    在java中用{}括起来的称为代码块,代码块可分为以下四种: 一.简介 1.普通代码块: 类中方法的方法体 2.构造代码块: 构造块会在创建对象时被调用,每次创建时都会被调用,优先于类构造函数执行. ...

  3. java sleep唤醒_详解Java中的线程让步yield()与线程休眠sleep()方法

    Java中的线程让步会让线程让出优先级,而休眠则会让线程进入阻塞状态等待被唤醒,这里我们对比线程等待的wait()方法,来详解Java中的线程让步yield()与线程休眠sleep()方法 线程让步: ...

  4. java 引用传递_详解java的值传递、地址传递、引用传递

    详解java的值传递.地址传递.引用传递 一直来觉得对值传递和地址传递了解的很清楚,刚才在开源中国上看到一篇帖子介绍了java中的值传递和地址传递,看完后感受颇深.下边总结下以便更容易理解. 按照以前 ...

  5. java内部格式_详解java内部类的访问格式和规则

    详解java内部类的访问格式和规则 1.内部类的定义 定义一个类来描述事物,但是这个事物其中可能还有事物,这时候在类中再定义类来描述. 2.内部类访问规则 ①内部类可以直接访问外部类中的成员,包括私有 ...

  6. java comparator相等_详解Java中Comparable和Comparator接口的区别

    详解Java中Comparable和Comparator接口的区别 发布于 2020-7-20| 复制链接 摘记: 详解Java中Comparable和Comparator接口的区别本文要来详细分析一 ...

  7. Java implement意思_详解JAVA中implement和extends的区别

    详解JAVA中implement和extends的区别 发布于 2020-4-14| 复制链接 摘记: 详解JAVA中implement和extends的区别extends是继承父类,只要那个类不是声 ...

  8. java static 函数_详解java中的static关键字

    Java中的static关键字可以用于修饰变量.方法.代码块和类,还可以与import关键字联合使用,使用的方式不同赋予了static关键字不同的作用,且在开发中使用广泛,这里做一下深入了解. 静态资 ...

  9. java排序接口_详解JAVA使用Comparator接口实现自定义排序

    1.原则 Comparator接口可以实现自定义排序,实现Comparator接口时,要重写compare方法: int compare(Object o1, Object o2) 返回一个基本类型的 ...

最新文章

  1. CSS的元素显示模式(块内元素和行内元素)
  2. java script valueof_Javascript new Date().valueOf()的作用与时间戳由来详解
  3. iOS推送:要支持戴口罩解锁了!以为是黑科技,原来是骗我买iWatch...
  4. 创维 linux内核,Linux2.6内核在创维特jx2410平台上的移植四
  5. 代码确认小米官方在线预定——对代码实现的分析
  6. 理解图像中卷积操作的含义
  7. 对抗攻击公开课第二弹来啦,真题演练 + 代码实战
  8. java类使用其他类的变量_如何将java类中的变量加载到其他类
  9. Linux下的图形库curses写贪吃蛇,酷
  10. [bug]微信小程序使用 scroll-view 和 box-shadow 引起页面抖动
  11. 如何修改GitHub项目显示语言问题
  12. ggplot2 | 统计变换的初步理解
  13. 第一次LeetCode周赛心得(力扣-cn周赛,使用python3)
  14. linux把程序添加到全局环境变量
  15. 计算机字体库位置,电脑cad软件字体库的路径怎么添加
  16. 【计算机网络】计算机网络笔记-韩立刚老师
  17. RISC-V嵌入式开发入门篇2:RISC-V汇编语言程序设计(上)
  18. 【每天play】为了学好python需要从脚下做起,Linux基础-用户权限与组管理 P60-70
  19. HDU题目分类大全【大集合】
  20. V神赞许的Aztec,如何让以太坊更隐私?

热门文章

  1. 湖北大学计算机科学与技术怎么样,湖北大学(专业学位)计算机技术考研难吗
  2. python怎么导入apscheduler,如何强制apscheduler将作业添加到作业存储?
  3. UC浏览器屏幕亮度在哪设置 UC浏览器屏幕亮度调节方法2019
  4. django和mysql如何建模_Django如何设计数据模型
  5. ubuntu mysql master slave_Ubuntu下MySQL5.5 配置主从(Master Slave)同步
  6. SQLIntegrityConstraintViolationException: 异常解决
  7. Linux系统下解压缩安装MongoDB
  8. mac11.14 mysql_mysql 5.7 11 章 数据类型(1)
  9. java数据库配置_java--数据库(文件配置连接,自定义连接池)
  10. 使用 ref 对已渲染到页面的节点进行标记