二、锁

2018-08-21  本文已影响0人  小绵羊你毛不多

线程同步

锁 (对象监视器)

synchronized锁

  1. 是什么锁?
  1. 用处是什么?
  1. 怎么使用?
public class syncTest implements  Runnable {
    int count=0;
    //加锁
    @Override
    public synchronized void run() {
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName()+":"+count++);
        }
    }

    public static void main(String[] args) {
    //创建一个对象
        syncTest syncTest = new syncTest();
        //两个线程 一个对象
        Thread thread = new Thread(syncTest,"thread1");
        Thread thread2 = new Thread(syncTest,"thread2");
        thread.start();
        thread2.start();
    }
}
打印结果:
thread1:0
thread1:1
thread1:2
thread2:3
thread2:4
thread2:5
 public static void main(String[] args) {
 //同样的  如果是两个线程
        syncTest syncTest = new syncTest();
        syncTest syncTest2 = new syncTest();
        // 两个线程 两个对象
        Thread thread = new Thread(syncTest,"thread1");
        Thread thread2 = new Thread(syncTest2,"thread2");
        thread.start();
        thread2.start();
    }
result:
thread1:0
thread2:0
thread1:1
thread2:1
thread1:2
thread2:2
//不涉及到数据共享的情况  分别访问两个不同的对象

对于这个问题 我们可以用修饰动态静态方法 来解决

public class syncTest implements Runnable {
//静态变量
    static int count = 0;

    @Override
    public  void run() {
        count();
    }
//修饰静态方法
    private synchronized static void count() {
        for (int i = 0; i < 3; i++) {
            System.out.println(Thread.currentThread().getName() + ":" + count++);
        }
    }

    public static void main(String[] args) {
        syncTest syncTest = new syncTest();
        syncTest syncTest2 = new syncTest();
        Thread thread = new Thread(syncTest, "thread1");
        Thread thread2 = new Thread(syncTest2, "thread2");
        thread.start();
        thread2.start();
    }
}
打印结果:
thread2:0
thread2:1
thread2:2
thread1:3
thread1:4
thread1:5

// 虽然是两个对象实例,但是是count是正确的,也就是线程安全的。  但是thread1 2 不一定哪个先运行。
其他代码同上 

private void count() {
…… 省略
        synchronized (this) {
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName() + ":" + count++);
            }
        }
    }
输入结果:
thread1:0
thread2:0
thread2:2
thread1:1
thread2:3
thread1:4
//是线程不安全的  因为this 指的就是调用这个对象的实例,上面两个线程调用两个不同的实例

解决上面问题的办法就是

//把this改成当前类 
synchronized (syncTest.class) {
    
}
//或者任意一个类都可以  不建议使用
synchronized (Object.class) {
    
}
  1. 原理
    通过javap生成的class可以看见
image

sync锁存放结构图:

image

monitor运行机制图:

image

Lock

image
  1. Lock是一个接口,常用方法有
    • lock()
// 1.尝试获取锁,如果锁被其他线程获取,则处于等待状态
// 2.必须主动释放锁,发生异常时,也不会自动释放锁
// 3. 必须在 try catch中进行,在finally中释放锁,防止死锁发生
void lock(); 
可中断锁
//尝试获取锁,可以相应中断,可以中断线程的等待状态
void lockInterruptibly() throws InterruptedException;   
 
轮询锁
//1.尝试获取锁,获取锁成功则返回true,否则返回false  
//2.所以拿不到锁也会立即返回 不会等待
boolean tryLock();   

定时锁
//1. 可以设置等待时间
//2. 未获取锁之前被中断,则抛出异常   
boolean tryLock(long time, TimeUnit unit)  throws InterruptedException;   
//释放锁  一定要在finally块中释放
void unlock();   
//返回当前锁的条件变量,通过条件变量可以实现类似notify和wait的功能
//一个锁可以有多个条件变量  
Condition newCondition();

Condition中await()/signal()/signalAll()分别对应synchronize中的wait()/notify()/notifyAll()

  1. ReentrentLock
 Lock lock = new ReentrentLock();
 
if(lock.tryLock()) {
     try{
         //处理任务
     }catch(Exception ex){

     }finally{
         lock.unlock();   //释放锁
     } 
}else {
    //如果不能获取锁,则直接做其他事情
}

实现原理参考

synchronized与ReentrentLock对比

乐观的并发策略里,需要操作和冲突检测具备原子性,这里用到了CAS操作Compare and Swap ,ReentrantLock中实现非公平锁的时候用到了compareAndSetState,compareAndSet()叫做非阻塞算法,意思是一个线程的失败或者挂起不应该影响其他的线程

3.ReadWriteLock

// 是一个接口  只定义了两个方法
//读和写分成两个锁,从而实现多个线程可以同时进行读操作
public interface ReadWriteLock {
   
    Lock readLock();

    Lock writeLock();
}
  1. ReentrenReadWriteLock
//并为实现Lock接口  只实现了ReadWriteLock接口
public class ReentrantReadWriteLock
        implements ReadWriteLock, java.io.Serializable {
            
        }

多个线程进行【读】操作

public class RwLockTest {
    //创建锁
    private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();

    public void read(Thread thread) {
        //获取锁
        rwLock.readLock().lock();
        try {
            long start = System.currentTimeMillis();
            System.out.println("线程" + thread.getName() + "read start");
            while (System.currentTimeMillis() - start <= 1) {
                System.out.println("线程" + thread.getName() + "read ing ");
            }
            System.out.println("线程" + thread.getName() + "read end");
        } finally {
            //必须释放锁
            rwLock.readLock().unlock();
        }
    }

    public static void main(String[] args) {
        //必须用final  不然下面两个线程会创建两个对象副本
        final RwLockTest rwtEST = new RwLockTest();
        new Thread("AAA") {
            @Override
            public void run() {
                rwtEST.read(Thread.currentThread());
            }
        }.start();

        new Thread("BBB") {
            @Override
            public void run() {
                rwtEST.read(Thread.currentThread());
            }
        }.start();
        
    }
}

打印结果:
线程BBB  read start
线程AAA  read start
线程BBB  read ing 
线程AAA  read ing 
……
线程BBB  read ing 
线程AAA  read ing 
线程BBB  read ing 
线程AAA  read end
线程BBB  read end

//可以看到两个线程是同时读的 

【注意】

  1. 锁的概念
//method1、method2都用synchronized修饰,当一个线程获取到method1的锁,而需要调用method2时,不需要再次申请锁,可以直接访问。
//假设不具备可重入性,该线程获取到该对象的锁,又要申请该对象的锁,会造成死锁情况。
class MyClass {
    public synchronized void method1() {
        method2();
    }

    public synchronized void method2() {

    }
}
上一篇 下一篇

猜你喜欢

热点阅读