Java 进阶7 并发优化 5 并发控制板方法

图片 1

Java 中的锁通常分为两种:

  • 通过关键字 synchronized
    获取的锁,我们称为同步锁,上一篇有介绍到:Java 多线程并发编程
    Synchronized
    关键字。
  • java.util.concurrent(JUC)包里的锁,如通过继承接口 Lock 而实现的
    ReentrantLock(互斥锁),继承 ReadWriteLock 实现的
    ReentrantReadWriteLock(读写锁)。

本篇主要介绍 ReentrantLock(互斥锁)。

Java 进阶7 并发优化 5 并发控制板方法 20131114

Lock 的使用

ReentrantLock(互斥锁)

ReentrantLock
互斥锁,在同一时间只能被一个线程所占有,在被持有后并未释放之前,其他线程若想获得该锁只能等待或放弃。

ReentrantLock 互斥锁是可重入锁,即某一线程可多次获得该锁。

前言:

使用 ReentrantLock 类

lock():获取锁

unlock():释放锁

效果和 synchronized 关键字一样。

公平锁 and 非公平锁

    public ReentrantLock() {
        sync = new NonfairSync();
    }

    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

由 ReentrantLock 的构造函数可见,在实例化 ReentrantLock
的时候我们可以选择实例化一个公平锁或非公平锁,而默认会构造一个非公平锁。

公平锁与非公平锁区别在于竞争锁时的有序与否。公平锁可确保有序性(FIFO
队列),非公平锁不能确保有序性(即使也有 FIFO 队列)。

然而,公平是要付出代价的,公平锁比非公平锁要耗性能,所以在非必须确保公平的条件下,一般使用非公平锁可提高吞吐率。所以
ReentrantLock 默认的构造函数也是“不公平”的。

         Java 中多线程并发程序中存在线程安全的问题,之前学习 Java的同步机制,掌握的同步方法只有一种就是使用 synchronized关键字,解决线程之间的同步问题。同时在操作系统 C++多线程中也了解到其他的多线程同步机制:比如信号量、临界区、互斥锁等等

使用 Condition 实现等待/通知

  • Object.wait() == Condition.await()
  • Object.wait(long timeout) == Condition.await(long time,TimeUnit
    unit)
  • Object.notify() == Condition.signal()
  • Object.notifyAll == Condition.signalAll()

以上的方法,必须在调用 lock.lock() 方法获取同步监视器之后调用。

如何实现通知部分线程:使用多个 Condition,各自通知自己。

一般使用

DEMO1:

public class Test {

    private static class Counter {

        private ReentrantLock mReentrantLock = new ReentrantLock();

        public void count() {
            mReentrantLock.lock();
            try {
                for (int i = 0; i < 6; i++) {
                    System.out.println(Thread.currentThread().getName() + ", i = " + i);
                }
            } finally {
                // 必须在 finally 释放锁
                mReentrantLock.unlock();
            }
        }
    }

    private static class MyThread extends Thread {

        private Counter mCounter;

        public MyThread(Counter counter) {
            mCounter = counter;
        }

        @Override
        public void run() {
            super.run();
            mCounter.count();
        }
    }

    public static void main(String[] var0) {
        Counter counter = new Counter();
        // 注:myThread1 和 myThread2 是调用同一个对象 counter
        MyThread myThread1 = new MyThread(counter);
        MyThread myThread2 = new MyThread(counter);
        myThread1.start();
        myThread2.start();
    }
}

DEMO1 输出:

Thread-0, i = 0
Thread-0, i = 1
Thread-0, i = 2
Thread-0, i = 3
Thread-0, i = 4
Thread-0, i = 5
Thread-1, i = 0
Thread-1, i = 1
Thread-1, i = 2
Thread-1, i = 3
Thread-1, i = 4
Thread-1, i = 5

DEMO1 仅使用了 ReentrantLock 的 lock 和 unlock
来提现一般锁的特性,确保线程的有序执行。此种场景 synchronized 也适用。

          在这里系统的整理一下在 Java中实现线程同步的机制:内部锁,重入锁,读写锁,信号量等等。

实现生产者/消费者模式:一对一交替打印

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

public class MyService {
    private ReentrantLock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    private boolean hasValue = false;
    public void set(){
        try {
            lock.lock();
            while(hasValue){
                condition.await();
            }
            System.out.println("★");
            hasValue = true;
            condition.signal();
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    public void get(){
        try {
            lock.lock();
            while(!hasValue){
                condition.await();
            }
            System.out.println("☆");
            hasValue = false;
            condition.signal();
        }catch (InterruptedException e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

public class MyThreadA implements Runnable {

    private MyService myService;

    public MyThreadA(MyService myService) {
        super();
        this.myService = myService;
    }

    @Override
    public void run() {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            myService.set();
        }
    }
}

public class MyThreadB implements Runnable {

    private MyService myService;

    public MyThreadB(MyService myService) {
        super();
        this.myService = myService;
    }

    @Override
    public void run() {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            myService.get();
        }
    }
}

public class Run {
    public static void main(String[] args) {
        MyService myService = new MyService();
        MyThreadA myThreadA = new MyThreadA(myService);
        new Thread(myThreadA).start();
        MyThreadB myThreadB = new MyThreadB(myService);
        new Thread(myThreadB).start();
    }
}

锁的作用域

DEMO2:

public class Test {

    private static class Counter {

        private ReentrantLock mReentrantLock = new ReentrantLock();

        public void count() {
            for (int i = 0; i < 6; i++) {
                mReentrantLock.lock();
                // 模拟耗时,突出线程是否阻塞
                try{
                    Thread.sleep(100);
                    System.out.println(Thread.currentThread().getName() + ", i = " + i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 必须在 finally 释放锁
                    mReentrantLock.unlock();
                }
            }
        }

        public void doOtherThing(){
            for (int i = 0; i < 6; i++) {
                // 模拟耗时,突出线程是否阻塞
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " doOtherThing, i = " + i);
            }
        }
    }

    public static void main(String[] var0) {
        final Counter counter = new Counter();
        new Thread(new Runnable() {
            @Override
            public void run() {
                counter.count();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                counter.doOtherThing();
            }
        }).start();
    }
}

DEMO2 输出:

Thread-0, i = 0
Thread-1 doOtherThing, i = 0
Thread-0, i = 1
Thread-1 doOtherThing, i = 1
Thread-0, i = 2
Thread-1 doOtherThing, i = 2
Thread-0, i = 3
Thread-1 doOtherThing, i = 3
Thread-0, i = 4
Thread-1 doOtherThing, i = 4
Thread-0, i = 5
Thread-1 doOtherThing, i = 5

DEMO3:

public class Test {

    private static class Counter {

        private ReentrantLock mReentrantLock = new ReentrantLock();

        public void count() {
            for (int i = 0; i < 6; i++) {
                mReentrantLock.lock();
                // 模拟耗时,突出线程是否阻塞
                try{
                    Thread.sleep(100);
                    System.out.println(Thread.currentThread().getName() + ", i = " + i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 必须在 finally 释放锁
                    mReentrantLock.unlock();
                }
            }
        }

        public void doOtherThing(){
            mReentrantLock.lock();
            try{
                for (int i = 0; i < 6; i++) {
                    // 模拟耗时,突出线程是否阻塞
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " doOtherThing, i = " + i);
                }
            }finally {
                mReentrantLock.unlock();
            }

        }
    }

    public static void main(String[] var0) {
        final Counter counter = new Counter();
        new Thread(new Runnable() {
            @Override
            public void run() {
                counter.count();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                counter.doOtherThing();
            }
        }).start();
    }
}

DEMO3 输出:

Thread-0, i = 0
Thread-0, i = 1
Thread-0, i = 2
Thread-0, i = 3
Thread-0, i = 4
Thread-0, i = 5
Thread-1 doOtherThing, i = 0
Thread-1 doOtherThing, i = 1
Thread-1 doOtherThing, i = 2
Thread-1 doOtherThing, i = 3
Thread-1 doOtherThing, i = 4
Thread-1 doOtherThing, i = 5

结合 DEMO2 和 DEMO3 输出可见,锁的作用域在于
mReentrantLock,因为所来自于 mReentrantLock。

volatile 关键字,保证读写的共享对象时在共享内存中的,而不是线程中的副本,但是不保证线程安全。

公平锁与非公平锁

公平锁:线程获取锁的顺序是按照线程加载的顺序来分配的(先来先得
FIFO)

非公平锁:一种获取锁的抢占机制,是随机获得锁的

  • new ReentrantLock(true); // 公平锁
  • new ReentrantLock(false); // 非公平锁(默认这种,空构造函数)

可终止等待

DEMO4:

public class Test {

    static final int TIMEOUT = 300;

    private static class Counter {

        private ReentrantLock mReentrantLock = new ReentrantLock();

        public void count() {
            try{
                //lock() 不可中断
                mReentrantLock.lock();
                // 模拟耗时,突出线程是否阻塞
                for (int i = 0; i < 6; i++) {
                    long startTime = System.currentTimeMillis();
                    while (true) {
                        if (System.currentTimeMillis() - startTime > 100)
                            break;
                    }
                    System.out.println(Thread.currentThread().getName() + ", i = " + i);
                }
            } finally {
                // 必须在 finally 释放锁
                mReentrantLock.unlock();
            }
        }

        public void doOtherThing(){
            try{
                //lockInterruptibly() 可中断,若线程没有中断,则获取锁
                mReentrantLock.lockInterruptibly();
                for (int i = 0; i < 6; i++) {
                    // 模拟耗时,突出线程是否阻塞
                    long startTime = System.currentTimeMillis();
                    while (true) {
                        if (System.currentTimeMillis() - startTime > 100)
                            break;
                    }
                    System.out.println(Thread.currentThread().getName() + " doOtherThing, i = " + i);
                }
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName() + " 中断 ");
            }finally {
                // 若当前线程持有锁,则释放
                if(mReentrantLock.isHeldByCurrentThread()){
                    mReentrantLock.unlock();
                }
            }
        }
    }

    public static void main(String[] var0) {
        final Counter counter = new Counter();
        new Thread(new Runnable() {
            @Override
            public void run() {
                counter.count();
            }
        }).start();
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                counter.doOtherThing();
            }
        });
        thread2.start();
        long start = System.currentTimeMillis();
        while (true){
            if (System.currentTimeMillis() - start > TIMEOUT) {
                // 若线程还在运行,尝试中断
                if(thread2.isAlive()){
                    System.out.println(" 不等了,尝试中断 ");
                    thread2.interrupt();
                }
                break;
            }
        }
    }
}

DEMO4 输出:

Thread-0, i = 0
Thread-0, i = 1
Thread-0, i = 2
不等了,尝试中断
Thread-1 中断
Thread-0, i = 3
Thread-0, i = 4
Thread-0, i = 5

线程 thread2 等待 300ms 后 timeout,中断等待成功。

若把 TIMEOUT 改成 3000ms,输出结果:(正常运行)

Thread-0, i = 0
Thread-0, i = 1
Thread-0, i = 2
Thread-0, i = 3
Thread-0, i = 4
Thread-0, i = 5
Thread-1 doOtherThing, i = 0
Thread-1 doOtherThing, i = 1
Thread-1 doOtherThing, i = 2
Thread-1 doOtherThing, i = 3
Thread-1 doOtherThing, i = 4
Thread-1 doOtherThing, i = 5

Synchronized**关键字**,可以锁住当前对象,或者是类的对象,同时还有代码块等等,用法之前介绍过,同时还有配合使用的 obj.wait()
obj.notify()

Lock 常用方法

定时锁

DEMO5:

public class Test {

    static final int TIMEOUT = 3000;

    private static class Counter {

        private ReentrantLock mReentrantLock = new ReentrantLock();

        public void count() {
            try{
                //lock() 不可中断
                mReentrantLock.lock();
                // 模拟耗时,突出线程是否阻塞
                for (int i = 0; i < 6; i++) {
                    long startTime = System.currentTimeMillis();
                    while (true) {
                        if (System.currentTimeMillis() - startTime > 100)
                            break;
                    }
                    System.out.println(Thread.currentThread().getName() + ", i = " + i);
                }
            } finally {
                // 必须在 finally 释放锁
                mReentrantLock.unlock();
            }
        }

        public void doOtherThing(){
            try{
                //tryLock(long timeout, TimeUnit unit) 尝试获得锁
                boolean isLock = mReentrantLock.tryLock(300, TimeUnit.MILLISECONDS);
                System.out.println(Thread.currentThread().getName() + " isLock:" + isLock);
                if(isLock){
                    for (int i = 0; i < 6; i++) {
                        // 模拟耗时,突出线程是否阻塞
                        long startTime = System.currentTimeMillis();
                        while (true) {
                            if (System.currentTimeMillis() - startTime > 100)
                                break;
                        }
                        System.out.println(Thread.currentThread().getName() + " doOtherThing, i = " + i);
                    }
                }else{
                    System.out.println(Thread.currentThread().getName() + " timeout");
                }
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName() + " 中断 ");
            }finally {
                // 若当前线程持有锁,则释放
                if(mReentrantLock.isHeldByCurrentThread()){
                    mReentrantLock.unlock();
                }
            }
        }
    }

    public static void main(String[] var0) {
        final Counter counter = new Counter();
        new Thread(new Runnable() {
            @Override
            public void run() {
                counter.count();
            }
        }).start();
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                counter.doOtherThing();
            }
        });
        thread2.start();
    }
}

DEMO5 输出:

Thread-0, i = 0
Thread-0, i = 1
Thread-0, i = 2
Thread-1 isLock:false
Thread-1 timeout
Thread-0, i = 3
Thread-0, i = 4
Thread-0, i = 5

tryLock() 尝试获得锁,tryLock(long timeout, TimeUnit unit) 在给定的
timeout 时间内尝试获得锁,若超时,则不带锁往下走,所以必须加以判断。

ReentrantLock重入锁:相比synchronized可以同步

getHoldCount()、getQueueLength()和getWaitQueueLength()

  • int getHoldCount():查询当前线程保持此锁定的个数(调用 lock()
    方法的次数
  • int getQueueLength():返回正等待获取此锁定的线程估计数(等待
    lock 释放的线程数
  • int getWaitQueueLength(Condition
    condition):返回等待与此锁定相关的给定条件 Condition
    的线程估计数(执行同一个condition.await()方法的线程数

ReentrantLock or synchronized

ReentrantLock 、synchronized 之间如何选择?

ReentrantLock 在性能上 比 synchronized 更胜一筹。

ReentrantLock 需格外小心,因为需要显式释放锁,lock() 后记得
unlock(),而且必须在 finally 里面,否则容易造成死锁。

synchronized 隐式自动释放锁,使用方便。

ReentrantLock 扩展性好,可中断锁,定时锁,自由控制。

synchronized 一但进入阻塞等待,则无法中断等待。

ReentrantReadWriteLock读写锁,获取读写锁到两个对象

hasQueuedThread()、hasQueuedThreads()和hasWaiters()

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

Condition对象:通过Lock 中newCondition获取一个 Condition对象实例。其中的await()
signal()类似 wait()和notify() 方法。

isFair()、isHeldByCurrentThread()和isLocked()

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

Semaphore信号量机制:方法有acquire和 release

lockInterruptibly()、tryLock()和tryLock(long timeout,TimeUnit unit)

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

ThreadLocal机制,使用空间换时间的理念,为每一个线程存储一个副本。

使用 Condition 实现顺序执行

使用 Condition 对象,可以对线程执行的业务进行排序规划

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

public class Run {

    volatile private static int nextPrintWho = 1;
    private static ReentrantLock lock = new ReentrantLock();
    final private static Condition conditionA = lock.newCondition();
    final private static Condition conditionB = lock.newCondition();
    final private static Condition conditionC = lock.newCondition();

    public static void main(String[] args) {
        Runnable runnableA = new Runnable() {
            @Override
            public void run() {
                try {
                    lock.lock();
                    while (nextPrintWho != 1) {
                        conditionA.await();
                    }
                    for (int i = 0; i < 3; i++) {
                        System.out.println("ThreadA " + (i + 1));
                    }
                    nextPrintWho = 2;
                    conditionB.signalAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        };

        Runnable runnableB = new Runnable() {
            @Override
            public void run() {
                try {
                    lock.lock();
                    while (nextPrintWho != 2) {
                        conditionB.await();
                    }
                    for (int i = 0; i < 3; i++) {
                        System.out.println("ThreadB " + (i + 1));
                    }
                    nextPrintWho = 3;
                    conditionC.signalAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        };

        Runnable runnableC = new Runnable() {
            @Override
            public void run() {
                try {
                    lock.lock();
                    while (nextPrintWho != 3) {
                        conditionC.await();
                    }
                    for (int i = 0; i < 3; i++) {
                        System.out.println("ThreadC " + (i + 1));
                    }
                    nextPrintWho = 1;
                    conditionA.signalAll();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        };

        for (int i = 0; i < 5; i++) {
            new Thread(runnableA).start();
            new Thread(runnableB).start();
            new Thread(runnableC).start();
        }
    }
}

1.Java 内存模型和 volatile

使用 ReentrantReadWriteLock 类

读写锁有两个锁:一个操作相关锁(共享锁),一个操作相关锁(排他锁)。

  • 多个 Thread 可以同时进行读取操作
  • 同一时刻只允许一个 Thread 进行写入操作

import java.util.concurrent.locks.ReentrantReadWriteLock;

public class Run {
    public static void main(String[] args) throws InterruptedException {

        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

        Runnable read = new Runnable() {
            @Override
            public void run() {
                try {
                    try {
                        lock.readLock().lock();
                        System.out.println("获得读锁 " + Thread.currentThread().getName() + " " + System
                                .currentTimeMillis());
                        Thread.sleep(3000);
                    } finally {
                        lock.readLock().unlock();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

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

        System.out.println("读读共享:");
        new Thread(read).start();
        new Thread(read).start();

        Thread.sleep(10000);

        System.out.println("写写互斥");
        new Thread(write).start();
        new Thread(write).start();

        Thread.sleep(10000);

        System.out.println("读写互斥");
        new Thread(read).start();
        Thread.sleep(1000);
        new Thread(write).start();

        Thread.sleep(10000);

        System.out.println("写读互斥");
        new Thread(write).start();
        Thread.sleep(1000);
        new Thread(read).start();
    }
}

图片 1

结果分析

         Java 中每一个线程都会有自己的内存工作区,其中存放着被所有线程共享的主内存变量的值得拷贝。当线程执行的时候,他在自己的工作内存中操作变量。为了取得一个共享变量,一个线程通常是先获取锁定并且清除其他工作内存区,保证共享变量从所有的线程的内存共享区正确的装入到线程的内存工作区,当线程解锁的时候保证工作内存区中变量的值写回到共享内存区。

          线程中可以执行的操作 use
assign load store lock unlock.而主内存可以执行的操作有 read
write lock unlock. 每一个操作都是原子的。

Java 进阶7 并发优化 5 并发控制板方法 20131114

前言:

         Java 中多线程并发程序中存在线程安全的问题,之前学习 Java的同步机制,掌握的同步方法只有一种就是使用 synchronized关键字,解决线程之间的同步问题。同时在操作系统 C++多线程中也了解到其他的多线程同步机制:比如信号量、临界区、互斥锁等等

          在这里系统的整理一下在 Java中实现线程同步的机制:内部锁,重入锁,读写锁,信号量等等。

volatile 关键字,保证读写的共享对象时在共享内存中的,而不是线程中的副本,但是不保证线程安全。

Synchronized**关键字**,可以锁住当前对象,或者是类的对象,同时还有代码块等等,用法之前介绍过,同时还有配合使用的 obj.wait()
obj.notify()

ReentrantLock重入锁:相比synchronized可以同步

ReentrantReadWriteLock读写锁,获取读写锁到两个对象

Condition对象:通过Lock 中newCondition获取一个 Condition对象实例。其中的await()
signal()类似 wait()和notify() 方法。

Semaphore信号量机制:方法有acquire和 release

ThreadLocal机制,使用空间换时间的理念,为每一个线程存储一个副本。

1.Java 内存模型和 volatile

         Java 中每一个线程都会有自己的内存工作区,其中存放着被所有线程共享的主内存变量的值得拷贝。当线程执行的时候,他在自己的工作内存中操作变量。为了取得一个共享变量,一个线程通常是先获取锁定并且清除其他工作内存区,保证共享变量从所有的线程的内存共享区正确的装入到线程的内存工作区,当线程解锁的时候保证工作内存区中变量的值写回到共享内存区。

          线程中可以执行的操作 use
assign load store lock unlock.而主内存可以执行的操作有 read
write lock unlock. 每一个操作都是原子的。

图片 2

          当一个线程使用一个变量的时候,不论程序是否争取的使用线程同步操作,他获得值一定是由它本身或者其他线程存储到变量的值。

          每一个线程都有自己的工作内存区,因此当一个线程改变自己的内存中的数据的时候,对于其他线程是不可见的,因此可以使用关键字 volatile关键字强制多线程读写主内存中的数据,从而使得 volatile变量在多线程中是可见的。

          声明变量时 volatile的时候,可以保证以下几点:

          其他线程对于变量的修改可以及时反映在当前线程中;

          确保当前线程对 volatile变量的修改,能够及时的写回主内存中,并且被其他的线程可见;

          使用volatile可以确保编译的时候保证有序性。

          但是**volatile**并不保证数据的线程安全

2. 同步关键字 synchronized

          最常用的同步关键字 synchronized,相比其他的同步机制synchronized关键字更加简洁明了,便于代码的维护。

         1)Synchronized 可以锁定一个方法,获得的是当前对象的锁

         public
synchronized void func(){}

         2)Synchronized 同步代码块,同步更为精准,这样的话,缩小了同步的范围,提高了性能。

         3) 同步静态方法,获得的是该类的对象上面,执行方法之前首先要获得当前类的对象的锁。同时为了实现多线程之间的交互,还应该使用 wait()/
notify() notifyAll()

         synchronized(obj){

         while(condition){

         obj.wait();// 这样因为不满足继续执行的条件,所以 wait会释放当前对象的锁,供其他线程使用该对象。当等待的一个线程受到 notify的时候,就会唤醒被阻塞的线程,当有多个线程被同一个对象阻塞的时候,只会唤醒一个线程。

}

}

3.ReentrantLock 重入锁

          ReentrantLock是重入锁,他比 synchronized内部锁拥有更加强大的功能,他可以中断,可以定时,在高并发的情况下,比 synchronized有着明显的优势。JDK6中差别不是太明显。同时 ReentrantLock提供了公平和不公平的两种锁机制。公平锁保证等待锁的线程

是公平的,不存在插队的情况,总是按照队列的方式先进先出;

          但还不公平锁是可以插队的,在性能上来说,非公平锁的兴根更佳。

          构造函数 public
ReentrantLock(Boolean fair);决定重入锁是否是公平的。并且需要记得在使用完成之后一定要释放锁,一般是放在 finally中释放的。

         ReentrantLock 提供的重要的方法:

         lock() :获得锁,如果被占用,则等待;

         lockInterruptibly(): 获得锁,但优先响应中断

         tryLock() 尝试获得锁,如果成功返回 true,反之,返回false,该方法是不会等待的,立即返回

         tryLock(long
time, TimeUnit unit);

         unlock(): 释放锁

4.ReadWriteLock 读写,有效的减少所锁的竞争机制,提升系统的性能。读写锁允许多个线程同时读,但是考虑到数据之间的完整性,写写操作和读写操作需要相互等待和持有锁。如果在系统中读的次数远远大于写的次数的话,则读写多会发挥很大的优势。

ReentrantReadWriteLock

private static Lock lock  = new ReentrantLock();

private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

private static Lock readLock = readWriteLock.readLock();

private static Lock writeLock = readWriteLock.writeLock();

private static int value = 10;

public int handleRead() throws InterruptedException{

    try{

         readLock.lock();

        Thread. sleep(2000);

         return value ;

    }finally{

         readLock.unlock();

    }   

}

public void handleWrite(int v) throws InterruptedException{

    try{

         writeLock.lock();

        Thread. sleep(2000);

         value = v;

    }finally{

         writeLock.unlock();

    }

}

         JDK1.5 之后提供的读写锁分离,对于读操作较多的并发系统中,使用读写锁的机制会提高程序的性能。

5.Condition 对象

          线程之间协调工作使用的是 Condition对象。Condition**是于锁相关的,通过 Lock接口的Condition
newCondition()**方法可以生成一个和锁绑定的 Condition**实例。**Condition 对象和Lock的关系就是 Object.wait()和Object.notify() 的关系一样,它们可以配合使用完成多线程的协作任务。

          Condition**提供的函数有:**

          void
await() ;**
是**当前线程等待,同时释放 lock在其他的线程中使用signal() 或者是 signalAll(),才可以唤醒当前线程;

         void
awaitUninterruptibly(); 等待过程不被中断 ,功能是和await 相同

         void
signal() 唤醒一个的等待的线程,相对于 signalAll()方法,会唤醒所有等待的线程。

6.Semaphore 信号量

          信号量为多线程写作提供了强大的控制方法,信号量是对锁的扩展,无论是内部锁( synchronized)还是重入锁(ReentrantLock),一次只允许一个线程访问资源,但是信号量确可以指定多个线程同时访问某一个资源。

         public
Semaphore(int permits);

         public
Semaphore(int permits, boolean fair);

          在构造信号量对象的时候,必须指定信号量的准入线程数目,其主要的方法有:

         public
void acquire();

         public
void acquireUninterruptibly();

         public
boolean tryAcquire();

         public
boolean tryAcquire(long timeout, TimeUnit unit);

         public
void release();

          一个对象池,其中对象的数量是 100,当他同时有超过100个对象的请求,资源池就会出现资源短缺,未获得资源的线程就必须等待,当某一个线程使用对象完毕的时候,就会将对象返回到资源池,此时会激活一个等待的线程。

7.ThreadLocal 线程局部变量

         ThreadLocal 是一种多线程间并发访问变量的解决方案,和 synchronized等锁方式是不同的,他完全不提供锁机制,而是使用的是空间换时间的手段,为每一线程提供变量的独立副本,来保证线程的安全。但是这种方式不是具有绝对的优势,在并发量不高的情况下,使用锁机制会更好,但是在高并发的情况下, ThreadLocal可以减少锁的竞争。

         ThreadLocal 接口:

         public
void set(T val); 将次线程局部变量的当前线程副本设置成指定的值

         public
T get(); 返回次线程局部变量在当前线程中的副本值

         public
void remove(); 移除此线程局部变量当前线程值

public class ThreadLocalExample implements Runnable {

    public static final ThreadLocal<Date> localvar = new ThreadLocal<Date>();

    private long time ;

    public ThreadLocalExample( long time){

        this.time = time;

    }

    @Override

    public void run() {

        // TODO Auto-generated
method stub

       Date d
new Date(time );

        for(int i = 0; i< 100;
i++){

            localvar.set(d);

            if(localvar .get().getTime()
!= time){

              System. out.println(“id=
” + time + ”
localvar = ” + localvar.get().getTime()
);

           }

       }

    }

}

并发的时候,可以保证多个线程间的 localvar是相互独立的,虽然没有同步操作,但是多个线程的数据是不会相互影响的。因此永远不会出现当前线程持有的时间和成员变量 time不一致。同时不同对象上的 Date对象副本并不是由ThreadLocal创建的,而且是必须在线程内创建,并保证不同线程间实例均不相同。如果多个线程使用的是同一个 Date对象实例,及时方法ThreadLocal中保护起来也是没有用的。

 

YangTengfei

2013.11.23

          当一个线程使用一个变量的时候,不论程序是否争取的使用线程同步操作,他获得值一定是由它本身或者其他线程存储到变量的值。

          每一个线程都有自己的工作内存区,因此当一个线程改变自己的内存中的数据的时候,对于其他线程是不可见的,因此可以使用关键字 volatile关键字强制多线程读写主内存中的数据,从而使得 volatile变量在多线程中是可见的。

          声明变量时 volatile的时候,可以保证以下几点:

          其他线程对于变量的修改可以及时反映在当前线程中;

          确保当前线程对 volatile变量的修改,能够及时的写回主内存中,并且被其他的线程可见;

          使用volatile可以确保编译的时候保证有序性。

          但是**volatile**并不保证数据的线程安全

2. 同步关键字 synchronized

          最常用的同步关键字 synchronized,相比其他的同步机制synchronized关键字更加简洁明了,便于代码的维护。

         1)Synchronized 可以锁定一个方法,获得的是当前对象的锁

         public
synchronized void func(){}

         2)Synchronized 同步代码块,同步更为精准,这样的话,缩小了同步的范围,提高了性能。

         3) 同步静态方法,获得的是该类的对象上面,执行方法之前首先要获得当前类的对象的锁。同时为了实现多线程之间的交互,还应该使用 wait()/
notify() notifyAll()

         synchronized(obj){

         while(condition){

         obj.wait();// 这样因为不满足继续执行的条件,所以 wait会释放当前对象的锁,供其他线程使用该对象。当等待的一个线程受到 notify的时候,就会唤醒被阻塞的线程,当有多个线程被同一个对象阻塞的时候,只会唤醒一个线程。

}

}

3.ReentrantLock 重入锁

          ReentrantLock是重入锁,他比 synchronized内部锁拥有更加强大的功能,他可以中断,可以定时,在高并发的情况下,比 synchronized有着明显的优势。JDK6中差别不是太明显。同时 ReentrantLock提供了公平和不公平的两种锁机制。公平锁保证等待锁的线程

是公平的,不存在插队的情况,总是按照队列的方式先进先出;

          但还不公平锁是可以插队的,在性能上来说,非公平锁的兴根更佳。

          构造函数 public
ReentrantLock(Boolean fair);决定重入锁是否是公平的。并且需要记得在使用完成之后一定要释放锁,一般是放在 finally中释放的。

         ReentrantLock 提供的重要的方法:

         lock() :获得锁,如果被占用,则等待;

         lockInterruptibly(): 获得锁,但优先响应中断

         tryLock() 尝试获得锁,如果成功返回 true,反之,返回false,该方法是不会等待的,立即返回

         tryLock(long
time, TimeUnit unit);

         unlock(): 释放锁

4.ReadWriteLock 读写,有效的减少所锁的竞争机制,提升系统的性能。读写锁允许多个线程同时读,但是考虑到数据之间的完整性,写写操作和读写操作需要相互等待和持有锁。如果在系统中读的次数远远大于写的次数的话,则读写多会发挥很大的优势。

ReentrantReadWriteLock

private static Lock lock  = new ReentrantLock();

private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

private static Lock readLock = readWriteLock.readLock();

private static Lock writeLock = readWriteLock.writeLock();

private static int value = 10;

public int handleRead() throws InterruptedException{

    try{

         readLock.lock();

        Thread. sleep(2000);

         return value ;

    }finally{

         readLock.unlock();

    }   

}

public void handleWrite(int v) throws InterruptedException{

    try{

         writeLock.lock();

        Thread. sleep(2000);

         value = v;

    }finally{

         writeLock.unlock();

    }

}

         JDK1.5 之后提供的读写锁分离,对于读操作较多的并发系统中,使用读写锁的机制会提高程序的性能。

5.Condition 对象

          线程之间协调工作使用的是 Condition对象。Condition**是于锁相关的,通过 Lock接口的Condition
newCondition()**方法可以生成一个和锁绑定的 Condition**实例。**Condition 对象和Lock的关系就是 Object.wait()和Object.notify() 的关系一样,它们可以配合使用完成多线程的协作任务。

          Condition**提供的函数有:**

          void
await() ;**
是**当前线程等待,同时释放 lock在其他的线程中使用signal() 或者是 signalAll(),才可以唤醒当前线程;

         void
awaitUninterruptibly(); 等待过程不被中断 ,功能是和await 相同

         void
signal() 唤醒一个的等待的线程,相对于 signalAll()方法,会唤醒所有等待的线程。

6.Semaphore 信号量

          信号量为多线程写作提供了强大的控制方法,信号量是对锁的扩展,无论是内部锁( synchronized)还是重入锁(ReentrantLock),一次只允许一个线程访问资源,但是信号量确可以指定多个线程同时访问某一个资源。

         public
Semaphore(int permits);

         public
Semaphore(int permits, boolean fair);

          在构造信号量对象的时候,必须指定信号量的准入线程数目,其主要的方法有:

         public
void acquire();

         public
void acquireUninterruptibly();

         public
boolean tryAcquire();

         public
boolean tryAcquire(long timeout, TimeUnit unit);

         public
void release();

          一个对象池,其中对象的数量是 100,当他同时有超过100个对象的请求,资源池就会出现资源短缺,未获得资源的线程就必须等待,当某一个线程使用对象完毕的时候,就会将对象返回到资源池,此时会激活一个等待的线程。

7.ThreadLocal 线程局部变量

         ThreadLocal 是一种多线程间并发访问变量的解决方案,和 synchronized等锁方式是不同的,他完全不提供锁机制,而是使用的是空间换时间的手段,为每一线程提供变量的独立副本,来保证线程的安全。但是这种方式不是具有绝对的优势,在并发量不高的情况下,使用锁机制会更好,但是在高并发的情况下, ThreadLocal可以减少锁的竞争。

         ThreadLocal 接口:

         public
void set(T val); 将次线程局部变量的当前线程副本设置成指定的值

         public
T get(); 返回次线程局部变量在当前线程中的副本值

         public
void remove(); 移除此线程局部变量当前线程值

public class ThreadLocalExample implements Runnable {

    public static final ThreadLocal<Date> localvar = new ThreadLocal<Date>();

    private long time ;

    public ThreadLocalExample( long time){

        this.time = time;

    }

    @Override

    public void run() {

        // TODO Auto-generated
method stub

       Date d
new Date(time );

        for(int i = 0; i< 100;
i++){

            localvar.set(d);

            if(localvar .get().getTime()
!= time){

              System. out.println(“id=
” + time + ”
localvar = ” + localvar.get().getTime()
);

           }

       }

    }

}

并发的时候,可以保证多个线程间的 localvar是相互独立的,虽然没有同步操作,但是多个线程的数据是不会相互影响的。因此永远不会出现当前线程持有的时间和成员变量 time不一致。同时不同对象上的 Date对象副本并不是由ThreadLocal创建的,而且是必须在线程内创建,并保证不同线程间实例均不相同。如果多个线程使用的是同一个 Date对象实例,及时方法ThreadLocal中保护起来也是没有用的。

 

YangTengfei

2013.11.23

You can leave a response, or trackback from your own site.

Leave a Reply

网站地图xml地图