Java Tread多线程(2)多线程安全问题
作者 :卿笃军
原文地址:http://blog.csdn.net/qingdujun/article/details/39348093
本文演示,Tread多线程安全问题,以及几种解决多线程安全方式(线程同步)。
1)一个线程不安全的Demo
2)线程同步(synchronized,函数同步,this锁,Class对象锁)
一、小Demo演示引出线程安全问题:
package thread.runable1.qdj;
//1.定义类实现Runnable接口
class RunDemo1 implements Runnable
{
private int x = 5;
//2.覆盖Runnable接口中的run方法
//将线程代码存放在run中
public void run()
{
while (true)
{
if (x > 0)
{
//加入sleep(),注意:sleep()会抛出异常
try {
Thread.sleep(10); //让线程睡眠10ms
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Runnable:"+x);
--x;
}
}
}
}
public class CRunableDemo1 { public static void main(String[] args) {
RunDemo1 r = new RunDemo1();
//3.通过Thread类建立线程对象,并将Runnable接口的子类对象作为參数
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
Thread t4 = new Thread(r);
//4.使用start开启线程
t1.start();
t2.start();
t3.start();
t4.start();
}
}
执行显示结果:
以上打印出了0,-1,-2等数字。发现没?(线程出问题了吧!
!!
)
问题:出问题的解决办法是什么呢?
解释:线程1进入,强制睡眠10ms。此时线程2进入,又强制睡眠10ms;线程3进入又强制睡眠10ms。线程4进入再强制睡眠10ms;
注意,以上4个线程睡眠时都已经进入了if语句,进入的时候x>0还是成立的;
好了。线程1醒来,開始打印打印5,4,3,2,这时候--x还没运行。线程2就醒来了。抢去了cpu的运行权.....................
二、线程同步
问题:对于上面的问题,我们是不是能够採取一个这种措施?当线程1运行run代码段的时候。我们不让其它的线程来运行,直到线程1运行完,其它的线程才干够进入。
解决方式:好在Java里面本来就有这种函数。将代码段包裹起来。就能够达到上面问题描写叙述的效果。
函数名:synchronized,须要一个參数。随便传个对象就ok了(详细參数差别分析。请往下拉见附录1)。
1)一个简单的解决方式:
package thread.runable1.qdj;
//1.定义类实现Runnable接口
class RunDemo1 implements Runnable
{
private int x = 5;
Object obj = new Object();
//2.覆盖Runnable接口中的run方法
//将线程代码存放在run中
public void run()
{
while (true)
{
synchronized (obj)
{
if (x > 0)
{
//加入sleep()。注意:sleep()会抛出异常
try {
Thread.sleep(10); //让线程睡眠10ms
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Runnable:"+x);
--x;
}
}
}
}
}
public class CRunableDemo1 { public static void main(String[] args) {
RunDemo1 r = new RunDemo1();
//3.通过Thread类建立线程对象,并将Runnable接口的子类对象作为參数
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
Thread t4 = new Thread(r);
//4.使用start开启线程
t1.start();
t2.start();
t3.start();
t4.start();
}
}
执行结果显示:
2)换一种更简单的解决方法:函数同步(将synchronized直接加入在函数前面)
说明:有没有发现,函数就是对多个语句的打包?可是,函数仅仅是打包。而没有像上面的synchronized一样实现同步。可是有一种方法能够达到这个效果。看以下的小Demo就明确了。
package thread.runable1.qdj;
//同步函数
class SynFunc
{
private int x = 5;
public synchronized void aFunc()
{
while (true)
{
if (x > 0)
{
//加入sleep(),注意:sleep()会抛出异常
try {
Thread.sleep(10); //让线程睡眠10ms
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Runnable:"+x);
--x;
}
}
}
}
//1.定义类实现Runnable接口
class RunDemo1 implements Runnable
{
private SynFunc syn = new SynFunc();
//2.覆盖Runnable接口中的run方法
//将线程代码存放在run中
public void run()
{
syn.aFunc();
}
}
public class CRunableDemo1 { public static void main(String[] args) {
RunDemo1 r = new RunDemo1();
//3.通过Thread类建立线程对象,并将Runnable接口的子类对象作为參数
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
Thread t3 = new Thread(r);
Thread t4 = new Thread(r);
//4.使用start开启线程
t1.start();
t2.start();
t3.start();
t4.start();
}
}
执行结果显示:(同上)
附录1:对于函数锁中synchronized中的參数的解释分析(我们先看一下以下这个小Demo)
package thread.runable1.qdj; //1.定义类实现Runnable接口
class RunDemo1 implements Runnable
{
private int x = 100;
Object obj = new Object();
boolean flag = true;
//2.覆盖Runnable接口中的run方法
//将线程代码存放在run中
public void run()
{
if (flag)
{
while (true)
{
synchronized(obj)
{
if (x > 0)
{
//加入sleep(),注意:sleep()会抛出异常
try {
Thread.sleep(10); //让线程睡眠10ms
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Runnable:"+x);
--x;
}
}
}
}
else
{
while (true)
aFunc();
}
}
public synchronized void aFunc()
{
while (true)
{
if (x > 0)
{
//加入sleep(),注意:sleep()会抛出异常
try {
Thread.sleep(10); //让线程睡眠10ms
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Runnable:"+x);
--x;
}
}
}
}
public class CRunableDemo1 { public static void main(String[] args) {
RunDemo1 r = new RunDemo1();
//3.通过Thread类建立线程对象。并将Runnable接口的子类对象作为參数
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
//4.使用start开启线程
t1.start();
//加入sleep()。注意:sleep()会抛出异常
try {
Thread.sleep(10); //让线程睡眠10ms
} catch (Exception e) {
e.printStackTrace();
}
r.flag = false;
t2.start();
}
}
执行显示结果:
郁闷吧,又出现线程安全问题了,但是我们分明都加了锁的呀?这是什么原因导致的呢?事实上,问题就出在了。synchronized里面的參数上。
函数同步锁定的是this,而上面的小Demo里面,一个是函数同步锁(this),另外一个是obj锁,发现没?解决方式:将obj锁改为this锁就没问题了。
解决后的小Demo:
package thread.runable1.qdj; //1.定义类实现Runnable接口
class RunDemo1 implements Runnable
{
private int x = 100;
Object obj = new Object();
boolean flag = true;
//2.覆盖Runnable接口中的run方法
//将线程代码存放在run中
public void run()
{
if (flag)
{
while (true)
{
synchronized(this)
{
if (x > 0)
{
//加入sleep(),注意:sleep()会抛出异常
try {
Thread.sleep(10); //让线程睡眠10ms
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Runnable:"+x);
--x;
}
}
}
}
else
{
while (true)
aFunc();
}
}
public synchronized void aFunc()
{
while (true)
{
if (x > 0)
{
//加入sleep(),注意:sleep()会抛出异常
try {
Thread.sleep(10); //让线程睡眠10ms
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Runnable:"+x);
--x;
}
}
}
}
public class CRunableDemo1 { public static void main(String[] args) {
RunDemo1 r = new RunDemo1();
//3.通过Thread类建立线程对象,并将Runnable接口的子类对象作为參数
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
//4.使用start开启线程
t1.start();
//加入sleep()。注意:sleep()会抛出异常
try {
Thread.sleep(10); //让线程睡眠10ms
} catch (Exception e) {
e.printStackTrace();
}
r.flag = false;
t2.start();
}
}
执行显示结果:
附录2:提出另外一个问题,既然函数同步锁定的是this,那么例如以下的这个函数要怎样解释?静态函数没有this,怎样锁定?
public static synchronized void aFunc()
{
while (true)
{
if (x > 0)
{
//加入sleep(),注意:sleep()会抛出异常
try {
Thread.sleep(10); //让线程睡眠10ms
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Runnable:"+x);
--x;
}
}
}
解决方式:静态函数,是类函数由类调用。
事实上这里static函数的锁是Class对象。以下注意看例如以下小Demo中synchronized的參数。
解决后的小Demo:
package thread.runable1.qdj; //1.定义类实现Runnable接口
class RunDemo1 implements Runnable
{
private static int x = 100;
boolean flag = true;
//2.覆盖Runnable接口中的run方法
//将线程代码存放在run中
public void run()
{
if (flag)
{
while (true)
{
synchronized(RunDemo1.class)
{
if (x > 0)
{
//加入sleep(),注意:sleep()会抛出异常
try {
Thread.sleep(10); //让线程睡眠10ms
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Runnable:"+x);
--x;
}
}
}
}
else
{
while (true)
aFunc();
}
}
public static synchronized void aFunc()
{
while (true)
{
if (x > 0)
{
//加入sleep(),注意:sleep()会抛出异常
try {
Thread.sleep(10); //让线程睡眠10ms
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("Runnable:"+x);
--x;
}
}
}
}
public class CRunableDemo1 { public static void main(String[] args) {
RunDemo1 r = new RunDemo1();
//3.通过Thread类建立线程对象。并将Runnable接口的子类对象作为參数
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
//4.使用start开启线程
t1.start();
//加入sleep(),注意:sleep()会抛出异常
try {
Thread.sleep(10); //让线程睡眠10ms
} catch (Exception e) {
e.printStackTrace();
}
r.flag = false;
t2.start();
}
}
执行显示结果:(同上)
參考文献:Java视频 毕向东 主讲
原文地址:http://blog.csdn.net/qingdujun/article/details/39348093
Java Tread多线程(2)多线程安全问题的更多相关文章
- Java Tread多线程(1)实现Runnable接口
作者 : 卿笃军 原文地址:http://blog.csdn.net/qingdujun/article/details/39347245 本文演示,Tread多线程实现Runnable接口,以及简单 ...
- 编写Java程序,实现多线程操作同一个实例变量的操作会引发多线程并发的安全问题。
查看本章节 查看作业目录 需求说明: 多线程操作同一个实例变量的操作会引发多线程并发的安全问题.现有 3 个线程代表 3 只猴子,对类中的一个整型变量 count(代表花的总数,共 20 朵花)进行操 ...
- Java 并发性和多线程
一.介绍 在过去单 CPU 时代,单任务在一个时间点只能执行单一程序.之后发展到多任务阶段,计算机能在同一时间点并行执行多任务或多进程.虽然并不是真正意义上的“同一时间点”,而是多个任务或进程共享一个 ...
- Java的三魂七魄 —— 高级多线程
目录 Java的三魂七魄 -- 高级多线程 一.多线程的创建 二.线程安全问题 三.线程通信问题 四.更多实例 1.用线程同步的方法解决单例模式的线程安全问题 2.银行存钱问题(线程安全问题) 3.生 ...
- Java高并发与多线程(四)-----锁
今天,我们开始Java高并发与多线程的第四篇,锁. 之前的三篇,基本上都是在讲一些概念性和基础性的东西,东西有点零碎,但是像文科科目一样,记住就好了. 但是本篇是高并发里面真正的基石,需要大量的理解和 ...
- Java学习手记2——多线程
一.线程的概念 CPU执行程序,就好比一个人在干事情一样,同一个时间你只能做一件事情,但是这样的效率实在是太低了,在你用电脑的时候,听歌就不能浏览网页,看电影就不能下载视频,你想想是不是很蛋疼. 所以 ...
- java 并发性和多线程 -- 读感 (一 线程的基本概念部分)
1.目录略览 线程的基本概念:介绍线程的优点,代价,并发编程的模型.如何创建运行java 线程. 线程间通讯的机制:竞态条件与临界区,线程安全和共享资源与不可变性.java内存模型 ...
- Java 并发和多线程(一) Java并发性和多线程介绍[转]
作者:Jakob Jenkov 译者:Simon-SZ 校对:方腾飞 http://tutorials.jenkov.com/java-concurrency/index.html 在过去单CPU时 ...
- Java核心知识点学习----多线程中的阻塞队列,ArrayBlockingQueue介绍
1.什么是阻塞队列? 所谓队列,遵循的是先进先出原则(FIFO),阻塞队列,即是数据共享时,A在写数据时,B想读同一数据,那么就将发生阻塞了. 看一下线程的四种状态,首先是新创建一个线程,然后,通过s ...
- Java多线程——<八>多线程其他概念
一.概述 到第八节,就把多线程基本的概念都说完了.把前面的所有文章加连接在此: Java多线程——<一>概述.定义任务 Java多线程——<二>将任务交给线程,线程声明及启动 ...
随机推荐
- 关于jquery点击之后,标签的hover失效这个问题
做一个点击切换的效果,加在a标签上,jquery的click加上css中的hover 点击之后,css的hover效果就没有了,后来知道是click的权值比外联的css大 当点击之后,css代码就被覆 ...
- CSS的导入方式:link与import方式的区别
在前端开发中,加载CSS样式文件有两种方式:link方式与import方式,它们之间的区别主要有以下几点: 1.兼容性不一样 link是一个HTML标签,所以它不存在兼容性问题,而import方式则具 ...
- 微信小程序,前端大梦想(二)
微信小程序之数据缓存实例-备忘录 数据缓存在移动端的使用是非常重要的,既可以减少用户的流量支出又可以提高程序的访问速度和用户体验.每个微信小程序都可以有自己的本地缓存,可以通过 wx.setS ...
- 项目报错:Cannot find class file for javax/servlet/ServletException
两种解决方法: 1. 假设是Maven项目,加入servlet-api依赖包: <dependency> <groupId>javax.servlet</groupId& ...
- java byte中存大于0x7E的十六进制数
在做一个Android app和arm板子交互的程序中,遇到一个问题,Java byte中无法直接存储大于0x7E的十六进制,但是C语言却可以. 出现这个状况的原因是:Java中是byte存储的是有符 ...
- resource-color 的引用
1.在xml文件中引用 android:textColor="@color/tv_top_title_color" 2.在代码中引用 1)在color.xml中定义 <?xm ...
- [LuoguU41039]PION后缀自动机 树链剖分+动态开点线段树
链接 刚开始看出题人题解都吓蒙掉了,还以为是什么难题,结果就一板子题 思路:对每一个文件名开一棵线段树,然后树剖即可 #include<bits/stdc++.h> #define REP ...
- 【2017 Multi-University Training Contest - Team 6】Inversion
[链接]h在这里写链接 [题意] 给出一个序列,求2~n每一个数,下标不是这个数倍数的最大值是什么? [题解] 把a数组从大到小排序. 每个位置i,逆序枚举b数组,找到第一个对应下标不是i的倍数的,直 ...
- 为什么golang的开发效率高(编译型的强类型语言、工程角度高、在开发上的高效率主要来自于后发优势,编译快、避免趁编译时间找产品妹妹搭讪,既是强类型语言又有gc,只要通过编译,非业务毛病就很少了)
作者:阿猫链接:https://www.zhihu.com/question/21098952/answer/21813840来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出 ...
- A glance on VDBI
Just like other thing in data transfter, a resource should have themselves description. And the reso ...