synchronized关键字

  锁对象。synchronized(this)和synchronized方法都是锁当前对象。

import java.util.concurrent.TimeUnit;

public class Test_01 {
private int count = 0;
private Object o = new Object(); public static void main(String[] args) {
final Test_01 t = new Test_01();
new Thread(new Runnable() {
@Override
public void run() {
t.testSync2();
}
}, "testSync2").start();
new Thread(new Runnable() {
@Override
public void run() {
t.testSync1();
}
}, "testSync1").start();
new Thread(new Runnable() {
@Override
public void run() {
t.testSync3();
}
}, "testSync3").start();
} public void testSync1() {
synchronized (o) {
System.out.println(Thread.currentThread().getName()
+ " count = " + count++);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public void testSync2() {
synchronized (this) {
System.out.println(Thread.currentThread().getName()
+ " count = " + count++);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public synchronized void testSync3() {
System.out.println(Thread.currentThread().getName()
+ " count = " + count++);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
} }

  同步方法 - static:静态同步方法,锁的是当前类型的类对象。在代码中就是类名.class

import java.util.concurrent.TimeUnit;

public class Test_02 {
private static int staticCount = 0; public static synchronized void testSync4() {
System.out.println(Thread.currentThread().getName()
+ " staticCount = " + staticCount++);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} public static void testSync5() {
synchronized (Test_02.class) {
System.out.println(Thread.currentThread().getName()
+ " staticCount = " + staticCount++);
}
} }

  同步方法 - 原子性
  加锁的目的: 就是为了保证操作的原子性。

public class Test_03 implements Runnable {

    private int count = 0;

    public static void main(String[] args) {
Test_03 t = new Test_03();
for (int i = 0; i < 5; i++) {
new Thread(t, "Thread - " + i).start();
}
} @Override
public /*synchronized*/ void run() {
System.out.println(Thread.currentThread().getName()
+ " count = " + count++);
} }

  同步方法 - 同步方法和非同步方法的调用
  同步方法只影响锁定同一个锁对象的同步方法。不影响其他线程调用非同步方法,或调用其他锁资源的同步方法。

public class Test_04 {
Object o = new Object(); public static void main(String[] args) {
Test_04 t = new Test_04();
new Thread(new Test_04.MyThread01(0, t)).start();
new Thread(new Test_04.MyThread01(1, t)).start();
new Thread(new Test_04.MyThread01(-1, t)).start();
} public synchronized void m1() { // 重量级的访问操作。
System.out.println("public synchronized void m1() start");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("public synchronized void m1() end");
} public void m3() {
synchronized (o) {
System.out.println("public void m3() start");
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("public void m3() end");
}
} public void m2() {
System.out.println("public void m2() start");
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("public void m2() end");
} public static class MyThread01 implements Runnable {
int i;
Test_04 t; public MyThread01(int i, Test_04 t) {
this.i = i;
this.t = t;
} public void run() {
if (i == 0) {
t.m1();
} else if (i > 0) {
t.m2();
} else {
t.m3();
}
}
} }

  结果:

public synchronized void m1() start
public void m2() start
public void m3() start
public void m2() end
public void m3() end
public synchronized void m1() end

  同步方法 - 多方法调用原子性问题(业务)
  同步方法只能保证当前方法的原子性,不能保证多个业务方法之间的互相访问的原子性。
  注意:在商业开发中,多方法要求结果访问原子操作,需要多个方法都加锁,且锁定统一个资源。一般来说,商业项目中,不考虑业务逻辑上的脏读问题。

import java.util.concurrent.TimeUnit;

public class Test_05 {
private double d = 0.0; public static void main(String[] args) {
final Test_05 t = new Test_05(); new Thread(new Runnable() {
@Override
public void run() {
t.m1(100);
}
}).start();
System.out.println(t.m2());
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(t.m2());
} public synchronized void m1(double d) {
try {
// 相当于复杂的业务逻辑代码。
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.d = d;
} public double m2() {
return this.d;
} }

  锁可重入: 同一个线程,多次调用同步代码,锁定同一个锁对象,可重入。

import java.util.concurrent.TimeUnit;

public class Test_06 {

    public static void main(String[] args) {

        new Test_06().m1();

    }

    synchronized void m1() { // 锁this
System.out.println("m1 start");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
m2();
System.out.println("m1 end");
} synchronized void m2() { // 锁this
System.out.println("m2 start");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m2 end");
} }

  同步方法 - 继承::类同步方法覆盖父类同步方法。可以指定调用父类的同步方法。相当于锁的重入。

import java.util.concurrent.TimeUnit;

public class Test_07 {

    public static void main(String[] args) {
new Sub_Test_07().m();
} synchronized void m() {
System.out.println("Super Class m start");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Super Class m end");
} } class Sub_Test_07 extends Test_07 {
synchronized void m() {
System.out.println("Sub Class m start");
super.m();
System.out.println("Sub Class m end");
}
}

  同步方法 - 锁与异常:当同步方法中发生异常的时候,自动释放锁资源。不会影响其他线程的执行。注意同步业务逻辑中,如果发生异常如何处理。

import java.util.concurrent.TimeUnit;

public class Test_08 {
int i = 0; public static void main(String[] args) {
final Test_08 t = new Test_08();
new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}, "t1").start(); new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}, "t2").start();
} synchronized void m() {
System.out.println(Thread.currentThread().getName() + " - start");
while (true) {
i++;
System.out.println(Thread.currentThread().getName() + " - " + i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (i == 5) {
i = 1 / 0;
}
}
} }

  结果:

t1 - start
t1 - 1
t1 - 2
t1 - 3
t1 - 4
t1 - 5
Exception in thread "t1" java.lang.ArithmeticException: / by zero
t2 - start
at concurrent.t01.Test_08.m(Test_08.java:43)
t2 - 6
at concurrent.t01.Test_08$1.run(Test_08.java:19)
at java.base/java.lang.Thread.run(Thread.java:844)
t2 - 7
t2 - 8
t2 - 9
t2 - 10

volatile关键字

  volatile的可见性:通知OS操作系统底层,在CPU计算过程中,都要检查内存中数据的有效性。保证最新的内存数据被使用。

import java.util.concurrent.TimeUnit;

public class Test_09 {

    volatile boolean b = true;

    public static void main(String[] args) {
final Test_09 t = new Test_09();
new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}).start(); try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} t.b = false;
} void m() {
System.out.println("start");
while (b) {
}
System.out.println("end");
}
}

  volatile的非原子性问题:只能保证可见性,不能保证原子性。不是枷锁问题,只是内存数据可见。

public class Test_10 {

    volatile int count = 0;

    public static void main(String[] args) {
final Test_10 t = new Test_10();
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 10; i++) {
threads.add(new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}));
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(t.count);
} /*synchronized*/ void m() {
for (int i = 0; i < 10000; i++) {
count++;
}
}
}

AtomicXxx

  同步类型:原子操作类型。 其中的每个方法都是原子操作。可以保证线程安全。

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; public class Test_11 {
AtomicInteger count = new AtomicInteger(0); public static void main(String[] args) {
final Test_11 t = new Test_11();
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < 10; i++) {
threads.add(new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}));
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(t.count.intValue());
} void m() {
for (int i = 0; i < 10000; i++) {
/*if(count.get() < 1000)*/
count.incrementAndGet();
}
}
}

  同步粒度问题:尽量在商业开发中避免同步方法。使用同步代码块。 细粒度解决同步问题。可以提高效率。

public class Test_12 {

    synchronized void m1() {
// 前置逻辑
System.out.println("同步逻辑");
// 后置逻辑
} void m2() {
// 前置逻辑
synchronized (this) {
System.out.println("同步逻辑");
}
// 后置逻辑
}
}

  对象变更问题:同步代码一旦加锁后,那么会有一个临时的锁引用执行锁对象,和真实的引用无直接关联。在锁未释放之前,修改锁对象引用,不会影响同步代码的执行。

public class Test_13 {
Object o = new Object(); int i = 0; public static void main(String[] args) {
final Test_13 t = new Test_13();
new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}, "thread1").start();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}, "thread2");
t.o = new Object();
thread2.start(); System.out.println(t.i);
System.out.println(t.a());
System.out.println(t.i);
} int a() {
try {
/*
* return i ->
* int _returnValue = i; // 0;
* return _returnValue;
*/
return i;
} finally {
i = 10;
}
} void m() {
System.out.println(Thread.currentThread().getName() + " start");
synchronized (o) {
while (true) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " - " + o);
}
}
}
}

  结果:

thread1 start
thread1 - java.lang.Object@6018fac7
thread1 - java.lang.Object@6018fac7
thread1 - java.lang.Object@6018fac7
0
0
10
thread2 start
thread1 - java.lang.Object@6edf18b4
thread2 - java.lang.Object@6edf18b4
thread1 - java.lang.Object@6edf18b4
thread2 - java.lang.Object@6edf18b4
thread1 - java.lang.Object@6edf18b4

  常量问题:在定义同步代码块时,不要使用常量对象作为锁对象。

  i1、i2会实现m1、m2方法的同步;s1、s2是不同的对象,不能实现m1、m2方法的同步。

public class Test_14 {
String s1 = "hello";
String s2 = new String("hello"); // new关键字,一定是在堆中创建一个新的对象。
Integer i1 = 1;
Integer i2 = 1; public static void main(String[] args) {
final Test_14 t = new Test_14();
new Thread(new Runnable() {
@Override
public void run() {
t.m1();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
t.m2();
}
}).start();
} void m1() {
synchronized (i1) {
System.out.println("m1()");
while (true) { }
}
} void m2() {
synchronized (i2) {
System.out.println("m2()");
while (true) { }
}
} }

门闩 - CountDownLatch

  可以和锁混合使用,或替代锁的功能。在门闩未完全开放之前等待。当门闩完全开放后执行。避免锁的效率低下问题。

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; public class Test_15 {
CountDownLatch latch = new CountDownLatch(5); public static void main(String[] args) {
final Test_15 t = new Test_15();
new Thread(new Runnable() {
@Override
public void run() {
t.m1();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
t.m2();
}
}).start();
} void m1() {
try {
latch.await();// 等待门闩开放。
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m1() method");
} void m2() {
for (int i = 0; i < 10; i++) {
if (latch.getCount() != 0) {
System.out.println("latch count : " + latch.getCount());
latch.countDown(); // 减门闩上的锁。
}
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("m2() method : " + i);
}
} }

  结果:

latch count : 5
m2() method : 0
latch count : 4
m2() method : 1
latch count : 3
m2() method : 2
latch count : 2
m2() method : 3
latch count : 1
m1() method

m2() method : 4
m2() method : 5
m2() method : 6
...

练习题

自定义容器,提供新增元素(add)和获取元素数量(size)方法。
启动两个线程。线程1向容器中新增10个数据。线程2监听容器元素数量,当容器元素数量为5时,线程2输出信息并终止。

  方法一(volatile的可见性):

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit; public class Test_01 {
public static void main(String[] args) {
final Test_01_Container t = new Test_01_Container();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("add Object to Container " + i);
t.add(new Object());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
while (true) {
if (t.size() == 5) {
System.out.println("size = 5");
break;
}
}
}
}).start();
}
} class Test_01_Container {
volatile List<Object> container = new ArrayList<>(); public void add(Object o) {
this.container.add(o);
} public int size() {
return this.container.size();
}
}

  方法二(synchornized):

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit; public class Test_02 {
public static void main(String[] args) {
final Test_02_Container t = new Test_02_Container();
final Object lock = new Object(); new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
if (t.size() != 5) {
try {
lock.wait(); // 线程进入等待队列。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("size = 5");
lock.notifyAll(); // 唤醒其他等待线程
}
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
for (int i = 0; i < 10; i++) {
System.out.println("add Object to Container " + i);
t.add(new Object());
if (t.size() == 5) {
lock.notifyAll();
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
}
} class Test_02_Container {
List<Object> container = new ArrayList<>(); public void add(Object o) {
this.container.add(o);
} public int size() {
return this.container.size();
}
}

  方法三(门闩):

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; public class Test_03 {
public static void main(String[] args) {
final Test_03_Container t = new Test_03_Container();
final CountDownLatch latch = new CountDownLatch(1); new Thread(new Runnable() {
@Override
public void run() {
if (t.size() != 5) {
try {
latch.await(); // 等待门闩的开放。 不是进入等待队列
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("size = 5");
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("add Object to Container " + i);
t.add(new Object());
if (t.size() == 5) {
latch.countDown(); // 门闩-1
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
} class Test_03_Container {
List<Object> container = new ArrayList<>(); public void add(Object o) {
this.container.add(o);
} public int size() {
return this.container.size();
}
}

多线程同步synchornized、volatile、Atomic、CountDownLatch示例的更多相关文章

  1. Java多线程同步工具类之CountDownLatch

    在过去我们实现多线程同步的代码中,往往使用join().wait().notiyAll()等线程间通信的方式,随着JUC包的不断的完善,java为我们提供了丰富同步工具类,官方也鼓励我们使用工具类来实 ...

  2. C++实现一个多线程同步方式的协同工作程序示例

    多线程并发程序与协同程序其实是不同的概念.多线程并发是多个执行序同时运行,而协同程序是多个执行序列相互协作,同一时刻只有一个执行序列.今天想到的是将两者结合起来,拿现实生活中的例子来说,假设一个班级有 ...

  3. 多线程同步工具——volatile变量

    关于volatile,找了一堆资料看,看完后想找一个方法去做测试,测了很久,感觉跟没有一样. 这本书<深入理解Java内存模型>,对volatile描述中有这样一个比喻的说法,如下代码所示 ...

  4. java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析

    java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...

  5. Java中使用CountDownLatch进行多线程同步

    CountDownLatch介绍 在前面的Java学习笔记中,总结了Java中进行多线程同步的几个方法: 1.synchronized关键字进行同步. 2.Lock锁接口及其实现类ReentrantL ...

  6. Java中多线程同步类 CountDownLatch

    在多线程开发中,常常遇到希望一组线程完成之后在执行之后的操作,java提供了一个多线程同步辅助类,可以完成此类需求: 类中常见的方法: 其中构造方法:CountDownLatch(int count) ...

  7. 转:关于JAVA多线程同步

    转:http://lanvis.blog.163.com/blog/static/26982162009798422547/ 因为需要,最近关注了一下JAVA多线程同步问题.JAVA多线程同步主要依赖 ...

  8. 并行编程之多线程共享非volatile变量,会不会可能导致线程while死循环

    背景 大家都知道线程之间共享变量要用volatilekeyword.可是,假设不用volatile来标识,会不会导致线程死循环?比方以下的伪代码: static int flag = -1; void ...

  9. 知识点干货—多线程同步【6】之synchronized

    "明日复明日,明日何其多. 我生待明日,万事成蹉跎. 世人若被明日累,春去秋来老将至. 朝看水东流,暮看日西坠. 百年明日能几何?请君听我明日歌. 明日复明日,明日何其多! 日日待明日,万世 ...

随机推荐

  1. airflow整体架构

    run命令运行过程 读取dag文件生成task依赖关系,然后生成封装airflow run的command命令,通过celery发送到executor端,重新执行该airflow run命令. sch ...

  2. Preloading Your ASP.NET Applications

    You may have noticed that the first request to an ASP.NET Web site takes longer than subsequent requ ...

  3. Express路由

    1. 路由器的配置分为两个,一个是需要做页面的渲染,一个是需要直接进行对数据进行输出,对于路由器的配置需要对路由器在公共的app.js进行注册与注入才能生效,否则是不能生效的.配置时根据不同的应用场景 ...

  4. Java进程&线程(整理)

    Java进程&线程 程序:程序员写的代码,就是代码,不运行好像不会发生什么: 进程:一个进程可以理解为"运行的"一个程序,当我们启动一个java程序后,对应的jvm就会创建 ...

  5. MyBatis注解-动态SQL 一个 SqlProvider的demo

    Provider动态语言注解 MyBatis提供了多个注解如:@InsertProvider,@UpdateProvider,@DeleteProvider和@SelectProvider,这些都是建 ...

  6. linux命令及用法

  7. 微信公众号openid处理的一些笔记

    每个用户对每个公众号的OpenID是唯一的.对于不同公众号,同一用户的openid不同.如果公司有多个公众号,可以通过开放平台关联,这样同一用户,对同一个微信开放平台下的不同应用,unionid是相同 ...

  8. spring boot 监听器实例

    在日常项目中订单创建成功后,会有类似各式各样的通知.有站内通知.短信通知.微信,app通知. 伪代码: 这里,只用伪代码示例.各式各样的通知 肯定不只一行代码.只是简化.如果后续还要增加各种各样的通知 ...

  9. 文件服务之二:ftp协议

    FTP连接 命令连接 传输命令(客户端发给服务端的命令),服务端的21/tcp 数据连接 传输数据(传输数据时建立,数据传输完拆除) 数据链接的建立方法:主动.被动 主动模式(PORTstyle服务器 ...

  10. python大法好——ython GUI编程(Tkinter)

    Python GUI编程(Tkinter) Python 提供了多个图形开发界面的库,几个常用 Python GUI 库如下: Tkinter: Tkinter 模块(Tk 接口)是 Python 的 ...