在Java多线程中我会重点总结五个如下的技术点:

1、非线程安全是如何出现的

2、synchronized对象监视器为Objec时的使用

3、synchronized对象监视器为Class时的使用

4、关键字volatile的主要作用

今天我先说一说第一个问题,非线程安全是如何出现的。“非线程安全”会在多个线程对同一个对象中的实例变量进行并发访问的时候发生,产生的后果就是“脏读”,也就是读取到的数其实是已经被更改过的了。下面我分别实现“线程安全”和“非线程安全”的两个例子,让大家来体会一下对实例变量更改是怎么发生的。但是多线程的结果会有很多输出结果,希望大家还是亲自敲代码才会理解的更深一些,下面先说一下“线程安全”的例子。如下:

	public static void main(String[] args) {

		PrivateNum num = new PrivateNum();
MyThreadA a = new MyThreadA(num);
MyThreadB b = new MyThreadB(num);
a.start();
b.start(); } public static class PrivateNum { public void addnum(String name) {
//重点是num变量是addnum这个方法的局部变量
int num = 0;
try {
if ("a".equals(name)) {
num = 100;
System.out.println("a ser over!");
Thread.sleep(2000);
} else {
num = 200;
System.out.println("b set over!");
}
System.out.println("线程" + name + "的num=" + num);
} catch (Exception e) {
e.printStackTrace();
} } } public static class MyThreadA extends Thread { private PrivateNum privateNum; public MyThreadA(PrivateNum privateNum) {
super();
this.privateNum = privateNum;
} @Override
public void run() { super.run();
privateNum.addnum("a"); }
} public static class MyThreadB extends Thread { private PrivateNum privateNum; public MyThreadB(PrivateNum privateNum) {
super();
this.privateNum = privateNum;
} @Override
public void run() { super.run();
privateNum.addnum("b"); }
}
     运行结果如下:无论输出顺序是怎样的,但是这段代码执行的结果一定是线程安全的,原因就在于num的这个变量是addnum方法私有的局部变量,PrivateNum的对象访问不到num这个变量,所以就没有多num这个变量改变的这个说,自然就不会出现“非线程安全”的状况。 

  下面在说一个“非线程安全”的例子,如       public static void main(String[] args) {		GetNum num = new GetNum();
		ThreadA a = new ThreadA(num);
a.start();
ThreadB b = new ThreadB(num);
b.start(); } public static class GetNum {
//之所以会出现“非线程安全”的现象,是因为num这个变量是GetNUm类的全局变量
private int num = 0; public void getNum(String name) {
try { if ("a".equals(name)) {
num = 100;
System.out.println("a set over"); 执行到这里的时候,线程a休眠了,此时num=100,接着线程b执行了,执行的结果把num重新赋值为num=200了,线程b执行完了之后,休眠的2000毫秒也过了,
线程a会接着执行,而不是重新执行,所以这个时候的num仍然是200,发生了“非线程安全问题”
Thread.sleep(2000); } else {
num = 200;
System.out.println("b set over");
} System.out.println("线程" + name + "的num=" + num);
} catch (Exception e) {
e.printStackTrace();
}
}
} public static class ThreadA extends Thread { private GetNum num; public ThreadA(GetNum num) {
super();
this.num = num;
} @Override
public void run() { super.run();
num.getNum("a"); }
} public static class ThreadB extends Thread { private GetNum num; public ThreadB(GetNum num) {
super();
this.num = num;
} @Override
public void run() { super.run();
num.getNum("b"); }
}

     运行结果如下:线程a的num=200,出现了“非线程安全”的现象了,出现的原因就是线程a和线程b都对PrivateNum类下的num对象作出改变了,具体的改变过程在程序中已经分析过了。而解决“非线程安全”的问题就是同步,下面的几节会说。

在上述例子中,是在给num赋值的时候出现没有保持线程同步,出现“脏读”,但是在取值的时候也可能出现“脏读”,发生“脏读”是因为在读取实例变量时,这个值已经被修改过了。下面看一个取值发生“脏读”的例子,如下:

       public static void main(String[] args) {

		try {
PublicVar publicVar = new PublicVar();
ThreadA threadA = new ThreadA(publicVar);
threadA.start();
// 睡眠时间影响输出结果
Thread.sleep(200);
publicVar.getValue(); } catch (InterruptedException e) { e.printStackTrace();
}
} public static class PublicVar { public String name = "A";
public String password = "a"; synchronized public void setValues(String name, String password) { try {
this.name = name;
Thread.sleep(5000);
this.password = password;
System.out.println("setValue method thread name="
+ Thread.currentThread().getName());
System.out.println("setValue name=" + name);
System.out.println("setValue password=" + password);
} catch (InterruptedException e) { e.printStackTrace();
}
} public void getValue() { System.out.println("getValue method thread name="
+ Thread.currentThread().getName());
System.out.println("getValue name=" + name);
System.out.println("getValue password=" + password);
}
} public static class ThreadA extends Thread { private PublicVar publicVar; public ThreadA(PublicVar publicVar) {
super();
this.publicVar = publicVar;
} @Override
public void run() {
super.run();
publicVar.setValues("B", "b");
}
}

  运行结果如下:这里边出现“脏读”是因为setValues()方法没有运行完,但是name已经赋值为B,password还没来得赋值就休眠了5000毫秒,所以是main线程先输出的name="B",password="a"。等到5000毫秒到了,子线程又在原来被打断的地方接着运行了,也就是开始给password赋值为b,所以第二次是Thread-0线程输出name="B",password="b"。这里我说过主线程的休眠时间会影响输出结果,如果主线程的休眠时间比子线程的时间长,能够保证子线程执行完,就不会发生“脏读”的情况了。解决脏读就要通过给getValue()方法添加synchronized关键字。

Java多线程之非线程安全的更多相关文章

  1. Java多线程系列--“JUC线程池”03之 线程池原理(二)

    概要 在前面一章"Java多线程系列--“JUC线程池”02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包括:线程池示例参考代 ...

  2. Java多线程并发02——线程的生命周期与常用方法,你都掌握了吗

    在上一章,为大家介绍了线程的一些基础知识,线程的创建与终止.本期将为各位带来线程的生命周期与常用方法.关注我的公众号「Java面典」了解更多 Java 相关知识点. 线程生命周期 一个线程不是被创建了 ...

  3. Java多线程系列--“JUC线程池”06之 Callable和Future

    概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...

  4. Java多线程系列--“JUC线程池”02之 线程池原理(一)

    概要 在上一章"Java多线程系列--“JUC线程池”01之 线程池架构"中,我们了解了线程池的架构.线程池的实现类是ThreadPoolExecutor类.本章,我们通过分析Th ...

  5. Java多线程系列--“JUC线程池”04之 线程池原理(三)

    转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基 ...

  6. Java多线程系列--“JUC线程池”05之 线程池原理(四)

    概要 本章介绍线程池的拒绝策略.内容包括:拒绝策略介绍拒绝策略对比和示例 转载请注明出处:http://www.cnblogs.com/skywang12345/p/3512947.html 拒绝策略 ...

  7. -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中

     本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁  sleep()和wait()方法的区别 为什么wait( ...

  8. 转:java多线程CountDownLatch及线程池ThreadPoolExecutor/ExecutorService使用示例

    java多线程CountDownLatch及线程池ThreadPoolExecutor/ExecutorService使用示例 1.CountDownLatch:一个同步工具类,它允许一个或多个线程一 ...

  9. Java多线程——进程和线程

    Java多线程——进程和线程 摘要:本文主要解释在Java这门编程语言中,什么是进程,什么是线程,以及二者之间的关系. 部分内容来自以下博客: https://www.cnblogs.com/dolp ...

随机推荐

  1. javascript--时钟

    <head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" ...

  2. NETTY 编码器介绍

    1. 背景 1.1. 编解码技术 通常我们也习惯将编码(Encode)称为序列化(serialization),它将对象序列化为字节数组,用于网络传输.数据持久化或者其它用途. 反之,解码(Decod ...

  3. Latex beamer

    使用明体(gbsn)和楷体(gkai)两种字型.以下的编程是一开始的宣告,并自动生成第一张封面投影片. \documentclass[cjk]{beamer}\usepackage{CJKutf8}\ ...

  4. PCB板常用知识简介——沉金板VS镀金板

    一.PCB板表面处理:  抗氧化,喷锡,无铅喷锡,沉金,沉锡,沉银,镀硬金,全板镀金,金手指,镍钯金 OSP: 成本较底,可焊性好,存储条件苛刻,时间短,环保工艺.焊接好 .平整 . 喷锡:喷锡板一般 ...

  5. Windows 系统消息范围和前缀,以及消息大全

    Windows系统定义的消息类别消息标识符前缀 消息分类ABM 应用桌面工具栏消息BM 按钮控件消息CB 组合框控件消息CBEM 扩展组合框控件消息CDM 通用对话框消息DBT 设备消息DL 拖曳列表 ...

  6. CVTE 一面

    在网上做完了测评之后,当天就收到面试的通知了,CVTE效率真高.第二天就去参加面试,面试前紧张了一把,后来去到之后发现只有几个应聘者,很多面试官前面都没人,估计现在中午一点,所以都去吃饭了.我和一个同 ...

  7. 双线服务器和CDN的区别

    双线机房是在国内这样特殊的网络环境下特有的产物,双线机房只能解决网通和电信访问瓶颈的问题,网络传输中的链路问题和其它各ISP互通问题还是没有得到解决. CDN的原理是边缘访问,访问者就近取数据,这样大 ...

  8. 用Java实现 ,冒泡排序与普通排序的区别

     冒泡排序与普通排序的区别  /**    *个人网址: http://www.lipengfei2013.tk    * 功能:冒泡排序与普通排序的区别    */ package www.csdn ...

  9. InitParam与ContextParm的异同

    web.xml里面可以定义两种参数:(1)application范围内的参数,存放在servletcontext中,在web.xml中配置如下: xml 代码 <context-param> ...

  10. PHPRPC for PHP

    14的路 PHPRPC for PHP PHPRPC 是一个轻型的.安全的.跨网际的.跨语言的.跨平台的.跨环境的.跨域的.支持复杂对象传输的.支持引用参数传递的.支持内容输出重定向的.支持分级错误处 ...