1.volatile----轻量级的synchronized锁 ,锁的是数据,保证线程之间变量的可见性,简单地说就是当线程A对变量x进行了修改之后,在线程A后面执行的其他线程能看到变量x的变动,volatile保证了数据永远是最新的,更详细地说是要符合以下两个规则:
F.线程对变量进行修改之后,要立刻回写到主内存
S.线程对变量进行读取的时候,要从主内存中读取,而不是缓存
出于性能的考虑,当几个线程同时进行的时候,上一个线程修改的数据可能并没有写回到内存中进行修改,而这时下一个线程可能就需要读取到这个数据,那么这个数据便不是最新的数据,这时volatile便发挥了用处,它保证了线程数据的同步。
volatile是不错的机制,但是不能保证原子性:例如a++这个指令在计算机执行的时候会分成3条:首先取出a,然后执行a+1,最后存回到a,volatile不能保证这三条语句一起执行。
package cn.MulThreadOthers.java.mhz;
/**
* volatile用于保证线程数据的同步性,也就是可见性
* @author MHZ
*
*/
public class MyJavaMulThreadVolatile {
private volatile static int num = 0;
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
while(num==0) {
}
}).start();
Thread.sleep(1000);
num = 1;
}
}
2.DCL单例设计模式
package cn.MulThreadOthers.java.mhz;
/**
* DCL 单例设计模式
* 单例设计模式 :在多线程环境下,对外存在一个对象
* 1.构造器私有化 --- >避免外部new构造器
* 2.提供私有的静态属性--->存储对象的地址
* 3.提供公共的静态方法--->获取属性
* @author MHZ
*
*/
public class MyJavaDoubleCheckedLocked {
//1.构造器私有化
private MyJavaDoubleCheckedLocked()
{
}
//2.提供私有的静态属性
private static volatile MyJavaDoubleCheckedLocked instance;
//3.提供公共的静态方法--->获取属性
public static MyJavaDoubleCheckedLocked getinstance()
{
//再次检测 节省时间
if(null!=instance) {
return instance;
}
synchronized(MyJavaDoubleCheckedLocked.class)
{
if(instance==null) {
//开辟空间 初始化对象信息 将对象的地址返回给引用
//上面为new一个对象时所做的事
//new这个对象的时候,可能会出现指令重排现象,这是由于初始化对象信息比较慢,cpu可能会先执行第3步,这样会导致线程返回一个错误的引用
//使用轻量级的锁(数据锁)violate来避免指令重排带来的错误,形成各个线程的数据可见
instance = new MyJavaDoubleCheckedLocked();
}
}
return instance;
}
public static void main(String[] args) {
Thread t = new Thread(()->{
System.out.println(MyJavaDoubleCheckedLocked.getinstance());
});
t.start();
System.out.println(MyJavaDoubleCheckedLocked.getinstance());
}
}
3.可重入锁
如果对象试图获得一个已经由它自己持有的锁时,那么这个请求会立刻成功,并且将这个锁的计数值加1,当线程退出同步代码块的时候,计数值将会递减,当计数值为0时,锁释放;大多数系统内置锁都是可重入锁,可以延续使用
package cn.MulThreadOthers.java.mhz;
/**
* 如果对象试图获得一个已经由它自己持有的锁时,那么这个请求会立刻成功,并且将这个锁的计数值加1,当线程退出同步代码块的时候,计数值将会递减,当计数值为0时,锁释放
* 可重入锁,大多数内置锁都是可重入锁 锁可以重复使用
* @author MHZ
*
*/
public class LockTest {
public void test()
{
//第一次获得锁
synchronized(this) {
//第二次获得同样的锁,这就是可重入锁,可延续,调用自己的锁不会出现死锁
while (true) {
synchronized (this) {
System.out.println("ReLock");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public synchronized void a()
{
System.out.println("first");
}
public synchronized void b()
{
System.out.println("second");
}
public void all()
{
//当前对象已经获得锁
synchronized(this)
{
//如果当前对象已经获得锁,那么它试图获得一个由它自己持有的锁,这个请求会立刻被执行
this.a();
this.b();
}
}
public static void main(String[] args) {
//new LockTest().test();
LockTest l = new LockTest();
l.a();
l.b();
l.all();
}
}
手动实现的不可重入锁
package cn.MulThreadOthers.java.mhz;
/**
* 不可重入锁,会形成死锁 无法延续锁
* @author MHZ
*
*/
public class LockTest01 {
Lock lock = new Lock();
public void a() throws InterruptedException {
lock.lock();
doSomething();
lock.unlock();
}
//不可重入
public void doSomething() throws InterruptedException {
lock.lock();
//...................
lock.unlock();
}
public static void main(String[] args) throws InterruptedException {
LockTest01 test = new LockTest01();
test.a();
test.doSomething();
}
}
// 不可重入锁
class Lock{
//是否占用
private boolean isLocked = false;
//使用锁
public synchronized void lock() throws InterruptedException {
while(isLocked) {
wait();
}
isLocked = true;
}
//释放锁
public synchronized void unlock() {
isLocked = false;
notify();
}
}
手动实现的可重入锁,加入了计数器
package com.sxt.others;
/**
* 可重入锁: 锁可以延续使用 + 计数器
*
* @author MHZ
*
*/
public class LockTest02 {
ReLock lock = new ReLock();
public void a() throws InterruptedException {
lock.lock();
System.out.println(lock.getHoldCount());
doSomething();
lock.unlock();
System.out.println(lock.getHoldCount());
}
//可重入 可延续
public void doSomething() throws InterruptedException {
lock.lock();
System.out.println(lock.getHoldCount());
//...................
lock.unlock();
System.out.println(lock.getHoldCount());
}
public static void main(String[] args) throws InterruptedException {
LockTest02 test = new LockTest02();
test.a();
Thread.sleep(1000);
System.out.println(test.lock.getHoldCount());
}
}
// 可重入锁
class ReLock{
//是否占用
private boolean isLocked = false;
private Thread lockedBy = null; //存储线程
private int holdCount = 0; //计数器
//使用锁
public synchronized void lock() throws InterruptedException {
Thread t = Thread.currentThread();
while(isLocked && lockedBy != t) {
wait();
}
isLocked = true;
lockedBy = t;
holdCount ++;
}
//释放锁
public synchronized void unlock() {
if(Thread.currentThread() == lockedBy) {
holdCount --;
if(holdCount ==0) {
isLocked = false;
notify();
lockedBy = null;
}
}
}
public int getHoldCount() {
return holdCount;
}
}
ReentrantLock是系统自带的可重入锁,可以直接使用
package com.sxt.others;
import java.util.concurrent.locks.ReentrantLock;
/**
* 可重入锁 ReentrantLock
* @author MHZ
*
*/
public class ReenLock {
ReentrantLock lock = new ReentrantLock();
public void a() throws InterruptedException {
lock.lock();
System.out.println(lock.getHoldCount());
doSomething();
lock.unlock();
System.out.println(lock.getHoldCount());
}
//可重入 可延续
public void doSomething() throws InterruptedException {
lock.lock();
System.out.println(lock.getHoldCount());
//...................
lock.unlock();
System.out.println(lock.getHoldCount());
}
public static void main(String[] args) throws InterruptedException {
ReenLock test = new ReenLock();
test.a();
Thread.sleep(1000);
System.out.println(test.lock.getHoldCount());
}
}
4.CAS原子操作
CAS是实现乐观锁的一种方式,乐观锁是指每次不加锁而是假设不冲突去完成某项工作,如果发生冲突就失败,并一直重试,直到成功为止
有可能出现ABA问题
package com.sxt.others;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 比较并交换
* CAS原子操作 是实现乐观锁的一种方式:每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止
* 有可能出现ABA问题,ABA问题是指变量V初次读取的值是A,但是这不能说明一开始就是A,可能是别的线程修改成B再改成A,这时候CAS并不能知晓
* @author MHZ
*
*/
public class CAS {
//库存 原子操作都使用了CAS的思想
private static AtomicInteger stock = new AtomicInteger(5);
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
Integer left = stock.decrementAndGet();
//System.out.println((int)left);
new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//Integer left = stock.decrementAndGet();
//System.out.println(left);
if(left<1) {
System.out.println("抢完了...");
return;
}
System.out.print(Thread.currentThread().getName()+"抢了一个商品 ");
System.out.println("还剩下"+"--->"+left);
}).start();
}
}
}