一、synchronized 关键字

  1)synchronized 锁什么?锁对象。可能锁对象包括: this, 临界资源对象,Class 类对象。如同下面例子所示;

 package cn.test.juc;

 public class TestSynchronized {

     private int count = 0;
private Object object = new Object(); public void testSyn1() {
//锁对象(这里面是锁临界资源)
synchronized (object) {
System.out.println(Thread.currentThread().getName()
+" count =" + count++);
}
} public void testSyn2() {
//锁当前对象
synchronized (this) {
System.out.println(Thread.currentThread().getName()
+" count =" + count++);
}
} //锁当前对象
public synchronized void testSyn3() {
System.out.println(Thread.currentThread().getName()
+" count =" + count++);
}
}

  2)如果在加锁的时候对当前对象的访问限定要求比较低的时候,建议锁某一段代码或者某一个对象;如果访问限定要求比较高的话,建议锁当前对象。简单而言就可以说是减小锁的范围。对于锁当前对象或者都是重量级锁,什么意思呢,“就是任意多个线程,多个资源不会被多个线程访问所影响的”

  3)再看下面的例子,锁当前类的类对象的两种方式:

 public class TestSynchronized02 {
private static int staticCount = 0; //静态同步方法,锁的是当前类型的类对象(即TestSynchronized02.class)
public static synchronized void testSyn1() {
System.out.println(Thread.currentThread().getName()
+" staticCount =" + staticCount++);
} //下面的这种方式也是锁当前类型的类对象
public static void testSyn2() {
synchronized (TestSynchronized02.class) {
System.out.println(Thread.currentThread().getName()
+" staticCount =" + staticCount++);
}
}
}

  4)看一下下面一段小程序的运行结果

 public class TestSynchronized03 implements Runnable{
private int count = 0; @Override
public /*synchronized */ void run() {
System.out.println(Thread.currentThread().getName()
+" count =" + count++);
} public static void main(String[] args) {
TestSynchronized03 testSynchronized03 = new TestSynchronized03();
for (int i = 0; i < 10 ; i++) {
new Thread(testSynchronized03, "Thread --- " + i).start();
}
}
}

  我们发下下面的结果少加了一个1,这就是原子性的问题。在synchronized关键字没有使用的时候,对于变量count而言(由多个线程访问),是不能保证原子性(某一段代码从开始运行到结束不能分步执行)的,上面的代码没有使用同步,那么很显然多线程对变量进行加操作就可能会在同一时刻只进行1次加操作

  

  5)关于同步方法和非同步方法:同步方法只影响  锁定同一个锁对象的同步方法,不影响非同步方法被其他线程调用,也不影响其他所资源的同步方法(简单理解就是锁的不是同一个资源,就不会影响);

 package cn.test.juc;

 public class TestSynchronized04 {

     private Object o = new Object();

     //同步方法
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 MyThread implements Runnable{
int i;
TestSynchronized04 testSynchronized04;
public MyThread(int i, TestSynchronized04 testSynchronized04) {
this.i = i;
this.testSynchronized04 = testSynchronized04;
} @Override
public void run() {
if(i == 0) {
testSynchronized04.m1();
} else if(i == 1) {
testSynchronized04.m3();
} else {
testSynchronized04.m2();
}
}
} public static void main(String[] args) {
TestSynchronized04 testSynchronized04 = new TestSynchronized04();
new Thread(new TestSynchronized04.MyThread(0, testSynchronized04)).start();
new Thread(new TestSynchronized04.MyThread(1, testSynchronized04)).start();
new Thread(new TestSynchronized04.MyThread(2, testSynchronized04)).start();
}
}  

  下面是运行的结果

  

  6)脏读问题

 package cn.test.juc;

 import java.util.concurrent.TimeUnit;

 public class TestSynchronized05 {
private double d = 0.0; //相当与是set方法
public synchronized void m1(double d) {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.d = d;
} //相当于是get方法
public double m2() {
return this.d;
} public static void main(String[] args) {
final TestSynchronized05 testSynchronized05 = new TestSynchronized05(); new Thread(new Runnable() {
@Override
public void run() {
testSynchronized05.m1(100);
}
}).start(); System.out.println(testSynchronized05.m2());
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(testSynchronized05.m2());
}
}

  上面代码的输出是0.0  100.00,而不是期望的100.00  100.00,出现这种情况(脏读)的原因是什么的?就是m1方法的这段代码引起的

  

  这段代码表示睡眠2秒之后再进行set操作,使用这一段代码的原因就是模拟实际当中的复杂处理操作,可能会比较耗时,但是这时候还没执行完毕没有将正确的结果写会,别的线程就去访问临界资源的话,就会出现脏读的情况。

  7)锁的可重入问题:同一个线程,多次调用同步代码,锁定同一个对象,可重入

  看看下面的代码实例:main调用m1方法,m1方法中调用m2方法,两个方法锁定的都是this对象,就会是上面说到的这种情况

 package cn.test.juc;

 import java.util.concurrent.TimeUnit;

 public class TestSynchronized06 {

     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()");
} public static void main(String[] args) {
new TestSynchronized06().m1();
}
}

  8)关于同步的继承问题:同一个线程中,子类同步方法覆盖父类的同步方法,可以指定调用父类的同步方法(相当于锁的重入);

 package cn.test.juc;

 import java.util.concurrent.TimeUnit;

 public class TestSynchronized07 {
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");
} public static void main(String[] args) {
new ExtendTest07().m();
}
} class ExtendTest07 extends TestSynchronized07 {
synchronized void m() {
System.out.println("Sub Class m start");
super.m();
System.out.println("Sub Class m end");
}
}

  9)锁与异常:当同步方法出现异常的时候会自动释放锁,不会影响其他线程的执行

 package cn.test.juc;

 import java.util.concurrent.TimeUnit;

 public class TestSynchronized08 {
int i = 0;
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;
}
}
} public static void main(String[] args) {
final TestSynchronized08 t = new TestSynchronized08();
new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}, "t1").start(); new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}, "t2").start();
}
}

  下面是输出的结果:

  

  10)synchronized锁的是对象,而不是引用:同步代码一旦加锁之后会有一个临时锁引用执行锁对象,和真实的引用无直接关联,在锁释放之前,修改锁对象引用不会影响同步代码块的执行

 package cn.test.syn;

 import java.util.concurrent.TimeUnit;

 public class TestSynchronized09 {
Object o = new Object(); int i = 0;
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);
}
}
} public static void main(String[] args) {
final TestSynchronized09 t = new TestSynchronized09();
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);
}
}

  11)synchronized中的常量问题:在定义同步代码块的时候,不要使用常量对象作为锁对象

 package cn.test.syn;

 import java.util.concurrent.TimeUnit;

 public class TestSynchronized09 {
Object o = new Object(); int i = 0;
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);
}
}
} public static void main(String[] args) {
final TestSynchronized09 t = new TestSynchronized09();
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);
}
}

二、Volatile关键字

  1、下面的代码在没有使用volatile之前,是不会从循环中跳出的(main线程和新创建的线程互相之间是不可见的,所以新创建的线程在使用m方法的时候并不知道main线程已经改变了b的值,所以不会跳出循环),那么使用volatile会怎样呢(简单说是可见性)但是啥是可见性:当某个线程正在使用对象状态,而另一个线程在同时修改该状态,需要确保当一个线程修改了对象状态后,其他线程能够看到发生的状态变化。可见性错误是指当读操作与写操作在不同的线程中执行时,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情。

 package cn.test.Volatile;

 import java.util.concurrent.TimeUnit;

 public class TestVolatile01 {
/*volatile*/ boolean b = true; void m(){
System.out.println("start");
while(b){}
System.out.println("end");
} public static void main(String[] args) {
final TestVolatile01 t = new TestVolatile01();
new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}).start(); try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.b = false;
}
}

  2、volatile只能保证可见性,不能保证原子性,volatile不是加锁问题,只是保证内存数据可见;参照下面的例子,运行的结果不是期望的100000,而是

当然,也不一定每次都是这个值。

 package cn.test.Volatile;

 import java.util.ArrayList;
import java.util.List; public class TestVolatile02 {
volatile int count = 0;
/*synchronized*/ void m(){
for(int i = 0; i < 10000; i++){
count++;
}
} public static void main(String[] args) {
final TestVolatile02 t = new TestVolatile02();
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);
}
}

三、AtomicXXX

  Atomic主要做的就是原子操作,其中的每个方法都是原子操作,可以保证线程安全。参照下面的例子:创建十个线程,每个线程累加100次,得到的结果就是1000

 package cn.test.atomic;

 import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; public class TestAtomic01 {
AtomicInteger count = new AtomicInteger(0);
void m1(){
for(int i = 0; i < 100; i++){
/*if(count.get() < 1000)*/
count.incrementAndGet();
}
} public static void main(String[] args) {
final TestAtomic01 t = new TestAtomic01();
List<Thread> threads = new ArrayList<>();
for(int i = 0; i < 10; i++){
threads.add(new Thread(new Runnable() {
@Override
public void run() {
t.m1();
}
}));
}
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());
}
}

四、CountDownLatch

 package cn.test.syn;
/**
* 门闩 - CountDownLatch
* 可以和锁混合使用,或替代锁的功能。
* 在门闩未完全开放之前等待。当门闩完全开放后执行。
* 避免锁的效率低下问题。
*/
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; public class Test {
CountDownLatch latch = new CountDownLatch(5); 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);
}
} public static void main(String[] args) {
final Test t = new Test();
new Thread(new Runnable() {
@Override
public void run() {
t.m1();
}
}).start(); new Thread(new Runnable() {
@Override
public void run() {
t.m2();
}
}).start();
}
}

Java并发编程基础——同步的更多相关文章

  1. Java并发编程基础

    Java并发编程基础 1. 并发 1.1. 什么是并发? 并发是一种能并行运行多个程序或并行运行一个程序中多个部分的能力.如果程序中一个耗时的任务能以异步或并行的方式运行,那么整个程序的吞吐量和可交互 ...

  2. 并发-Java并发编程基础

    Java并发编程基础 并发 在计算机科学中,并发是指将一个程序,算法划分为若干个逻辑组成部分,这些部分可以以任何顺序进行执行,但与最终顺序执行的结果一致.并发可以在多核操作系统上显著的提高程序运行速度 ...

  3. Java并发编程--基础进阶高级(完结)

    Java并发编程--基础进阶高级完整笔记. 这都不知道是第几次刷狂神的JUC并发编程了,从第一次的迷茫到现在比较清晰,算是个大进步了,之前JUC笔记不见了,重新做一套笔记. 参考链接:https:// ...

  4. Java并发编程:同步容器

    Java并发编程:同步容器 为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch). ...

  5. 【转】Java并发编程:同步容器

    为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch).今天我们就来讨论下同步容器. ...

  6. 8、Java并发编程:同步容器

    Java并发编程:同步容器 为了方便编写出线程安全的程序,Java里面提供了一些线程安全类和并发工具,比如:同步容器.并发容器.阻塞队列.Synchronizer(比如CountDownLatch). ...

  7. Java并发编程基础-线程安全问题及JMM(volatile)

    什么情况下应该使用多线程 : 线程出现的目的是什么?解决进程中多任务的实时性问题?其实简单来说,也就是解决“阻塞”的问题,阻塞的意思就是程序运行到某个函数或过程后等待某些事件发生而暂时停止 CPU 占 ...

  8. java并发编程基础概念

    本次内容主要讲进程和线程.CPU核心数和线程数.CPU时间片轮转机制.上下文切换,并行和并发的基本概念以及并发编程的好处和注意事项,为java并发编程打下扎实基础. 1.什么是进程和线程 1.1 进程 ...

  9. 多线程(一)java并发编程基础知识

    线程的应用 如何应用多线程 在 Java 中,有多种方式来实现多线程.继承 Thread 类.实现 Runnable 接口.使用 ExecutorService.Callable.Future 实现带 ...

随机推荐

  1. 阅读笔记:JAVA - chapter 1 & 2

    static 即使没有创建对象,也能调用这个方法. 当Static method, static变量有定义,不同对象将指向同一存储空间,将其初始化. 存储方式 寄存器 堆栈:对象引用,需要知道存活多久 ...

  2. springmvc的面试知识点总结

    新的一年,开启新的篇章,欧气满满,迎接未来. 前几天回顾了spring相关的知识点,现在再来回顾下springmvc相关的知识点做一下总结. 问题总结 之前面试问题总结的那篇文章中,与springmv ...

  3. 怎么把微信里的文件发到QQ?

    对于如何将微信里的文件发到QQ这个问题,首先要看是在手机中操作还是在电脑上操作,针对不同的发送方式逐一介绍如下: 一.从手机微信发送文件到QQ 1.在手机微信中找到需要发送的文件打开,点击右上角竖排的 ...

  4. java 日志体系(四)log4j 源码分析

    java 日志体系(四)log4j 源码分析 logback.log4j2.jul 都是在 log4j 的基础上扩展的,其实现的逻辑都差不多,下面以 log4j 为例剖析一下日志框架的基本组件. 一. ...

  5. IDEA debugger模式下启动慢

    很可能是因为代码里面有端点造成的. 点击如下图的重叠红点,找到对应端点点掉就可以了.

  6. centos6.6安装Elasticsearch

    1. 安装jar8 yum list java-1.8* sudo yum install java-1.8.0-openjdk* -y java --version 2. 安装elasticsear ...

  7. 非root用户安装cuda和cudnn

    1.根据自己的系统在官网下载cuda (选择runfile(local)) https://developer.nvidia.com/cuda-downloads 2.进入下载目录,并执行 sh cu ...

  8. Mybatis中的CDATA标签

    术语 CDATA 指的是不应由 XML 解析器进行解析的文本数据(Unparsed Character Data). 在 XML 元素中,"<" 和 "&& ...

  9. AtCoder Regular Contest 090

    C - Candies 链接:https://arc090.contest.atcoder.jp/tasks/arc090_a 题意:从左上角走到右下角,只能向右和向下走,问能最多能拿多少糖果. 思路 ...

  10. 分别求二叉树前、中、后序的第k个节点

    一.求二叉树的前序遍历中的第k个节点 //求先序遍历中的第k个节点的值 ; elemType preNode(BTNode *root,int k){ if(root==NULL) return ' ...