在上节中已经说过了“非线程安全”是如何出现的,链接如下:http://www.cnblogs.com/chentong/p/5650137.html,那么怎么解决“非线程安全”问题呢,只需要在两个线程都需要同时访问的方法前面加上synchronized关键字即可,我只贴出需要修改的这个方法的代码,具体修改如下:

     public static class GetNum {

		private int num = 0;
//两个线程访问同一个对象中的同步方法时一定是线程安全的
synchronized public void getNum(String name) {
try { if ("a".equals(name)) {
num = 100;
System.out.println("a set over");
Thread.sleep(2000); } else {
num = 200;
System.out.println("b set over");
} System.out.println("线程" + name + "的num=" + num);
} catch (Exception e) {
e.printStackTrace();
}
}
}

   结果如下:无论哪个线程先执行,一定不会出现交叉执行的情况,因为synchronized取得是对象锁,在main方法里只有一个GetNum对象num(不知道main方法如何调用的,可以看上一篇博客,链接上边已经附上了),因此只有一把锁,所以只有一个线程把run方法执行完了,才会释放锁,另一个线程才会执行。因此在只有一个对象锁的情况下,synchronized声明的方法一定是排队运行的。

上面的这种情况是一个对象一把锁,下面说一下多个对象多个锁是怎么执行的。例子代码如下:

        public static void main(String[] args) {

		GetNum num1 = new GetNum();
GetNum num2 = new GetNum();

ThreadA a = new ThreadA(num1);
a.start();
ThreadB b = new ThreadB(num2);
b.start();
} public static class GetNum {
private int num = 0;
synchronized public void getNum(String name) {
try {
if ("a".equals(name)) {
num = 100;
System.out.println("a set 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 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");
}
}

     运行结果如下:大家会发现打印的结果是交叉的,原因是由于synchronized取得是对象锁,而在这里两个线程传入的又不是同一个GetNum对象(num1和num2),所以synchronized取得是两个不一样的锁,大家互相不影响。只能哪个线程抢上CPU哪个线程就执行。

上面的两个例子访问的都是synchronized关键字声明的方法,那么如果有其它的普通方法被调用的时候,会怎样执行呢,下面我写了一个例子来演示这种情况,如下:

	public static void main(String[] args) {

		MyObject object = new MyObject();
ThreadA a = new ThreadA(object);
a.setName("A");
ThreadB b = new ThreadB(object);
b.setName("B");
a.start();
b.start();
} public static class MyObject {
//synchronized声明的方法
synchronized public void meathodA() {
try {
System.out.println("begin methodA threeName="
+ Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("meathodA endTime="
+ System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//普通方法
public void meathodB() {
try {
System.out.println("begin methodB threeName="
+ Thread.currentThread().getName());
System.out.println("meathodB beginTime="
+ System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static class ThreadA extends Thread { private MyObject object;
public ThreadA(MyObject object) {
super();
this.object = object;
}
@Override
public void run() {
super.run();
//线程a调用的synchronized声明的方法
object.meathodA();
}
}
public static class ThreadB extends Thread { private MyObject object;
public ThreadB(MyObject object) {
super();
this.object = object;
}
@Override
public void run() {
super.run();
//线程b调用的普通方法
object.meathodB();
}
}

  输出结果如下:可见输出结果是交叉的,说明线程B可以以异步的形式调用MyObject类中的非synchronized类型的方法

上面说过了synchronized方法和普通方法的调用,那么synchronized方法/块的内部调用其他的synchronized方法/块的时候,是怎么的情况呢,我写了一个小例子,如下:

       public static void main(String[] args) {

		MyThread t = new MyThread();
t.start();
} public static class Service { synchronized public void service1() {
System.out.println("service1");
service2();
} synchronized public void service2() {
System.out.println("service2");
service3();
} synchronized public void service3() {
System.out.println("service3");
}
} public static class MyThread extends Thread { @Override
public void run() {
// TODO Auto-generated method stub
super.run();
Service s = new Service();
s.service1();
}
}

   运行结果如下:从运行结果可以引出一个概念“可重入锁”:自己可以再次获得自己的内部锁。说明一个线程如果获得了某个对象锁,此时这个对象锁还没有释放,当想要再次获得这个对象锁的时候还是可以获取的。

synchronized“可重入锁“还有一个特点,是什么呢?我写了一个例子,可以看一下,如下:

       public static void main(String[] args) {

		MyThread t = new MyThread();
t.start();
} public static class MyThread extends Thread { @Override
public void run() {
super.run();
Sub sub = new Sub();
sub.openrateSubMenthod();
}
} }
//Sub和Main两个外部类
class Sub extends Main { synchronized public void openrateSubMenthod() {
//用的是父类定义的i=10
while (i > 0) { i--;
try {
System.out.println("sub print i=" + i);
Thread.sleep(100);
//调用父类的openrateMainMenthod()方法
this.openrateMainMenthod();
} catch (Exception e) { e.printStackTrace();
}
}
}
} class Main { public int i = 10; synchronized public void openrateMainMenthod() { i--;
try {
System.out.println("main print i=" + i);
Thread.sleep(100);
} catch (InterruptedException e) { e.printStackTrace();
}
}
  运行结果如下:在执行子线程MyThread的run方法中,调用了子类Sub重写父类Main中的operateMainMenthod()方法,而在子类Sub的operateMainMenthod()方法中又调用了父类的synchronized方法,从执行结果可以看出,“可重入锁”支持在父子类继承的环境中。

上面说“可重入锁”支持父子类继承的环境中,但是同步是不具有继承性的,为了证明这一点,我写了一个小例子,如下:

 public class Test09 {

	public static void main(String[] args) {

		Child c = new Child();
MyThreasA a = new MyThreasA(c);
a.setName("A");
MyThreasB b = new MyThreasB(c);
b.setName("B");
a.start();
b.start();
} public static class MyThreasA extends Thread { private Child c; public MyThreasA(Child c) {
super();
this.c = c;
} @Override
public void run() {
// TODO Auto-generated method stub
super.run();
c.serviceMethod();
}
} public static class MyThreasB extends Thread { private Child c; public MyThreasB(Child c) {
super();
this.c = c;
} @Override
public void run() {
// TODO Auto-generated method stub
super.run();
c.serviceMethod();
}
}
}

//Child和Parent两个外部类,子类与父类
class Child extends Parent { @Override
public void serviceMethod() { try {
System.out.println("int child 下一步 sleep begin threadName="
+ Thread.currentThread().getName() + " time="
+ System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("int child 下一步 sleep end threadName="
+ Thread.currentThread().getName() + " time="
+ System.currentTimeMillis()); } catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
} class Parent { synchronized public void serviceMethod() { try {
System.out.println("int parent 下一步 sleep begin threadName="
+ Thread.currentThread().getName() + " time="
+ System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("int parent 下一步 sleep end threadName="
+ Thread.currentThread().getName() + " time="
+ System.currentTimeMillis());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
}

运行结果如下:从运行结果可以看出,线程A和线程B交叉出现的,没有同步,说明子类的serviceMethod()方法并没有synchronized的特点,如果有synchronized的特点执行结果应该是同步的,也就是说一个线程执行完之后释放锁之后另一个线程才能执行,如果想让子类的方法也拥有synchronized的特点需要自己在这个方法前面收到添加synchronized关键字。

在Child类的serviceMethod()方法前添加完synchronized字段之后,执行结果如下:

以上说的都是synchronized对象监视器为Object时的使用,也就是说这里的锁是对象锁,但是在这里关键字synchronized都是用来声明方法的,这样写的弊端和如何改进我会在下节里说到的。

 

  

 

Java多线程之synchronized(一)的更多相关文章

  1. (二)java多线程之synchronized

    本人邮箱: kco1989@qq.com 欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco github: https://github.com/kco198 ...

  2. JAVA多线程之Synchronized关键字--对象锁的特点

    一,介绍 本文介绍JAVA多线程中的synchronized关键字作为对象锁的一些知识点. 所谓对象锁,就是就是synchronized 给某个对象 加锁.关于 对象锁 可参考:这篇文章 二,分析 s ...

  3. JAVA多线程之Synchronized、wait、notify实例讲解

    一.Synchronized synchronized中文解释是同步,那么什么是同步呢,解释就是程序中用于控制不同线程间操作发生相对顺序的机制,通俗来讲就是2点,第一要有多线程,第二当多个线程同时竞争 ...

  4. Java多线程之synchronized(四)

    前面几章都是在说synchronized用于对象锁,无论是修饰方法也好修饰代码块也好,然而关键字synchronized还可以应用到static静态方法上,如果这样写,那就是对当前的*.java文件所 ...

  5. Java多线程之synchronized(三)

    在多线程访问同一个对象中的不同的synchronized方法或synchronized代码块的前提下,也就是“对象监控器”为同一个对象的时候,也就是synchronized的锁为同一把锁的时候,调用的 ...

  6. Java多线程之synchronized及其优化

    Synchronized和同步阻塞synchronized是jvm提供的同步和锁机制,与之对应的是jdk层面的J.U.C提供的基于AbstractQueuedSynchronizer的并发组件.syn ...

  7. JAVA多线程之synchronized和volatile实例讲解

    在多线程中,提到线程安全.线程同步,我们经常会想到两个关键字:volatile和synchronized,那么这两者有什么区别呢? 1. volatile修饰的变量具有可见性 volatile是变量修 ...

  8. java 多线程之synchronized wait/notify解决买票问题

    一.Java线程具有五中基本状态 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread(); 就绪状态(Runnable):当调用线程对象的st ...

  9. Java多线程之synchronized和volatile

    概述 用Java来开发多线程程序变得越来越常见,虽然Java提供了并发包来简化多线程程序的编写,但是我们有必要深入研究一下,才能更好的掌握这块知识. 本文主要对Java提供的底层原语synchroni ...

  10. Java多线程之synchronized详解

    目录 synchronized简介 同步的原理 对象头与锁的实现 锁的优化与升级 Monitor Record 锁的对比 synchronized简介 synchronized关键字,一般称之为&qu ...

随机推荐

  1. 64bits Python2.7.5安装numpy包

    由于数值分析需要numpy计算包,我找了很多numpy-cp27的下载地址,下了最新版的.whl文件,但总是安装不成功,后来找到一个.exe文件 直接下载安装即可使用:下面是网址链接http://do ...

  2. DNS解析

    大家好,今天51开源给大家介绍一个在配置文件,那就是/etc/resolv.conf.很多网友对此文件的用处不太了解.其实并不复杂,它是DNS客户机配置文件,用于设置DNS服务器的IP地址及DNS域名 ...

  3. VC中使用GetModuleFileName获取应用程序路径

      .\\与API函数GetModuleFileName获取应用程序目录有何不一样? 采用.\\也能获得应用程序目录,采用GetModuleFileName也能获得,二者有何不同? 一样!一个是相对路 ...

  4. Mirantis Certification summary

    preface Mirantis Certification (MCA100 )summary roughly question types handy remain by Ruiy!

  5. 追踪神秘的成都Uber:月入2万元是现实还是传说

    4月6日,一个视频在网上疯转——在上海,明星佟大为驾驶着售价近100万元的特斯拉电动汽车,作为一名Uber的司机满市转悠着拉客. Uber——优步,如果你不知道这个词,那就OUT了.就是这样的一款软件 ...

  6. pcre函数具体解释

    PCRE是一个NFA正则引擎,不然不能提供全然与Perl一致的正则语法功能.但它同一时候也实现了DFA,仅仅是满足数学意义上的正则. PCRE提供了19个接口函数,为了简介,使用PCRE内带的測试程序 ...

  7. DICOM医学图像处理:DCMTK在VS2012中的配置

    背景: 近期因为项目须要,将原本的开发IDE环境由VS2008升级到了VS2012.本以为编译完毕后的DCMTK开源库能够直接从VS2008移植到VS2012.可是通过项目属性加入完包括文件夹和依赖库 ...

  8. iOS面试题05-父子控制器、内存管理

    内存管理.父子控制器面试题 1.建立父子关系控制器有什么用 回答:1>监听屏幕选中 2>如果想拿到你当前的很小的一个控制器所在的导航控制器必须要跟外面比较大的控制器建立父子关系,才能一层一 ...

  9. Windows 配置JAVA的环境变量

    Java是由Sun公司开发的一种应用于分布式网络环境的程序设计语言,Java语言拥有跨平台的特性,它编译的程序能够运行在多种操作系统平台上,可以实现“一次编写,到处运行”的强大功能. 工具/原料 JD ...

  10. 【转载】VC++中的图像类型转换--使用开源CxImage类库

    一.CxImage类库简介 这只是翻译了CxImage开源项目主页上的部分简介及简单使用. CxImage类库是一个优秀的图像操作类库.它可以快捷地存取.显示.转换各种图像.有的读者可能说,有那么多优 ...