java多线程(二)——锁机制synchronized(同步方法)

synchronized

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

                                                        ——以上来源百度百科

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

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

MyService类

 1 package com.mythread.www.day8.testSyn.ep1;
 2
 3 public class MyService {
 4     public void add(String name) {
 5         try {
 6             int num = 0;
 7             if (name.equals("a")) {
 8                 num = 100;
 9                 System.out.println("a is over");
10                 Thread.sleep(1000);
11             } else {
12                 num = 200;
13                 System.out.println("b is over");
14             }
15             System.out.println(name + " num = " + num);
16         } catch (InterruptedException e) {
17             e.printStackTrace();
18         }
19     }
20 }

线程类A

 1 package com.mythread.www.day8.testSyn.ep1;
 2
 3 public class ThreadA extends Thread {
 4     private MyService service;
 5
 6     public ThreadA(MyService service) {
 7         super();
 8         this.service = service;
 9     }
10
11     @Override
12     public void run() {
13         service.add("a");
14     }
15 }

线程类B

 1 package com.mythread.www.day8.testSyn.ep1;
 2
 3 public class ThreadB extends Thread {
 4     private MyService service;
 5
 6     public ThreadB(MyService service) {
 7         super();
 8         this.service = service;
 9     }
10
11     @Override
12     public void run() {
13         service.add("b");
14     }
15 }

运行类

 1 package com.mythread.www.day8.testSyn.ep1;
 2
 3 public class Run {
 4     public static void main(String[] args) {
 5         MyService service = new MyService();
 6         ThreadA threadA = new ThreadA(service);
 7         threadA.start();
 8         ThreadB threadB = new ThreadB(service);
 9         threadB.start();
10     }
11 }

结果

1 a is over
2 b is over
3 b num = 200
4 a num = 100

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

二、实例变量非线程安全

MyService类

 1 package com.mythread.www.day8.testSyn.ep1;
 2
 3 public class MyService {
 4     private int num = 0;
 5     public void add(String name) {
 6         try {
 7
 8             if (name.equals("a")) {
 9                 num = 100;
10                 System.out.println("a is over");
11                 Thread.sleep(1000);
12             } else {
13                 num = 200;
14                 System.out.println("b is over");
15             }
16             System.out.println(name + " num = " + num);
17         } catch (InterruptedException e) {
18             e.printStackTrace();
19         }
20     }
21 }

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

1 a is over
2 b is over
3 b num = 200
4 a num = 200

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

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

MyService类

 1 package com.mythread.www.day8.testSyn.ep1;
 2
 3 public class MyService {
 4     private int num = 0;
 5     synchronized public void add(String name) {
 6         try {
 7
 8             if (name.equals("a")) {
 9                 num = 100;
10                 System.out.println("a is over");
11                 Thread.sleep(1000);
12             } else {
13                 num = 200;
14                 System.out.println("b is over");
15             }
16             System.out.println(name + " num = " + num);
17         } catch (InterruptedException e) {
18             e.printStackTrace();
19         }
20     }
21 }

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

1 a is over
2 a num = 100
3 b is over
4 b num = 200

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

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

 1 package com.mythread.www.day8.testSyn.ep1;
 2
 3 public class Run {
 4     public static void main(String[] args) {
 5         MyService serviceA = new MyService();
 6         MyService serviceB = new MyService();
 7         ThreadA threadA = new ThreadA(serviceA);
 8         threadA.start();
 9         ThreadB threadB = new ThreadB(serviceB);
10         threadB.start();
11     }
12 }

结果

1 a is over
2 b is over
3 b num = 200
4 a num = 100

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

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

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

My Service类

 1 package com.mythread.www.day8.testSyn.ep1;
 2
 3 public class MyService {
 4     synchronized public void methodA() {
 5         try {
 6             System.out.println("begin methodA threadName = " + Thread.currentThread().getName());
 7             Thread.sleep(3000);
 8             System.out.println("end methodA time = " + System.currentTimeMillis());
 9         } catch (InterruptedException e) {
10             e.printStackTrace();
11         }
12     }
13
14     public void methodB() {
15         try {
16             System.out.println("begin methodB threadName = " + Thread.currentThread().getName());
17             Thread.sleep(3000);
18             System.out.println("end methodB time = " + System.currentTimeMillis());
19         } catch (InterruptedException e) {
20             e.printStackTrace();
21         }
22     }
23 }

线程类A

 1 package com.mythread.www.day8.testSyn.ep1;
 2
 3 public class ThreadA extends Thread {
 4     private MyService service;
 5
 6     public ThreadA(MyService service) {
 7         super();
 8         this.service = service;
 9     }
10
11     @Override
12     public void run() {
13         service.methodA();
14     }
15 }

线程类B

 1 package com.mythread.www.day8.testSyn.ep1;
 2
 3 public class ThreadB extends Thread {
 4     private MyService service;
 5
 6     public ThreadB(MyService service) {
 7         super();
 8         this.service = service;
 9     }
10
11     @Override
12     public void run() {
13         service.methodB();
14     }
15 }

运行类

 1 package com.mythread.www.day8.testSyn.ep1;
 2
 3 public class Run {
 4     public static void main(String[] args) {
 5         MyService service = new MyService();
 6         ThreadA threadA = new ThreadA(service);
 7         threadA.start();
 8         ThreadB threadB = new ThreadB(service);
 9         threadB.start();
10     }
11 }

结果

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

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

My Service类

 1 package com.mythread.www.day8.testSyn.ep1;
 2
 3 public class MyService {
 4     synchronized public void methodA() {
 5         try {
 6             System.out.println("begin methodA threadName = " + Thread.currentThread().getName());
 7             Thread.sleep(3000);
 8             System.out.println("end methodA time = " + System.currentTimeMillis());
 9         } catch (InterruptedException e) {
10             e.printStackTrace();
11         }
12     }
13
14     synchronized public void methodB() {
15         try {
16             System.out.println("begin methodB threadName = " + Thread.currentThread().getName());
17             Thread.sleep(3000);
18             System.out.println("end methodB time = " + System.currentTimeMillis());
19         } catch (InterruptedException e) {
20             e.printStackTrace();
21         }
22     }
23 }

结果

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

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

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

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

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

三、synchronized锁重入

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

My Service类

 1 package com.mythread.www.day8.testSyn.ep1;
 2
 3 public class MyService {
 4     synchronized public void methodA() {
 5         System.out.println("methodA");
 6         methodB();
 7     }
 8
 9     synchronized public void methodB() {
10         System.out.println("methodB");
11         methodC();
12     }
13
14     synchronized public void methodC() {
15         System.out.println("methodC");
16     }
17 }

线程类

1 package com.mythread.www.day8.testSyn.ep1;
2
3 public class MyThread extends Thread {
4     @Override
5     public void run() {
6         MyService myService = new MyService();
7         myService.methodA();
8     }
9 }

运行类

1 package com.mythread.www.day8.testSyn.ep1;
2
3 public class Run {
4     public static void main(String[] args) {
5         MyThread myThread = new MyThread();
6         myThread.start();
7     }
8 }

结果

1 methodA
2 methodB
3 methodC

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

Main类

 1 package com.weishiyao.learn.day4.testThread;
 2
 3 public class Main {
 4     public int i = 10;
 5     synchronized public void operateIMainMethod() {
 6         try {
 7             i--;
 8             System.out.println("main print i=" + i);
 9             Thread.sleep(100);
10         } catch (Exception e) {
11             e.printStackTrace();
12         }
13     }
14 }

Sub类

 1 package com.weishiyao.learn.day4.testThread;
 2
 3 public class Sub extends Main{
 4     synchronized public void operateISubMethod() {
 5         try {
 6             while (i > 0) {
 7                 i--;
 8                 System.out.println("sub print i=" + i);
 9                 Thread.sleep(100);
10                 this.operateIMainMethod();
11             }
12         } catch (Exception e) {
13             e.printStackTrace();
14         }
15     }
16 }

线程类

1 package com.weishiyao.learn.day4.testThread;
2
3 public class MyThread  extends Thread{
4     @Override
5     public void run() {
6         Sub sub = new Sub();
7         sub.operateISubMethod();
8     }
9 }

运行类

1 package com.weishiyao.learn.day4.testThread;
2
3 public class Run {
4     public static void main(String[] args) {
5         MyThread t = new MyThread();
6         t.run();
7     }
8 }

结果

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

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

时间: 2024-12-18 20:13:58

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

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

用关键字synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法之行一个长时间的任务,那么B线程必须等待比较长的时间,在这样的情况下可以使用synchronized同步语句快来解决. 一.用同步代码块解决同步方法的弊端 Task类 1 package com.weishiyao.learn.day4.testSynchorized.ep2; 2 3 public class Task { 4 5 private String getData1; 6 private Stri

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

一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在 java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池 等待队列中). 取到锁后,他就开始执行同步代码(被synchronized修饰的代码):线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中 等待的某个线程就可以拿到锁执行同步代码了.这样就保证了同步代码在统一时刻只有一个线程在执行. 众所周知,在Java多线程

【转载】Java中的锁机制 synchronized & Lock

参考文章: http://blog.csdn.net/chen77716/article/details/6618779 目前在Java中存在两种锁机制:synchronized和Lock,Lock接口及其实现类是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug Lea.本文并不比较synchronized与Lock孰优孰劣,只是介绍二者的实现原理. 数据同步需要依赖锁,那锁的同步又依赖谁?synchronized给出的答案是在软件层面依赖JVM,而Lock给出的方案是在硬件层面依赖特殊的

深入浅出 Java Concurrency(二)—锁机制(一)

前面的章节主要谈谈原子操作,至于与原子操作一些相关的问题或者说陷阱就放到最后的总结篇来整体说明.从这一章开始花少量的篇幅谈谈锁机制. 上一个章节 中谈到了锁机制,并且针对于原子操作谈了一些相关的概念和设计思想.接下来的文章中,尽可能的深入研究锁机制,并且理解里面的原理和实际应用场合. 尽管synchronized在语法上已经足够简单了,在JDK 5之前只能借助此实现,但是由于是独占锁,性能却不高,因此JDK 5以后就开始借助于JNI来完成更高级的锁实现. JDK 5中的锁是接口java.util

java的锁机制——synchronized

转自:http://blog.csdn.net/yangzhijun_cau/article/details/6432216 一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池等待队列中). 取到锁后,他就开始执行同步代码(被synchronized修饰的代码):线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中

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

接上文<深入浅出Java并发包—锁机制(一)  >  2.Sync.FairSync.TryAcquire(公平锁) 我们直接来看代码 protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (isFirst(current) && compareAndSetStat

JAVA中关于锁机制

本文转自 http://blog.csdn.net/yangzhijun_cau/article/details/6432216 一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池等待队列中). 取到锁后,他就开始执行同步代码(被synchronized修饰的代码):线程执行完同步代码后马上就把锁还给同步对象,其他在锁

Java 多线程(六) synchronized关键字详解

Java 多线程(六) synchronized关键字详解 多线程的同步机制对资源进行加锁,使得在同一个时间,只有一个线程可以进行操作,同步用以解决多个线程同时访问时可能出现的问题. 同步机制可以使用synchronized关键字实现. 当synchronized关键字修饰一个方法的时候,该方法叫做同步方法. 当synchronized方法执行完或发生异常时,会自动释放锁. 下面通过一个例子来对synchronized关键字的用法进行解析. 1.是否使用synchronized关键字的不同 例子

深入浅出Java并发包—锁机制(一)

前面我们看到了Lock和synchronized都能正常的保证数据的一致性(上文例子中执行的结果都是20000000),也看到了Lock的优势,那究竟他们是什么原理来保障的呢?今天我们就来探讨下Java中的锁机制! Synchronized是基于JVM来保证数据同步的,而Lock则是在硬件层面,依赖特殊的CPU指令实现数据同步的,那究竟是如何来实现的呢?我们一一看来! 一.synchronized的实现方案 synchronized比较简单,语义也比较明确,尽管Lock推出后性能有较大提升,但是