synchronized

  Java语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码。当两个并发线程访问同一个对象object中的这个加锁同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。然而,当一个线程访问object的一个加锁代码块时,另一个线程仍然可以访问该object中的非加锁代码块。

                                                        ——以上来源百度百科

一、方法内的变量为线程安全

“非线程安全”的问题存在于“实例变量”中,如果是方法内部的私有变量,则不会存在“非线程安全”问题,所得结果就是“线程安全”的了。

MyService类

 package com.mythread.www.day8.testSyn.ep1;

 public class MyService {
public void add(String name) {
try {
int num = 0;
if (name.equals("a")) {
num = 100;
System.out.println("a is over");
Thread.sleep(1000);
} else {
num = 200;
System.out.println("b is over");
}
System.out.println(name + " num = " + num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

线程类A

 package com.mythread.www.day8.testSyn.ep1;

 public class ThreadA extends Thread {
private MyService service; public ThreadA(MyService service) {
super();
this.service = service;
} @Override
public void run() {
service.add("a");
}
}

线程类B

 package com.mythread.www.day8.testSyn.ep1;

 public class ThreadB extends Thread {
private MyService service; public ThreadB(MyService service) {
super();
this.service = service;
} @Override
public void run() {
service.add("b");
}
}

运行类

 package com.mythread.www.day8.testSyn.ep1;

 public class Run {
public static void main(String[] args) {
MyService service = new MyService();
ThreadA threadA = new ThreadA(service);
threadA.start();
ThreadB threadB = new ThreadB(service);
threadB.start();
}
}

结果

 a is over
b is over
b num = 200
a num = 100

从运行结果来看,方法中的变量不存在非线程安全的问题,永远都是线程安全的,这事方法内部的变量是私有的特性造成的。

二、实例变量非线程安全

MyService类

 package com.mythread.www.day8.testSyn.ep1;

 public class MyService {
private int num = 0;
public void add(String name) {
try { if (name.equals("a")) {
num = 100;
System.out.println("a is over");
Thread.sleep(1000);
} else {
num = 200;
System.out.println("b is over");
}
System.out.println(name + " num = " + num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

线程类和运行类同上,运行结果

 a is over
b is over
b num = 200
a num = 200

产生这个结果的原因是两个线程同事访问同一个没有同步的方法,如果两个对象同时操作对象中的实例变量,可能会造成非线程安全的问题

最简单的解决方案是在方法的前面加个synchronized同步锁

MyService类

 package com.mythread.www.day8.testSyn.ep1;

 public class MyService {
private int num = 0;
synchronized public void add(String name) {
try { if (name.equals("a")) {
num = 100;
System.out.println("a is over");
Thread.sleep(1000);
} else {
num = 200;
System.out.println("b is over");
}
System.out.println(name + " num = " + num);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

线程类和运行类同上,运行结果

 a is over
a num = 100
b is over
b num = 200

在两个线程访问同一个对象中的同步方法时一定是线程安全的,上面的代码由于时同步访问,所以先打印出a,然后在打印出b

改一下运行类,其他类同上

 package com.mythread.www.day8.testSyn.ep1;

 public class Run {
public static void main(String[] args) {
MyService serviceA = new MyService();
MyService serviceB = new MyService();
ThreadA threadA = new ThreadA(serviceA);
threadA.start();
ThreadB threadB = new ThreadB(serviceB);
threadB.start();
}
}

结果

 a is over
b is over
b num = 200
a num = 100

这是两个线程分别访问同一个类的两个不同实例的相同名称的同步方法,效果却是以异步的形式来执行的。

因为创建了两个业务对象,在系统中产生了两个锁,所以运行结果是异步的。

关键字synchronized所取得的锁都是对象锁,而不是把一段代码或者方法当作锁。

My Service类

 package com.mythread.www.day8.testSyn.ep1;

 public class MyService {
synchronized public void methodA() {
try {
System.out.println("begin methodA threadName = " + Thread.currentThread().getName());
Thread.sleep(3000);
System.out.println("end methodA time = " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
} public void methodB() {
try {
System.out.println("begin methodB threadName = " + Thread.currentThread().getName());
Thread.sleep(3000);
System.out.println("end methodB time = " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

线程类A

 package com.mythread.www.day8.testSyn.ep1;

 public class ThreadA extends Thread {
private MyService service; public ThreadA(MyService service) {
super();
this.service = service;
} @Override
public void run() {
service.methodA();
}
}

线程类B

 package com.mythread.www.day8.testSyn.ep1;

 public class ThreadB extends Thread {
private MyService service; public ThreadB(MyService service) {
super();
this.service = service;
} @Override
public void run() {
service.methodB();
}
}

运行类

 package com.mythread.www.day8.testSyn.ep1;

 public class Run {
public static void main(String[] args) {
MyService service = new MyService();
ThreadA threadA = new ThreadA(service);
threadA.start();
ThreadB threadB = new ThreadB(service);
threadB.start();
}
}

结果

 begin methodA threadName = Thread-1
begin methodB threadName = Thread-2
end methodB time = 1458400534384
end methodA time = 1458400534384

在My Service的methodB前面也加上关键字synchronized

My Service类

 package com.mythread.www.day8.testSyn.ep1;

 public class MyService {
synchronized public void methodA() {
try {
System.out.println("begin methodA threadName = " + Thread.currentThread().getName());
Thread.sleep(3000);
System.out.println("end methodA time = " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
} synchronized public void methodB() {
try {
System.out.println("begin methodB threadName = " + Thread.currentThread().getName());
Thread.sleep(3000);
System.out.println("end methodB time = " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

结果

 begin methodA threadName = Thread-1
end methodA time = 1458400619034
begin methodB threadName = Thread-2
end methodB time = 1458400622035

对比上面两次代码的运行结果。

在第一次运行时,当A线程先持有My Service的同步锁时,B线程可以已异步的方式去调用My Service对象中的非synchronized方法。

在第二次运行时,当A线程先持有My Service的同步锁时,当B想调用My Service对象中的synchronized,则需要先等A释放对象锁。

所以,synchronized锁住的时对象,而不是其他的一些东西。

三、synchronized锁重入

当一个线程获得一个对象锁后,再次请求此对象锁时是可以再次获得此对象锁的

My Service类

 package com.mythread.www.day8.testSyn.ep1;

 public class MyService {
synchronized public void methodA() {
System.out.println("methodA");
methodB();
} synchronized public void methodB() {
System.out.println("methodB");
methodC();
} synchronized public void methodC() {
System.out.println("methodC");
}
}

线程类

 package com.mythread.www.day8.testSyn.ep1;

 public class MyThread extends Thread {
@Override
public void run() {
MyService myService = new MyService();
myService.methodA();
}
}

运行类

 package com.mythread.www.day8.testSyn.ep1;

 public class Run {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}

结果

 methodA
methodB
methodC

自己还可以重新获得自己的内部锁,如果不可以的话,上面的这个Demo则会造成死锁现象

Main类

 package com.weishiyao.learn.day4.testThread;

 public class Main {
public int i = 10;
synchronized public void operateIMainMethod() {
try {
i--;
System.out.println("main print i=" + i);
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
}
}

Sub类

 package com.weishiyao.learn.day4.testThread;

 public class Sub extends Main{
synchronized public void operateISubMethod() {
try {
while (i > 0) {
i--;
System.out.println("sub print i=" + i);
Thread.sleep(100);
this.operateIMainMethod();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

线程类

 package com.weishiyao.learn.day4.testThread;

 public class MyThread  extends Thread{
@Override
public void run() {
Sub sub = new Sub();
sub.operateISubMethod();
}
}

运行类

 package com.weishiyao.learn.day4.testThread;

 public class Run {
public static void main(String[] args) {
MyThread t = new MyThread();
t.run();
}
}

结果

 sub print i=9
main print i=8
sub print i=7
main print i=6
sub print i=5
main print i=4
sub print i=3
main print i=2
sub print i=1
main print i=0

当存在父子类继承关系时,子类完全可以通过父类“可重入锁”调用父类的同步方法

java多线程(二)——锁机制synchronized(同步方法)的更多相关文章

  1. java多线程(三)——锁机制synchronized(同步语句块)

    用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法之行一个长时间的任务,那么B线程必须等待比较长的时间,在这样的情况下可以使用synchronized同步语句快来解 ...

  2. JAVA多线程与锁机制

    JAVA多线程与锁机制 1 关于Synchronized和lock synchronized是Java的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码 ...

  3. Java多线程的同步机制(synchronized)

    一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在 java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个 ...

  4. 【转载】Java中的锁机制 synchronized & 偏向锁 & 轻量级锁 & 重量级锁 & 各自优缺点及场景 & AtomicReference

    参考文章: http://blog.csdn.net/chen77716/article/details/6618779 目前在Java中存在两种锁机制:synchronized和Lock,Lock接 ...

  5. java的锁机制——synchronized

    一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个线 ...

  6. Java 多线程:锁(二)

    Java 多线程:锁(二) 作者:Grey 原文地址: 博客园:Java 多线程:锁(二) CSDN:Java 多线程:锁(二) AtomicLong VS LongAddr VS Synchroni ...

  7. 深入浅出Java并发包—锁机制(二)

    接上文<深入浅出Java并发包—锁机制(一)  >  2.Sync.FairSync.TryAcquire(公平锁) 我们直接来看代码 protected final boolean tr ...

  8. JAVA中关于锁机制

    本文转自 http://blog.csdn.net/yangzhijun_cau/article/details/6432216 一段synchronized的代码被一个线程执行之前,他要先拿到执行这 ...

  9. 深入浅出Java并发包—锁机制(三)

    接上文<深入浅出Java并发包—锁机制(二)>  由锁衍生的下一个对象是条件变量,这个对象的存在很大程度上是为了解决Object.wait/notify/notifyAll难以使用的问题. ...

随机推荐

  1. HTML5新特性之移动设备API

    为了更好地为移动设备服务,HTML5推出了一系列针对移动设备的API. 1.Geolocation API Geolocation接口用于获取用户的地理位置.它使用的方法基于GPS或者其他机制(比如I ...

  2. ASP.NET MVC 的自定义模型属性别名绑定

    最近在研究 ASP.NET MVC 模型绑定,发现 DefaultModelBinder 有一个弊端,就是无法实现对浏览器请求参数的自定义,最初的想法是想为实体模型的属性设置特性(Attribute) ...

  3. WorkbookDesigner mvc里面返回file

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...

  4. CSS基础(三):选择器

    常用选择器 元素选择器,即html标记如div,ul,li,p,h1~h6,table等. p { font-size:14px; } h1 { color:#F00; } 复合选择器, 由两个选择器 ...

  5. windows 获取以及更改CMD控制台编码[转]

    本文转自 http://blog.sina.com.cn/s/blog_794b1d96010136yy.html 命令 chcp 功能:显示或设置活动代码页编号   CHCP [nnn]   nnn ...

  6. 向上下左右不间断无缝滚动图片的效果(兼容火狐和IE)

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  7. 30分钟入门Java8之默认方法和静态接口方法

    30分钟入门Java8之默认方法和静态接口方法 前言 上一篇文章30分钟入门Java8之lambda表达式,我们学习了lambda表达式.现在继续Java8新语言特性的学习,今天,我们要学习的是默认方 ...

  8. Tomcat Manager 用户名和密码配置

    在Tomcat中,应用程序的部署很简单,只需将你的WAR放到Tomcat的webapp目录下,Tomcat会自动检测到这个文件,并将其解压.在浏览器中访问这个应用的Jsp时,通常第一次会很慢,因为To ...

  9. 让我们一起Go(十三)

    前言: 上篇,我们了解了Go语言接口的一些知识,在这篇中,我们将继续聊聊接口这东西. Go语言空接口 Go语言中定义一个空接口,也就是没有任何函数需要实现的接口就是一个空接口,作为一个空接口,因为对象 ...

  10. 记一个dynamic的坑

    创建一个控制台程序和一个类库, 在控制台创建一个匿名对象,然后再在类库中访问它,代码如下: namespace ConsoleApplication1 { class Program { static ...