夜光序言:
如果心灵看不到春日和煦的阳光,痛苦像毒蛇一样缠绕于你的心房。人生短暂呵,要勇敢地去追寻梦想,在短暂的生命长河中留下点点帆影。
正文:
以道御术 / 以术识道
Synchronized和Lock的区别
并发编程中,锁是经常需要使用的。
在开发中我们常用的锁有两种Synchronized和Lock。
线程安全问题
线程安全是在多线程编程中
有可能会出现同时访问同一个 共享、可变资源 的情况
始终都不会导致数据破坏以及其他不该出现的结果。
这种资源可以是一个变量、一个对象、一个文件等。
共享:多个线程可以同时访问该共享变量。
可变:数据在生命周期中可以被改变。
内置锁
package 单例问题与线程安全性问题;
public class Singleton {
//私有化构造方法
private Singleton(){ }
//夜光:有饿汉式和懒汉式两种区别
//首先我们说,饿汉式,创建的过程中就okay了
private static Singleton instance = new Singleton();
public static Singleton getInstance(){
return instance;
}
}
package 单例问题与线程安全性问题;
//夜光,我们之前写了一个饿汉式
//这里,我们写一个懒汉式
public class Singleton1 {
//首先,我们要有一个私有化的构造方法
private Singleton1(){
}
private static Singleton1 instance;
public static Singleton1 getInstance(){
if (instance == null) //夜光:等于空的时候,创建
instance = new Singleton1();
return instance;
}
}
package 单例问题与线程安全性问题;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MultiThreadMain {
public static void main(String[] args) {
//先写上这行代码
ExecutorService threadPool = Executors.newFixedThreadPool(20); //夜光:给个值20
for(int i=0;i<20;i++){
threadPool.execute(new Runnable() {
@Override
public void run() {
//我们输出一下
System.out.println(Thread.currentThread().getName() +":"+ Singleton1.getInstance());
}
});
}
}
}
package 自旋锁死锁重入锁;
//写一个测试案例
public class Demo {
//同步锁
public synchronized void a(){
System.out.println("a");
//在a方法里面调用b方法
b();
}
//同步锁
public synchronized void b(){
System.out.println("b");
}
public static void main(String[] args) {
new Thread(new Runnable() { //我们需要实现一个接口
@Override
public void run() {
Demo d = new Demo(); //先实例化一个对象
d.a();
}
}).start();
}
}
package 自旋锁死锁重入锁;
import java.util.Random;
//我们再写一个例子
//多个线程执行完毕之后,打印一句话
public class Demo02 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始执行~");
try {
Thread.sleep(new Random().nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程执行完毕了~~");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始执行~");
try {
Thread.sleep(new Random().nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程执行完毕了~~");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始执行~");
try {
Thread.sleep(new Random().nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程执行完毕了~~");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始执行~");
try {
Thread.sleep(new Random().nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程执行完毕了~~");
}
}).start();
System.out.println("所有的线程执行完毕了~~");
}
}
package 自旋锁死锁重入锁;
import java.util.Random;
//我们再写一个例子
//多个线程执行完毕之后,打印一句话
public class Demo02 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始执行~");
try {
Thread.sleep(new Random().nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程执行完毕了~~");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始执行~");
try {
Thread.sleep(new Random().nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程执行完毕了~~");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始执行~");
try {
Thread.sleep(new Random().nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程执行完毕了~~");
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程开始执行~");
try {
Thread.sleep(new Random().nextInt(2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程执行完毕了~~");
}
}).start();
while(Thread.activeCount()==1) {//当前活动线程的数量
//活动线程只剩下主线程,我们就认为执行完毕了
System.out.println("所有的线程执行完毕了~~");
}
}
}
上图很不给面子,没有私有,那我们加点语句,写上一个睡眠的
package 自旋锁死锁重入锁;
public class Demo03 {
private Object obj1 = new Object();
private Object obj2 = new Object();
//方法a
public void a(){
synchronized (obj1){
synchronized (obj2){
System.out.println("Genius Team -- a");
}
}
}
//方法b
public void b(){
synchronized (obj2){
synchronized (obj1){
System.out.println("Genius Team -- b");
}
}
}
//死锁发生之后,在java虚拟机里面就死了~
public static void main(String[] args) {
Demo03 d = new Demo03();
new Thread(new Runnable() { //我们创建一个线程
@Override
public void run() {
d.a();
}
}).start();
new Thread(new Runnable() { //我们创建一个线程
@Override
public void run() {
d.b();
}
}).start();
}
}
package 自旋锁死锁重入锁;
public class Demo03 {
private Object obj1 = new Object();
private Object obj2 = new Object();
//方法a
public void a(){
synchronized (obj1){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj2){
System.out.println("Genius Team -- a");
}
}
}
//方法b
public void b(){
synchronized (obj2){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj1){
System.out.println("Genius Team -- b");
}
}
}
//死锁发生之后,在java虚拟机里面就死了~
public static void main(String[] args) {
Demo03 d = new Demo03();
new Thread(new Runnable() { //我们创建一个线程
@Override
public void run() {
d.a();
}
}).start();
new Thread(new Runnable() { //我们创建一个线程
@Override
public void run() {
d.b();
}
}).start();
}
}
volatile被称为轻量级锁
被其修饰的变量,在线程之间是可见的
夜光:
可见,一个线程修改了这个变量值,在另外一个线程可以读到这个修改后的值
synchronized 除了线程之间互斥以外
还有一个非常大的作用,就是保证变量的可见性
package 深入理解volatile原理与使用;
//保证可见性的前提
//多个线程拿到的是同一把锁,否则是保证不了的
public class Demo {
private int a = 1;
public int getA() {
return a;
}
public void setA(int a) {
try {
Thread.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.a = a;
}
//一个线程在get,另外一个线程在set,很大情况下会出现线程安全问题
public static void main(String[] args) {
//我们先new一个对象出来
Demo d = new Demo();
new Thread(new Runnable() {
@Override
public void run() {
d.setA(10);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(d.getA());
}
}).start();
}
}