线程不安全的问题分析

当多线程并发访问同一个资源对象的时候,可能出现线程不安全的问题.但是,我们分析打印的结果,发现没有问题,意识:看不到问题,我们经验不够,问题出现的不够明显.接下来为了让问题更明显:我们使用Thread.sleep(10);//当前线程睡10毫秒,当前线程休息着,让其他线程去抢资源.  经常用来模拟网络延迟。

*:在程序中并不是使用Thread.sleep(10)之后,程序才出现问题,而是使用之后,问题更明显。

代码演示:

package synchronizeddemo;class Apple implements Runnable{private int num = 50;@Overridepublic void run() {for (int i = 0; i < 50; i++) {if(num > 0){try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"吃了编号为"+num+"的苹果");num--;}}}}public class SynchronizedBlock {public static void main(String[] args) {Apple apple = new Apple();new Thread(apple,"dodo").start();new Thread(apple,"haha").start();new Thread(apple,"lili").start();}
}

代码运行结果:

代码讲解:①::在线程的run方法上不能使用throws来声明抛出异常,只能在方法中使用try-catch来处理异常,原因是:子类覆盖父类方法的原则,子类不能抛出新的异常,在Runnable接口中的run方法,都没有声明抛出异常。②:为什么50的苹果被吃三次,还出现-1的苹果?原因是,当其中一个线程进入拿到苹果,还没执行num--,其他线程也进来拿苹果导致,出现了多次重复的苹果现象,还有出现-1的问题,所以我们需要引入原子性的概念。

要解决上述多线程并发访问多一个资源的安全性问题:
解决方案:保证打印苹果编号和苹果总数减1操作,必须同步完成.
A线程进入操作的时候,B和C线程只能在外等着,A操作结束,A和B和C才有机会进入代码去执行.
-------------------------------------------------------------------------------------------
方式1:同步代码块
方式2:同步方法
方式3:锁机制(Lock)

方式1:同步代码块

同步代码块:
语法:
synchronized(同步锁)
{
     需要同步操作的代码
}
--------------------------------------------------------------------------------------------
同步锁:
为了保证每个线程都能正常执行原子操作,Java引入了线程同步机制。同步监听对象/同步锁/同步监听器/互斥锁:
对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁,Java程序运行使用任何对象作为同步监听对象,但是一般的,我们把当前并发访问的共同资源作为同步监听对象。
注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着。

代码演示:

package synchronizeddemo;class Apple implements Runnable{private int num = 50;@Overridepublic void run() {for (int i = 0; i < 50; i++) {synchronized (this) {if(num > 0){try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"吃了编号为"+num+"的苹果");num--;}}  }   }
}public class SynchronizedBlock {public static void main(String[] args) {Apple apple = new Apple();new Thread(apple,"dodo").start();new Thread(apple,"haha").start();new Thread(apple,"lili").start();}
}

代码讲解:加入同步锁以后,运行代码,我们将能保证线程同步了,同步代码块的使用,一定要记住将需要同步完成的动作包含进同步代码块里,这是逻辑上需要考虑的事,这里我们的同步锁对象用的是this即apple对象,没有使用num的原因是,因为num这个数字一直在变,用来当同步锁对象是不合理的。

方式2:同步方法

同步方法:使用synchronized修饰方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着.

synchronized public    void  doWork(){
         ///TODO
    }
同步锁是谁:
      对于非static方法,同步锁就是this.  
      对于static方法,我们使用当前方法所在类的字节码对象(Apple2.class).

代码演示:

package synchronizeddemo;class Apple2 implements Runnable{private int num = 50;@Overridepublic void run() {for (int i = 0; i < 50; i++) {eat();} }   //此时方法的同步锁对象就是this,指代当前的apple对象synchronized private void eat(){if(num > 0){try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"吃了编号为"+num+"的苹果");num--;}}
}public class SynchronizedMethod {public static void main(String[] args) {Apple2 apple = new Apple2();new Thread(apple,"dodo").start();new Thread(apple,"haha").start();new Thread(apple,"lili").start();}
}

代码讲解:不要使用synchronized修饰run方法,修饰之后,某一个线程就执行完了所有的功能,好比是多个线程出现串行。解决方案:把需要同步操作的代码定义在一个新的方法中,并且该方法使用synchronized修饰,再在run方法中调用该新的方法即可。

synchronized的优缺点

优点:保证了多线程并发访问时的同步操作,避免线程的安全性问题.
缺点:使用synchronized的方法/代码块的性能比不用要低一些.
建议:尽量减小synchronized的作用域。
-------------------------------------------------------------------------------
使用synchronized 安全保证了,但是性能降低了。
不使用synchronized 此时性能高了,但是安全问题出现了。
-------------------------------------------------------------------------------
面试题:
    1:StringBuilder和StringBuffer的区别.
    2:说说ArrayList和Vector的区别,HashMap和Hashtable的区别.

方法三:同步锁

同步锁(Lock):Lock机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。

API:

代码演示:

package synchronizeddemo;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;class Apple3 implements Runnable {private int num = 500;// 创建锁对象Lock lock = new ReentrantLock();@Overridepublic void run() {for (int i = 0; i < 500; i++) {eat();}}private void eat() {// 一进方法就获取锁lock.lock();try {if (num > 0) {Thread.sleep(100);System.out.println(Thread.currentThread().getName() + "吃了编号为" + num + "的苹果");num--;}} catch (Exception e) {// TODO: handle exception} finally {// 释放锁lock.unlock();}}
}public class LockDemo {public static void main(String[] args) {Apple3 apple = new Apple3();new Thread(apple, "dodo").start();new Thread(apple, "haha").start();new Thread(apple, "lili").start();}
}

单例模式设计

我们先来写个懒汉式(懒加载)提出问题

代码演示:

package singletonpattern;//单例模式-懒汉式
public class ArrayUtil1 {//构造器私有化,防止外界创建对象private ArrayUtil1(){}private static ArrayUtil1 instance = null;//存在线程安全问题public static ArrayUtil1 getInstance(){if(instance==null){instance = new ArrayUtil1();}return instance;}}

代码讲解:上述代码存在线程安全问题,因此我们加入synchronized,修改成下述代码

package singletonpattern;//单例模式-懒汉式
public class ArrayUtil1 {//构造器私有化,防止外界创建对象private ArrayUtil1(){}private static ArrayUtil1 instance = null;//存在线程安全问题synchronized public static ArrayUtil1 getInstance(){if(instance==null){instance = new ArrayUtil1();}return instance;}}

代码讲解:我们知道synchronized加入后,线程安全了--->性能下降了,所以我们应该尽量减小synchronized的作用范围,再次修改成下述代码:

package singletonpattern;//单例模式-懒汉式
public class ArrayUtil1 {//构造器私有化,防止外界创建对象private ArrayUtil1(){}private static ArrayUtil1 instance = null;//同步方法,此时监听对象应该指向ArrayUtil1.classpublic static ArrayUtil1 getInstance(){synchronized (ArrayUtil1.class) {if(instance==null){instance = new ArrayUtil1();}}return instance;}
}

我们能够看到,单例模式-懒汉式,尽管我们加入synchronized,但是还是降低了性能,所以我们单例设计,一般推荐饿汉式,暴力,简单

代码演示:

package singletonpattern;//单例模式-饿汉式
public class ArrayUtil2 {//构造器私有化,防止外界创建对象private ArrayUtil2(){}private static ArrayUtil2 instance = new ArrayUtil2();//同步方法,此时监听对象应该指向ArrayUtil1.classpublic static ArrayUtil2 getInstance(){return instance;}
}

二十五、JAVA多线程(三、线程同步)相关推荐

  1. JavaSE基础二十:Java 多线程(线程基础知识、Java 多线程、Java 实现多线程(继承 Thread 类、实现 Runnable 接口、实现 Callable 接口))

    本章目录 1.基础知识准备 2.Java 多线程概述 3.Java 实现多线程 3.1.继承 Thread 类 如何开启新线程 Thread 类常用方法 多线程中的同步 Thread 类同步方法 多线 ...

  2. Java多线程之线程同步机制(锁,线程池等等)

    Java多线程之线程同步机制 一.概念 1.并发 2.起因 3.缺点 二.三大不安全案例 1.样例一(模拟买票场景) 2.样例二(模拟取钱场景) 3.样例三(模拟集合) 三.同步方法及同步块 1.同步 ...

  3. java多线程采集+线程同步-【多线程数据采集之四】

    前些日子讲解了java数据抓取, 今天就讲解最核心的. java多线程数据抓取. java多线程采集+数据同步+线程同步[多线程数据采集之四] 主要讲解多线程抓取,多线程同步,多线程启动,控制等操作. ...

  4. java多线程之线程同步问题

    1.线程不安全的问题分析 当多线程并发访问同一个资源对象的时候,可能出现线程不安全的问题.但是,我们分析打印的结果,发现没有问题: 为了让问题更明显:     Thread.sleep(10);//当 ...

  5. java多线程:线程同步synchronized(不同步的问题、队列与锁),死锁的产生和解决

    0.不同步的问题 并发的线程不安全问题: 多个线程同时操作同一个对象,如果控制不好,就会产生问题,叫做线程不安全. 我们来看三个比较经典的案例来说明线程不安全的问题. 0.1 订票问题 例如前面说过的 ...

  6. Java 多线程和线程同步总结

    转载:JAVA多线程实现和线程同步总结 1.JAVA多线程实现方式 JAVA多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable ...

  7. Java多线程编程——线程同步与线程安全问题及synchronized关键字

    在多线程环境下,我们常常需要让多个线程同时去操作同一资源.在某些情况下,这种情形会导致程序的运行结果出现差错.专业上的,当多个线程在执行同一段代码的时候,每次的执行结果和单线程执行的结果都是一样的,不 ...

  8. Java多线程之线程同步

    线程同步 线程同步:当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态,实现线程同步的方法有很多. ...

  9. Java多线程(线程同步)

    多线程编程很容易出现"错误情况",这是由系统的线程调度具有一定的随机性造成的,不过即使程序偶然出现问题,那也是由于编程不当引起.使用多个线程访问同一个数据时很容易出现此类状况,因此 ...

  10. iOS面试题(二十五)多线程 --NSOperation和NSOperationQueueNSThread锁机制

    7.多线程 GCD(使用最多) NSOperation/NSOperationQueue(AFNetworking源码中所有网络请求任务都封装到NSOperation,提交到operationQueu ...

最新文章

  1. Fetch API 初步解读
  2. 大专生自学Java到找到工作的过程
  3. 虚拟机linux识别不了u盘_将Arch Linux安装到U盘
  4. C# Serializable(序列化)
  5. 《Jersey用户指南》翻译邀请
  6. PHP 基本数据类型
  7. 【C语言】单链表的所有操作的实现(包括PopBack、PushBack、PopFront、PushFront、Insert)
  8. 团队项目计划BACKLOG
  9. [QNAP crontab 定時執行程式
  10. 寻找是生命中的另一场迷失
  11. 一加9真机曝光:120Hz单孔屏+骁龙888
  12. iPhone 11办理联通5G套餐后,上网速度变快?网友:发广告翻车了?
  13. oracle 安装的提示ntp,oracle rac 安装 PRVG-13606 ntp 同步报错解决过程
  14. SQL查询语句精华 笔记
  15. 谷歌换帅 佩奇将接任CEO[转]
  16. Symbian s40 Java编程实战(一)--开发环境配置
  17. html设置自动居中显示,css+div实现整个html居中最简单方法
  18. 常见的十几种编程语言介绍
  19. 2015 年电赛测评试题——多种波形发生器
  20. Wox and Everything

热门文章

  1. (转)ZooKeeper伪分布式集群安装及使用
  2. 11-Container With Most Water
  3. Ubuntu server配置远程VNC服务
  4. 重新想象 Windows 8 Store Apps (35) - 通知: Toast 详解
  5. 过滤字符串 和 select选择后控件值变更
  6. Java中List,ArrayList、Vector,map,HashTable,HashMap区别用法
  7. mfc中句柄与指针的区别
  8. html支持图片文字的控件_[小工具][爬虫]一键完整备份你的Csdn博客文章(支持Markdown,HTML,文中图片)...
  9. 拓端tecdat|如何用R语言绘制生成正态分布图表
  10. 拓端tecdat|python安娜卡列妮娜词云图制作