一, 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 会怎样呢(简单说是可见性) 但是啥是可见性: 就是告知 OS 在管理 CPU 计算过程中要检查内存中的数据的有效性, 保证使用的数据都是有效的.
- 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();
- }
- }
来源: http://www.bubuko.com/infodetail-2970586.html