深入理解Java多线程(四)

关于java多线程的概念以及基本用法:java多线程基础

4,Lock的使用

  • ReentrantLook类的使用
  • ReentrantReadWriteLock类的使用
4.1,ReentrantLook类的使用

新建MyService类:

public class MyService {

    private Lock lock = new ReentrantLock();
    public void testMethod() {
        lock.lock();//获取锁
        for(int i=0;i<5;i++) {
            System.out.println("ThreadName="+Thread.currentThread().getName()+(" "+(i+1)));
        }
        lock.unlock();//解锁
    }
}

MyThread类:

public class MyThread extends Thread{

    private MyService service;
    public MyThread(MyService service) {
        super();
        this.service = service;
    }
    @Override
    public void run() {
        service.testMethod();
    }

}

测试类:

public class Run {

    public static void main(String[] args) {
        MyService service = new MyService();
        MyThread a1 = new MyThread(service);
        MyThread a2 = new MyThread(service);
        MyThread a3 = new MyThread(service);
        MyThread a4 = new MyThread(service);
        MyThread a5 = new MyThread(service);
        a1.start();
        a2.start();
        a3.start();
        a4.start();
        a5.start();
    }
}

结果:

ThreadName=Thread-1 1
ThreadName=Thread-1 2
ThreadName=Thread-1 3
ThreadName=Thread-1 4
ThreadName=Thread-1 5
ThreadName=Thread-3 1
ThreadName=Thread-3 2
ThreadName=Thread-3 3
ThreadName=Thread-3 4
ThreadName=Thread-3 5
ThreadName=Thread-2 1
ThreadName=Thread-2 2
ThreadName=Thread-2 3
ThreadName=Thread-2 4
ThreadName=Thread-2 5
ThreadName=Thread-0 1
ThreadName=Thread-0 2
ThreadName=Thread-0 3
ThreadName=Thread-0 4
ThreadName=Thread-0 5
ThreadName=Thread-4 1
ThreadName=Thread-4 2
ThreadName=Thread-4 3
ThreadName=Thread-4 4
ThreadName=Thread-4 5

由结果可以看到一个线程打印完将锁释放,其他线程才可以继续打印,但是线程间的顺序是随机的

4.2,Condition实现等待/通知

关键字synchronized与wait()和notify()/notifyAll()方法结合可以实现等待/通知模式,ReentrantLock借助Condition对象可以实现相同的功能。一个Lock对象里面可以创建多个Condition(即对象监视器)实例,线程对象可以注册在指定的Condition中,从而可以自由选择线程通知,使用notify()/notifyAll()方法进行通知时,被通知的线程是由JVM随机选择的

新建MyService类:

public class MyService {

    private Lock lock = new ReentrantLock();
    public Condition condition = lock.newCondition();

    public void await() {
        try {
            lock.lock();
            System.out.println("wait 时间为:"+System.currentTimeMillis());
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void signal() {
        try {
            lock.lock();
            System.out.println("signal时间为:"+System.currentTimeMillis());
            condition.signal();
        } finally{
            lock.unlock();
        }
    }
}

ThreadA类:

public class ThreadA extends Thread{

    private MyService service;

    public ThreadA(MyService service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.await();
    }

}

测试类:

public class Run {

    public static void main(String[] args) throws InterruptedException {
        MyService service = new MyService();
        ThreadA a = new ThreadA(service);
        a.start();
        Thread.sleep(3000);
        service.signal();
    }
}

结果:

扫描二维码关注公众号,回复: 4274554 查看本文章
wait 时间为:1534645646731
signal时间为:1534645649731

Objectl类中wait()方法相当于Condition类中的await()方法
Objectl类中notify()方法相当于Condition类中的signal()方法

4.3,多个Condition实现通知部分线程

新建MyService类:

public class MyService {

    private Lock lock = new ReentrantLock();
    public Condition conditionA = lock.newCondition();
    public Condition conditionB = lock.newCondition();

    public void awaitA() {
        try {
            lock.lock();
            System.out.println("begin awaitA时间为:"+System.currentTimeMillis()
            +"ThreadName="+Thread.currentThread().getName());
            conditionA.await();
            System.out.println("end awaitA时间为:"+System.currentTimeMillis()
            +"ThreadName="+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void awaitB() {
        try {
            lock.lock();
            System.out.println("begin awaitB时间为:"+System.currentTimeMillis()
            +"ThreadName="+Thread.currentThread().getName());
            conditionB.await();
            System.out.println("end awaitB时间为:"+System.currentTimeMillis()
            +"ThreadName="+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void signalAll_A() {
        try {
            lock.lock();
            System.out.println("signalAll_A时间为:"+System.currentTimeMillis()
            +"ThreadName="+Thread.currentThread().getName());
            conditionA.signalAll();
        } finally{
            lock.unlock();
        }
    }

    public void signalAll_B() {
        try {
            lock.lock();
            System.out.println("signalAll_B时间为:"+System.currentTimeMillis()
            +"ThreadName="+Thread.currentThread().getName());
            conditionB.signalAll();
        } finally{
            lock.unlock();
        }
    }

}

两个线程类:

public class ThreadA extends Thread{

    private MyService service;

    public ThreadA(MyService service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.awaitA();
    }

}
public class ThreadB extends Thread {
    private MyService service;

    public ThreadB(MyService service) {
        super();
        this.service = service;
    }

    @Override
    public void run() {
        service.awaitB();
    }
}

测试类:

public class Run {

    public static void main(String[] args) throws InterruptedException{
        MyService service = new MyService();
        ThreadA a = new ThreadA(service);
        a.setName("A");
        a.start();
        ThreadB b= new ThreadB(service);
        b.setName("B");
        b.start();
        Thread.sleep(3000);
        service.signalAll_A();
    }
}

结果:

begin awaitA时间为:1534648259058ThreadName=A
begin awaitB时间为:1534648259058ThreadName=B
signalAll_A时间为:1534648262059ThreadName=main
end awaitA时间为:1534648262059ThreadName=A
4.4,公平锁与非公平锁

锁Lock分为公平锁与非公平锁,公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,非公平锁是一种获取锁的抢占机制,是随机获得锁的

新建Service类:

public class Service {

    private ReentrantLock lock;

    public Service(boolean isFair) {
        super();
        lock = new ReentrantLock(isFair);
    }
    public void serviceMethod() {
        try {
            lock.lock();
            System.out.println("ThreadName="+Thread.currentThread().getName()+"获得锁定");
        }finally{
            lock.unlock();
        }
    }

}

RunFair类:

public class RunFair {

    public static void main(String[] args) throws InterruptedException{
        final Service service = new Service(true);
        Runnable runnable = new Runnable() {

            @Override
            public void run() {
                System.out.println("*线程"+Thread.currentThread().getName()+"运行了");
                service.serviceMethod();

            }
        };
        Thread[] threadArray = new Thread[10];
        for(int i=0;i<10;i++) {
            threadArray[i] = new Thread(runnable);
        }
        for(int i=0;i<10;i++) {
            threadArray[i].start();
        }
    }
}

RunNoFair类:

public class RunNoFair {

    public static void main(String[] args) throws InterruptedException{
        final Service service = new Service(false);
        Runnable runnable = new Runnable() {

            @Override
            public void run() {
                System.out.println("*线程"+Thread.currentThread().getName()+"运行了");
                service.serviceMethod();

            }
        };
        Thread[] threadArray = new Thread[10];
        for(int i=0;i<10;i++) {
            threadArray[i] = new Thread(runnable);
        }
        for(int i=0;i<10;i++) {
            threadArray[i].start();
        }
    }
}

运行结果:

RunFair类:基本有序
RunNoFair类:基本无序
4.5,lock的一些方法
  • 方法int getHoldCount()的作用是查询当前线程保持此锁定的个数,也就是锁的个数
  • 方法int getQueueLength()的作用是返回正等待获取此锁定的线程估计数,也就是等待锁的线程
  • 方法int getWaitQueueLength(Condition condition)的作用是返回等待与此锁定相关的给定条件condition的线程估计数,也就是执行了同一个condition对象的await()方法的线程数

  • 方法boolean hasQueuedThread(Thread thread)的作用是查询指定的线程是否正在等待获取此线程
  • 方法 boolean hasQueuedThreads()的作用是查询是否有线程正在等待获取此锁定
  • 方法boolean hasWaiters(Condition condition)的作用是查询是否有线程正在等待与此锁定有关的condition条件

  • 方法boolean isFair()的作用是判断是不是公平锁
  • 方法boolean isHeldByCurrentThread()的作用是查询当前线程是否保持此锁定
  • 方法boolean isLocked()的作用是查询此锁定是否由任意线程保持

  • 方法 void lockInterruptibly()的作用是:如果当前线程未被中断,则获取锁定,否则出现异常
  • 方法boolean tryLock()的作用是:仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定
  • 方法 boolean tryLock(long timeout,TimeUnit unit)的作用是,如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定

使用Lock+Condition实现线程顺序执行:点这里

4.6,ReentranReadWriteLock类

同一时间只有一个线程在执行ReentranLock.lock()方法后面的任务,这样确实保证了线程的安全,但是效率很低,ReentranReadWriteLock类可以提高效率
读写锁有两个锁,一个读操作相关的锁,也称为共享锁,一个写操作相关的锁,也称为排他锁。读锁与写锁互斥,写锁与写锁互斥,但是读锁与读锁不互斥,也就是没写操作的情况下可以进行很多的读操作

1,读读共享
Service类:

public class Service {

    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void read() {
        try {
            try {
                lock.readLock().lock();
                System.out.println("获得锁"+Thread.currentThread().getName()
                        +" "+System.currentTimeMillis());
                Thread.sleep(10000);
            } finally {
                lock.readLock().unlock();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

两个线程类:

public class ThreadA extends Thread {

    private Service service;
    public ThreadA(Service service) {
        super();
        this.service = service;
    }

    public void run() {
        service.read();
    }
}
public class ThreadB extends Thread {

    private Service service;
    public ThreadB(Service service) {
        super();
        this.service = service;
    }

    public void run() {
        service.read();
    }
}

测试类:

public class Run {

    public static void main(String[] args) {
        Service service = new Service();
        ThreadA a = new ThreadA(service);
        a.setName("A");
        ThreadB b= new ThreadB(service);
        b.setName("B");
        a.start();
        b.start();
    }
}

结果:

获得锁B 1534681602545
获得锁A 1534681602545

由结果可以看到,两个线程几乎同时进入lock()后的代码

2,写写互斥

保持上面代码不变,更改Service类:

public class Service {

    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void write() {
        try {
            try {
                lock.writeLock().lock();
                System.out.println("获得写锁"+Thread.currentThread().getName()
                        +" "+System.currentTimeMillis());
                Thread.sleep(10000);
            } finally {
                lock.writeLock().unlock();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果:

获得写锁A 1534681950514
获得写锁B 1534681960516

显然写操作同一时间只允许一个线程执行

3,读写互斥
更改Service类和测试类:

public class Service {

    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    public void read() {
        try {
            try {
                lock.readLock().lock();
                System.out.println("获得锁"+Thread.currentThread().getName()
                        +" "+System.currentTimeMillis());
                Thread.sleep(10000);
            } finally {
                lock.readLock().unlock();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public void write() {
        try {
            try {
                lock.writeLock().lock();
                System.out.println("获得写锁"+Thread.currentThread().getName()
                        +" "+System.currentTimeMillis());
                Thread.sleep(10000);
            } finally {
                lock.writeLock().unlock();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public class Run {

    public static void main(String[] args) throws InterruptedException {
        Service service = new Service();
        ThreadA a = new ThreadA(service);
        a.setName("A");
        a.start();
        Thread.sleep(1000);
        ThreadB b= new ThreadB(service);
        b.setName("B");
        b.start();
    }
}

结果:

获得锁A 1534682422325
获得锁B 1534682423326

4,写读互斥
更改测试类;

public class Run {

    public static void main(String[] args) throws InterruptedException {
        Service service = new Service();

        ThreadB b= new ThreadB(service);
        b.setName("B");
        b.start();
        Thread.sleep(1000);
        ThreadA a = new ThreadA(service);
        a.setName("A");
        a.start();

    }
}

结果:

获得锁B 1534682527683
获得锁A 1534682528683

综上:只有读读操作是异步且非互斥的,其他的都是互斥的

猜你喜欢

转载自blog.csdn.net/qq_37438740/article/details/81835076