同步锁重入锁,Java中的Synchronize,Lock,ReentrantLock,ReentrantReadWriteLock,Condition简单示例及说明

作者: admin 分类: JAVA 发布时间: 2019-06-22 13:10  阅读: 330 views

在大多数实际的多线程应用中,两个或两个以上的线程需要共享对同一数据的存取。如果两个线程存取相同的对象,并且每一个线程都调用了一个修改该对象状态的方法,会产生并发性问题。两个不同执行空间上的方法在堆上对同一个对象执行getter,setter请求,数据会错乱。

 

Java提供了synchronized同步关键字,1.5之后提供了lock接口及实现。

synchronized同步普通方法,锁的是当前对象。同步静态方法,锁的是当前 Class 对象。同步块,锁的是 {} 中的对象。相比于lock的劣势如下:

1. 它无法中断一个正在等候获得锁的线程
2. 无法通过投票得到锁,如果不等下去,就无法得到锁
3. 同步还要求所的释放只能在与获得锁所在的堆栈帧相同的堆栈帧中进行。

lock实现了比synchronized更广泛的锁操作。此实现允许更灵活的锁实现。不使用块结构锁就失去了自动释放锁的功能,所以必须在最后释放锁。常用的两个实现类为 reentrantLock(重入锁)、ReentrantReadWriteLock(重入读写锁)

下面是几个锁的简单实现及 Condition类的使用

Synchronize

package com.chl.lock;

import java.math.BigDecimal;

/**
 * java同步关键字
 * 同步普通方法,锁的是当前对象。同步静态方法,锁的是当前 Class 对象。同步块,锁的是 {} 中的对象。
 * 如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。 
 * @author chenhailong
 */
public class SynchronizeTest {
    
    public static void main(String[] args) {
        
        //第一部分,简单的线程执行过程控制
        
//      BlockAsync ba = new BlockAsync();
//      new Thread(ba).start();
//      new Thread(ba).start();
//
//      BlockSync bs = new BlockSync();
//      new Thread(bs).start();
//      new Thread(bs).start();
        
        //输出结果看,这里是无序的
//      currentThread:Thread-0---count:0
//      currentThread:Thread-1---count:1
//      currentThread:Thread-0---count:2
//      currentThread:Thread-1---count:3
//      currentThread:Thread-0---count:4
//      currentThread:Thread-1---count:5
//      currentThread:Thread-0---count:6
//      currentThread:Thread-1---count:7
//      currentThread:Thread-0---count:8
//      currentThread:Thread-1---count:9
        
        //这里是有序的
//      currentThread:Thread-2---count:0
//      currentThread:Thread-2---count:1
//      currentThread:Thread-2---count:2
//      currentThread:Thread-2---count:3
//      currentThread:Thread-2---count:4
//      currentThread:Thread-3---count:5
//      currentThread:Thread-3---count:6
//      currentThread:Thread-3---count:7
//      currentThread:Thread-3---count:8
//      currentThread:Thread-3---count:9

        
        //第二部分,模拟账户控制,  Synchronize的添加与否对结果影响很大
        Account a = new Account("小明",new BigDecimal(1000));
        AccountOperation ao = new AccountOperation(a);
        for(int i = 0;i<5;i++) {
            new Thread(ao).start();
        }
    }
}

/**
 * 异步的执行线程
 */
class BlockAsync implements Runnable{
    private static int count ;
    
    BlockAsync(){
        count = 0;
    }

    @Override
    public void run() {
        for(int i = 0;i<5;i++) {
            System.out.println("currentThread:"+Thread.currentThread().getName()+"---count:"+count++);
        }
    }
    
}

/**
 * 同步的执行线程
 */
class BlockSync implements Runnable{
    private static int count ;
    
    BlockSync(){
        count = 0;
    }

    @Override
    public void run() {
        synchronized (this) {
            for(int i = 0;i<5;i++) {
                System.out.println("currentThread:"+Thread.currentThread().getName()+"---count:"+count++);
            }
        }
    }
    
}




/**
 * 设定一个账户类,统一的操作方法
 */
class Account {
    String name;
    BigDecimal money;
    
    Account(String name,BigDecimal money){
        this.name = name;
        this.money = money;
        System.out.println("小明一开始有"+money+"元");
    }
    
    void Get(BigDecimal get) {
        money = money.subtract(get);
        System.out.println("小明取钱"+get+"元");
    }
    
    void Put(BigDecimal put) {
        money = money.add(put);
        System.out.println("小明存钱"+put+"元");
    }
    
    BigDecimal getMoney() {
        return money;
    }
}

/**
 * 单独的线程操作每个账户
 */
class AccountOperation implements Runnable{
    
    private Account accout;
    
    AccountOperation(Account account){
        accout = account;
    }

    @Override
    public synchronized void run() {
        accout.Get(new BigDecimal(150));
        accout.Put(new BigDecimal(200));
        System.out.println("小明现在还有 "+ accout.getMoney()+"钱");
    }
}

Lock

package com.chl.lock;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * lock: java锁对象,实现了比synchronized更广泛的锁操作
 * lock是一个接口,只有 ReentrantLock
 * @author chenhailong
 */
public class LockTest {

    public static void main(String[] args) {
        
        //无锁的场景设置
        
//      NormalQueue nq =  new NormalQueue();
//      JumpAQueue jaq =  new JumpAQueue();
//      
//      new Thread(nq).start();
//      try {
//          Thread.sleep(1 * 1000l);
//      } catch (InterruptedException e) {
//          e.printStackTrace();
//      }
//      new Thread(jaq).start();
        
        //有锁的场景设置
        
        Lock lock = new ReentrantLock();
        LockQueue lq = new LockQueue(lock);
        CutInLine cil = new CutInLine(lock);
        
        new Thread(lq).start();
        try {
            Thread.sleep(1 * 1000l);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(cil).start();
    }

}


/**
 * 一个正常的线程
 */
class NormalQueue implements Runnable{
    @Override
    public void run() {
        System.out.println("小明正在排队入场,需要等待10s!");
        try {
            for(int i = 0;i<10;i++) {
                Thread.sleep(1 * 1000l);
                System.out.println("还剩"+(9-i)+"秒");
            }
            System.out.println("小明正式入场!");
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}

/**
 * 一个插队队列
 */
class JumpAQueue implements Runnable{

    @Override
    public void run() {
        System.out.println("小王想插队!");
        try {
            Thread.sleep(1 * 1000l);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("小王插队成功!,小明多等了1s");
    }
}

/**
 * 一个带锁的线程队列
 */
class LockQueue implements Runnable {

    Lock lock;
    
    LockQueue(Lock lock){
        this.lock = lock;
    }
    
    @Override
    public void run() {
        System.out.println("小丽正在排队上厕所,需要等待10s!");
        try {
            
            /**
             * 如果当前线程未被中断,则获取锁;如果锁可用,则获取锁并立即返回;
             * 如果锁不可用,则禁用当前线程,并且处于以下情况处休眠种
             * 【当前线程获得锁,其他线程中断当前线程,并支持对锁获取的中断。
             * */
//          lock.lockInterruptibly();
            
            lock.lock();
            for(int i = 0;i<10;i++) {
                Thread.sleep(1 * 1000l);
                System.out.println("还剩"+(9-i)+"秒");
            }
            System.out.println("小丽进去了!");
            lock.unlock();
            System.out.println("资源释放了!");
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
    
}

/**
 * 一个线程尝试获取锁
 */
class CutInLine implements Runnable{

    Lock lock;
    
    CutInLine(Lock lock){
        this.lock = lock;
    }
    
    @Override
    public void run() {
        try {
            System.out.println("小红想插队!");
            for(;;) {
                if(!lock.tryLock()) {
                    Thread.sleep(1 * 1000l);
                    System.out.println("小红问小丽可以插队吗?小丽说no!");
                }else {
                    break;
                }
            }
            System.out.println("小红尿湿了裤子!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

ReentrantLock

package com.chl.lock;

import java.util.concurrent.locks.ReentrantLock;

/**
 * java中唯一实现 Lock接口的类,可以查看当前线程持有锁数量和 队列中等待拿锁的长度
 * @author chenhailong
 */
public class ReentrantLockTest {

    public static void main(String[] args) {
        ReentrantLock rtl = new ReentrantLock();
        FlowTime ft = new FlowTime(rtl);
        FlowQueue fq = new FlowQueue(rtl);
        new Thread(ft).start();
        new Thread(fq).start();
    }

}

class FlowTime implements Runnable {
    private ReentrantLock rtl;

    FlowTime(ReentrantLock rtl) {
        this.rtl = rtl;
    }

    @Override
    public void run() {
        try {
            rtl.lock();
            Thread.sleep(1 * 1000l);
            System.out.println("当前FT" + Thread.currentThread().getName() + "持有该锁的数量" + rtl.getHoldCount() + "--queue长度"
                    + (rtl.getQueueLength() > 0 ? rtl.getQueueLength() + "--FT在等待" : 0));
        } catch (Exception e) {
            e.printStackTrace();
        }
        rtl.unlock();
    }

}

class FlowQueue implements Runnable {
    private ReentrantLock rtl;

    FlowQueue(ReentrantLock rtl) {
        this.rtl = rtl;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(1 * 1000l);
            rtl.lock();
            int count = rtl.getHoldCount();
            System.out.println("当前FQ" + Thread.currentThread().getName() + "持有该锁的数量" + count + "--queue长度"
                    + (rtl.getQueueLength() > 0 ? rtl.getQueueLength() + "--FQ在等待" : 0));
        } catch (Exception e) {
            e.printStackTrace();
        }
        rtl.unlock();
    }

}

ReentrantReadWriteLock

package com.chl.lock;

import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

/**
 * ReadWriteLock接口,获取读锁和写锁
 * 写操作时,读锁要释放
 * @author chenhailong
 */
public class ReentrantReadWriteLockTest {

    public static void main(String[] args) {

        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        WriteLock wl = lock.writeLock();
        ReadLock rl = lock.readLock();

        WriteBus wb = new WriteBus(wl);
        ReadBus rb = new ReadBus(rl);
        new Thread(wb).start();
        new Thread(rb).start();
    }

}

class WriteBus implements Runnable {
    private WriteLock wl;

    WriteBus(WriteLock wl) {
        this.wl = wl;
    }

    @Override
    public void run() {
        try {
            for (;;) {
                Thread.sleep(3 * 1000l);
                wl.lock();
                System.out.println("开启写锁,持续1s");
                Thread.sleep(1 * 1000l);
                wl.unlock();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

class ReadBus implements Runnable {
    private ReadLock rl;

    ReadBus(ReadLock rl) {
        this.rl = rl;
    }

    @Override
    public void run() {
        try {
            for (;;) {
                rl.lock();
                Thread.sleep(1000l);
                System.out.println("开启读锁,一直读取");
                rl.unlock();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

Condition

package com.chl.lock;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 它用来替代传统的Object的wait()、notify()实现线程间的协作,使用Condition的await()、signal()这种方式实现线程间协作更加安全和高效。
 * @author chenhailong
 */
public class ConditionTest {
    Lock lock = new ReentrantLock();
    Condition con = lock.newCondition();

    public static void main(String[] args) {

        ConditionTest ct = new ConditionTest();
        new Thread(ct.new PlanA()).start();
        new Thread(ct.new PlanB()).start();
    }
    
    class PlanA implements Runnable{

        @Override
        public void run() {
            try {
                lock.lock();
                System.out.println("planA:waiting for planB finished!");
                con.await();
            }catch(Exception e) {
                e.printStackTrace();
            }finally {
                System.out.println("planA:ok, finished it over!");
                lock.unlock();
            };
        }
    }
    
    class PlanB implements Runnable{

        @Override
        public void run() {
            try {
                lock.lock();
                System.out.println("planB:i need finish this work quickly, and push the entire plan!");
                con.signalAll();
            }catch(Exception e) {
                e.printStackTrace();
            }finally {
                System.out.println("planB:i did it ");
                lock.unlock();
            }
        }
    }
    
}

 

下面说下死锁产生的条件

死锁:集合中的每一个进程都在等待只能由本集合中的其他进程才能引发的事件,那么该组进程是死锁的。

产生条件:

互斥条件 指进程所分配到的资源进行排他性使用,即一段时间内资源只被一个进程占用。如果其他进程请求资源,只能等待,直到占有进程用毕释放。
请求和保持条件 进程至少已经占有一个资源,又提出新的资源请求,新资源被其他资源占有。此时请求进程阻塞,但又对自己获得的其他资源保持不放。
不剥夺条件 进程已经获得的资源,在未使用完之前,不能被剥夺,只能自己使用完之后释放。
环路等待条件 指发生死锁时,必然存在一个进程-资源的环形链。即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。

产生原因:竞争资源引起进程死锁。

避免方法:有序的进行资源分配。

Synchronized锁的原理请看:https://www.deathearth.com/1285.html


   原创文章,转载请标明本文链接: 同步锁重入锁,Java中的Synchronize,Lock,ReentrantLock,ReentrantReadWriteLock,Condition简单示例及说明

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

更多阅读