线程安全:
线程安全:
线程安全 – 加锁
注意:要想多个线程互斥住,就必须使用同一把锁(对象)!!!
加锁方式:
- synchronized
- Lock
synchronized
- 同步代码块
- 同步方法
同步代码块:
数据结构:
synchronized(锁对象){//自动上锁
...想要互斥的代码...
}//自动解锁
同步方法:
- 成员同步方法
- 静态同步方法
成员同步方法:
注意:锁对象 -> this
多个子线程时,调用的对象(this)不一样,则锁不住。
数据结构:
public synchronized void method(){//自动上锁
...想要互斥的代码...
}//自动解锁
静态同步方法:
注意:锁对象 -> 类.class
public static synchronized void method(){//自动上锁
...想要互斥的代码...
}//自动解锁
Lock:
//锁对象
Lock lock = new ReentrantLock();
lock.lock();//手动上锁
...想要互斥的代码...
lock.unlock();//手动解锁
应用:
public class MyThread extends Thread{
private static int allTicket = 1000;
private static int curTicket = 0;
private static Lock lock = new ReentrantLock();
public MyThread(String name) {
super(name);
}
@Override
public void run() {
while(curTicket < allTicket){
lock.lock();//手动上锁
try {
if(curTicket < allTicket){
curTicket++;
System.out.println("窗口" + Thread.currentThread().getName() + "正在销售第" + curTicket + "张票");
}
if(curTicket >= allTicket){
System.out.println("窗口" + Thread.currentThread().getName() + "票已经售完");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();//手动解锁
}
}
}
}
public static void main(String[] args) {
MyThread t1 = new MyThread("001");
MyThread t2 = new MyThread("002");
MyThread t3 = new MyThread("003");
t1.start();
t2.start();
t3.start();
}
单例模式:
该类的对象在整个项目中只创建一次(只实例化一次)。
懒汉式:
单例模式(懒汉式)不是线程安全的。
public class A {
//声明对象名
private static A a;
private A(){}
public static A getIntance(){
//判断对象为空,再创建对象
if(a == null){
a = new A();
}
return a;
}
}
public static void main(String[] args) {
A a1 = A.getIntance();
A a2 = A.getIntance();
A a3 = A.getIntance();
A a4 = A.getIntance();
//地址都是一样的,则是一个对象
System.out.println(a1);
System.out.println(a2);
System.out.println(a3);
System.out.println(a4);
}
饿汉式:
单例模式(饿汉式)是线程安全的。
public class A {
//先创建对象
private static A a = new A();
private A(){}
public static A getIntance(){
return a;
}
public static void method(){
System.out.println("用良心做教育");
}
}
public static void main(String[] args) {
A a1 = A.getIntance();
A a2 = A.getIntance();
A a3 = A.getIntance();
A a4 = A.getIntance();
System.out.println(a1);
System.out.println(a2);
System.out.println(a3);
System.out.println(a4);
}
缺点:如果只调用了类里的静态方法,没用到单例对象,就是浪费空间。
public static void main(String[] args) {
A.method();
}
枚举饿汉式:
枚举单例模式(饿汉式)是线程安全的。
public enum A {
//public static final A a = new A();
a;
private A(){}
public static A getIntance(){
return a;
}
public static void method(){
System.out.println("用良心做教育");
}
@Override
public String toString() {
return String.valueOf(a.hashCode());
}
}
public static void main(String[] args) {
A a1 = A.getIntance();
A a2 = A.getIntance();
A a3 = A.getIntance();
A a4 = A.getIntance();
System.out.println(a1);
System.out.println(a2);
System.out.println(a3);
System.out.println(a4);
}
缺点:如果只调用了枚举里的静态方法,没用到单例对象,就是浪费空间。
public static void main(String[] args) {
A.method();
}
双重检验锁:
项目中使用的单例模式------->双重检验锁。
双重检验锁的单例模式是线程安全的。
volatile – 防止指令重排
创建对象的过程: a.开辟空间 ----- new 对象() – 0x001
b.调用构造方法 – 初始化数据
c.将空间赋值给引用 – 类型 引用 = 0x001
创建对象的步骤:a/b/c 或 a/c/b
注意:如果创建对象的步骤是a/c/b,多线程的情况下可能会导致获取的属性为null
解决方案:使用volatile,防止指令重排,创建的步骤必须按照a/b/c
public class A {
private static volatile A a;
private A(){}
public static A getIntance(){
if(a == null){
synchronized (A.class) {
if(a == null){
a = new A();
}
}
}
return a;
}
// public static A getIntance(){
//
// if(a != null){
// return a;
// }
// synchronized (A.class) {
// if(a == null){
// a = new A();
// }
// }
// return a;
// }
}
public static void main(String[] args) {
A a1 = A.getIntance();
A a2 = A.getIntance();
A a3 = A.getIntance();
A a4 = A.getIntance();
System.out.println(a1);
System.out.println(a2);
System.out.println(a3);
System.out.println(a4);
}