线程安全性的定义:
当多个线程访问某个类的时候,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的同步或者协同,这个类都能表现出正确的行为,那么我们就称则这个类是线程安全的
原子性的锁有两种:
synchronized:是Java中的关键字,是一种同步锁,依赖于JVM
Lock:依赖特殊的CPU指令,代码实现,ReentrantLock
这里我们先来了解synchronized
1、修饰代码块:大括号括起来的代码,作用于调用的对象
2、修饰方法:整个方法,作用于调用的对象
3、修饰静态方法:整个静态方法,作用于所有对象
4、修饰类:括号括起来的部分,作用于所有对象
修饰代码块
package com.lyy.concurrency.sync; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SynchronizedExample1 { // 修饰一个代码块 public void test1(int j){ synchronized (this){ for (int i = 0; i < 10; i++) { System.out.println("test1 j:"+j+" — i:"+i); } } } public static void main(String[] args) { SynchronizedExample1 example1 = new SynchronizedExample1(); SynchronizedExample1 example2 = new SynchronizedExample1(); ExecutorService executorService = Executors.newCachedThreadPool();//声明一个线程池 //加上线程池相当于我们调用了两个线程 //两个线程调用了同一个对象 executorService.execute(() ->{ example1.test1(1); }); executorService.execute(() ->{ example2.test1(2); }); } }
返回结果:
test1 j:1 — i:0 test1 j:1 — i:1 test1 j:1 — i:2 test1 j:1 — i:3 test1 j:1 — i:4 test1 j:1 — i:5 test1 j:1 — i:6 test1 j:1 — i:7 test1 j:1 — i:8 test1 j:1 — i:9 test1 j:2 — i:0 test1 j:2 — i:1 test1 j:2 — i:2 test1 j:2 — i:3 test1 j:2 — i:4 test1 j:2 — i:5 test1 j:2 — i:6 test1 j:2 — i:7 test1 j:2 — i:8 test1 j:2 — i:9
线程1和线程2按照各自顺序执行,线程一和线程二都能够按照自己的同步代码走下去,但是不一定能保证线程一执行完之后才到线程二执行,这就要看哪一个线程能够率先抢到资源。
修饰方法
package com.lyy.concurrency.sync; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class SynchronizedExample1 { //修饰一个方法 public synchronized void test2(int j){ for (int i = 0; i < 10; i++) { System.out.println("test2 j:"+j+" — i:"+i); } } public static void main(String[] args) { SynchronizedExample1 example1 = new SynchronizedExample1(); SynchronizedExample1 example2 = new SynchronizedExample1(); ExecutorService executorService = Executors.newCachedThreadPool();//声明一个线程池 //加上线程池相当于我们调用了两个线程 //两个线程调用了同一个对象 executorService.execute(() ->{ example1.test2(1); }); executorService.execute(() ->{ example2.test2(2); }); } }
返回结果:
test2 j:1 — i:0 test2 j:1 — i:1 test2 j:1 — i:2 test2 j:1 — i:3 test2 j:1 — i:4 test2 j:1 — i:5 test2 j:1 — i:6 test2 j:1 — i:7 test2 j:2 — i:0 test2 j:2 — i:1 test2 j:2 — i:2 test2 j:2 — i:3 test2 j:2 — i:4 test2 j:2 — i:5 test2 j:2 — i:6 test2 j:2 — i:7 test2 j:2 — i:8 test2 j:1 — i:8 test2 j:2 — i:9 test2 j:1 — i:9
我们可以看到1 和2 是交替运行的,但是各自都是按照顺序在执行,这里是因为修饰代码块只能作用于当前调用的对象,我们这里是调用了两个方法所以,两个线程则互不干扰,都是各自执行各自的代码,同步是整个方法
注意:如果SynchronizedExample1 是个子类 那么实现test2的时候是不会携带synchronized关键字 ,因为synchronized是不属于方法声明的一部分,因此,synchronized关键字不能被继承,如果想要去实现这个子类继承synchronized,需要我们手动是实现这个功能