`

j2se------多线程--锁

    博客分类:
  • J2SE
阅读更多
====================================================
锁。。
CountDownLatch -----解锁需要手动提供钥匙
闭锁(Latch),它可以延迟线程的进度知道线程到达终止状态。一个闭锁工作方式就像一道门,直到闭锁到达终点状态之前,门一直关闭着。终点状态到了之后,所有阻塞的线程都可以通过。CountDownLatch 使用一个计数器作为终点状态,知道计数器的值到达0时,闭锁才会打开。调用await 方法,线程会阻塞知道计数器为0,countDown 方法使计数器减一。

闭锁有两种常见的用法,开始闭锁,结束闭锁。开始闭锁用于等待一个条件到达后所有线程一起执行,结束闭锁可以用来等待所有条件或所有线程结束后再进行后续处理。例子:
final CountDownLatch startLatch = new CountDownLatch(1);  //想象成这把锁需要开一次就能打开
final CountDownLatch endLatch = new CountDownLatch(3); //想象成这把锁需要开三次才能打开
Runnable prepare = new Runnable() {
	@Override
	public void run() {
		try {
			startLatch.await();//等待开始闭锁,线程同时开始执行,等待别人手动打开锁
			System.out.println("收拾东西,准备出门");
			Random rnd = new Random();
			Thread.sleep(rnd.nextInt(1000));
		} catch (InterruptedException ignored) {
		}
		endLatch.countDown();
	}
};

Thread mum = new Thread(prepare);
Thread dad = new Thread(prepare);
Thread me = new Thread(prepare);
mum.start();
dad.start();
me.start();
startLatch.countDown();  //当他们准备好了,主线程开锁
try {
	endLatch.await();  //主线程也被上了锁,不过这次可以解开锁的人是三个人
} catch (InterruptedException ignored) {
}
System.out.println("逛街");
~~~~~~~~~~~~~~~~~~忧郁的分割线~~~~~~~~~~~~~~~~~~~~~~~~~~~
CyclicBarrier //不需要显示的开锁

关卡(Barrier)类似于闭锁,他们都能阻塞一组线程,知道某些事件发生,不同之处在于所有CyclicBarrier等待的是现线程,只有一定数目的线程到达这个点时,才允许同时通过。它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作很有用。
该例子中CyclicBarrier等待两个线程到达后输出conditon is arrive and CycleBarrier is running,两个线程都从await中返回。
public class Main { 

    public static CyclicBarrier getCyclicBarrier(int count) { 
        if (count <= 0) 
            return null; 
        final CyclicBarrier cyclicBarrier = new CyclicBarrier(count, 
                new Runnable() { 
                    public void run() { 
                        try { 
                            Thread.sleep(1000); 
                        } catch (InterruptedException e) { 
                            e.printStackTrace(); 
                        } 
                        System.out.println("conditon is arrive and CycleBarrier is running"); 
                    } 
                }); 
        return cyclicBarrier; 
    } 

    public static Thread getThread(String nameOfThread, 
            final CyclicBarrier cyclicBarrier) { 
        Thread thread = new Thread(nameOfThread) { 
            public void run() { 
                System.out.println(this.getName() + 
"is begin; and count is "+ (++count)); 
                try { 
                    cyclicBarrier.await(); 
                } catch (InterruptedException e) { 
                    e.printStackTrace(); 
                } catch (BrokenBarrierException e) { 
                    e.printStackTrace(); 
                } 
                System.out.println(this.getName() + "finished"); 
            } 
        }; 
        return thread; 

    } 

    static int count = 0; 

    public static void main(String[] args) { 
        /** define a cyclicBarrier and number of barrier is 2. */ 
        CyclicBarrier cyclicBarrier = getCyclicBarrier(2); 
        Thread threadOne = getThread("threadOne", cyclicBarrier); 
        threadOne.start(); 
        Thread threadTwo = getThread("threadTwo", cyclicBarrier); 
        threadTwo.start(); 
    } 
}
下面进一步理解循环概念:
比如有几个旅行团需要途经深圳、广州、韶关、长沙最后到达武汉。旅行团中有自驾游的,有徒步的,有乘坐旅游大巴的;这些旅行团同时出发,并且每到一个目的地,都要等待其他旅行团到达此地后再同时出发,直到都到达终点站武汉。

这时候CyclicBarrier就可以派上用场。CyclicBarrier最重要的属性就是参与者个数,另外最要方法是await()。当所有线程都调用了await()后,就表示这些线程都可以继续执行,否则就会等待。

package examples.ch06.example01;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestCyclicBarrier {
	// 徒步需要的时间: Shenzhen, Guangzhou, Shaoguan, Changsha, Wuhan
	private static int[] timeWalk = { 5, 8, 15, 15, 10 };
	// 自驾游
	private static int[] timeSelf = { 1, 3, 4, 4, 5 };
	// 旅游大巴
	private static int[] timeBus = { 2, 4, 6, 6, 7 };

	static String now() {
		SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
		return sdf.format(new Date()) + ": ";
	}

	static class Tour implements Runnable {
		private int[] times;
		private CyclicBarrier barrier;
		private String tourName;

		public Tour(CyclicBarrier barrier, String tourName, int[] times) {
			this.times = times;
			this.tourName = tourName;
			this.barrier = barrier;
		}

		public void run() {
			try {
				Thread.sleep(times[0] * 1000);
				System.out.println(now() + tourName + " Reached Shenzhen");
				barrier.await();
				Thread.sleep(times[1] * 1000);
				System.out.println(now() + tourName + " Reached Guangzhou");
				barrier.await(); //第二次阻塞了哈
				Thread.sleep(times[2] * 1000);
				System.out.println(now() + tourName + " Reached Shaoguan");
				barrier.await();
				Thread.sleep(times[3] * 1000);
				System.out.println(now() + tourName + " Reached Changsha");
				barrier.await();
				Thread.sleep(times[4] * 1000);
				System.out.println(now() + tourName + " Reached Wuhan");
				barrier.await();
			} catch (InterruptedException e) {
			} catch (BrokenBarrierException e) {
			}
		}
	}

	public static void main(String[] args) {
		// 三个旅行团
		CyclicBarrier barrier = new CyclicBarrier(3);
		ExecutorService exec = Executors.newFixedThreadPool(3);
		exec.submit(new Tour(barrier, "WalkTour", timeWalk));
		exec.submit(new Tour(barrier, "SelfTour", timeSelf));
		exec.submit(new Tour(barrier, "BusTour", timeBus));
		exec.shutdown();
	}
}

~~~~~~~~~~~~~~~~~~~~~~wait和notify级别的锁~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
显式锁
在java 5之前,用于调节共享对象访问的机制只有synchronized和volatile。java 5提供了新的选择:ReentrantLock。ReentrantLock能够提供更多的高级特性,比如轮询和可定时的加锁,可中断的加锁。以及一个支持读锁和写锁的ReentrantReadWriteLock。使用ReentrantLock必须手动使用lock或其他操作加锁,在finally块中unlock。

Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set (wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition(); 
   final Condition notEmpty = lock.newCondition(); 

   final Object[] items = new Object[100];
   int putptr, takeptr, count;

   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length) 
         notFull.await();
       items[putptr] = x; 
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();
     } finally {
       lock.unlock();
     }
   }

   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0) 
         notEmpty.await();
       Object x = items[takeptr]; 
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();
       return x;
     } finally {
       lock.unlock();
     }
   } 
 }



ReentrantLock:一个可重入的互斥锁Lock,它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。 使用ReentrantLock构建的同步Map:
public class LockedMap<K, V> {
	private Map<K, V> map;
	private Lock lock = new ReentrantLock();
	
	public LockedMap(Map<K, V> map) {
		this.map = map;
	}

	public V get(K key) {
		lock.lock();
		try {
			return map.get(key);
		} finally {
			lock.unlock();
		}
	}

	public void put(K key, V value) {
		lock.lock();
		try {
			map.put(key, value);
		} finally {
			lock.unlock();
		}
	}
}
public class ReentrantLockTest {

    private List<Integer> numbers = new ArrayList<Integer>();
    private Lock numbersLock = new ReentrantLock();

    public void addNumbers(int num) {
        try {
            numbersLock.lock();
            numbers.add(num);
        } finally {
            numbersLock.unlock();
        }
    }

    public void outputNumbers() {
        try {
            if (numbersLock.tryLock(1, TimeUnit.SECONDS)) {
                for (int num : numbers) {
                    System.out.println(num);
                }
            }
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        } finally {
            numbersLock.unlock();
        }
    }    

    public static void main(String[] args) {
        final ReentrantLockTest test = new ReentrantLockTest();
        Executor pool = Executors.newFixedThreadPool(3);
        pool.execute(new Runnable() {

            public void run() {
                Random rnd = new Random();
                while (true) {
                    int number = rnd.nextInt();
                    test.addNumbers(number);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException ignored) {
                    }
                }
            }
        });

        pool.execute(new Runnable() {

            public void run() {
                while (true) {
                    test.outputNumbers();
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ignored) {
                    }
                }
            }
        });
    }
}
ReentrantReadWriteLock提供了对读锁和写锁的支持,同一时刻,可允许多个读锁,但只允许有一个写锁,读锁的获取和写锁的获取是互斥的。从ReentrantReadWriteLock对象的readLock方法可以获得相应的读锁,writeLock方法可以获得相应的写锁。使用 ReentrantReadWriteLock构建的Map,允许多个get操作并发执行: 
public class ReadWriteMap<K,V>  {
	private Map<K,V> map;
	private ReadWriteLock lock = new ReentrantReadWriteLock();
	private Lock readLock = lock.readLock();
	private Lock writeLock  = lock.writeLock();	
	
	public ReadWriteMap(Map<K,V> map){
		this.map = map;
	}
	
	public V get(K key){
		readLock.lock();
		try{
			return map.get(key);
		}
		finally{
			readLock.unlock();
		}	
	}
	
	public void put(K key,V value){
		writeLock.lock();
		try{
			map.put(key, value);
		}
		finally{
			writeLock.unlock();
		}
	}
	
}


分享到:
评论

相关推荐

    JAVA学生作品---航空售票系统(J2SE).zip

    此外,考虑到并发处理,系统可能运用了多线程技术,以应对多个用户同时操作。Java提供了强大的线程支持,可以确保系统在高并发环境下的稳定运行。同时,为了防止数据竞争,可能还采用了锁机制或者并发容器。 安全性...

    java_movestar.rar_j2se_线程控制

    这个项目可能采用了其中的一种或两种方法,让星星在屏幕上移动,这涉及到了多线程的同步和通信问题。 首先,我们可能会看到一个自定义的Thread类或Runnable实现,用于描绘星星的移动逻辑。在这个类中,重写`run()`...

    csdn_多线程

    多线程是计算机编程中的一个核心概念,尤其在Java(J2SE)平台中尤为重要。在现代计算环境中,多线程技术允许程序同时执行多个不同的任务,极大地提高了系统的并发性和效率。下面我们将深入探讨多线程的基本概念、...

    J2SE 全套课件

    6. **多线程**:Java提供内置的多线程支持,理解线程的创建、同步、互斥锁、条件变量等概念,能够编写出并发执行的高效程序。 7. **反射机制**:反射是Java的一项高级特性,它允许程序在运行时动态获取类的信息并...

    J2SE_5.0_第09章_线程2.rar

    J2SE 5.0中的线程管理是Java并发编程的基础,理解线程的创建、状态、同步机制以及如何避免并发问题,对于编写高效、可靠的多线程程序至关重要。通过学习和实践这些知识,开发者可以更好地利用多核处理器的性能,提高...

    J2SE进阶(技术提高)

    2. **多线程**:Java 提供了内置的多线程支持,可以创建 Thread 对象或者使用 Runnable 接口。深入理解线程同步、锁机制(如 synchronized 关键字,ReentrantLock)以及并发工具类(如 ExecutorService 和 Future)...

    java 测试题

    Java 测试题旨在检验开发者对Java编程语言的...以上就是针对Java测试题中涉及的一些核心知识点的详细解释,这些知识点包括对象实例化、数据类型、方法参数、字符串与数组操作、多线程、访问控制以及构造函数的使用等。

    Java Threads英文第三版

    根据提供的文件信息,这是一本名为《Java Threads英文第三版》的技术书籍,专注于Java多线程编程。内容涵盖了从线程的基本概念到复杂的线程同步技术。以下是对标题和描述中提到知识点的详细说明。 ### 知识点说明 ...

    J2SE应用编程150例

    6. **多线程编程**:Java提供对并发处理的强大支持,例子将涵盖线程的创建、同步、互斥锁等概念。 7. **反射API**:通过反射,可以在运行时动态地获取类的信息并操作对象。这个部分可能会教你如何使用Class、Method...

    J2SE综合实例

    本综合实例主要关注J2SE中的线程管理,这是一门非常关键的技术,尤其是在多任务并行处理和优化应用程序性能时。线程允许多个任务同时执行,使得程序更加高效且响应迅速。 线程在Java中是通过`java.lang.Thread`类来...

    java并发笔记

    - `count++`操作涉及到读取、修改和写回三个步骤,在多线程环境中不是原子操作,容易引发数据不一致性。 - `AtomicLong`类提供了线程安全的整数加法操作,避免了竞争条件下的数据不一致性问题。 #### 三、多...

    java图形界面教程-北航

    线程间的通信和同步是多线程编程中的关键点,Java提供了多种机制,如synchronized关键字实现互斥访问,wait()、notify()和notifyAll()方法进行线程间协作,以及Lock接口(如ReentrantLock)提供更高级的锁机制。...

    J2SE 1.6所有的类

    这些类是Java编程的基础,提供了丰富的功能,涵盖了I/O、网络、多线程、GUI、数据库连接、安全控制等多个方面。 在J2SE 1.6中,`AbstractAction`类是 Swing 库中的一个关键类,用于创建可重复使用的动作,常被用作...

    J2se外企it面试题

    Java面试中的这些问题涵盖了Java语言的核心概念、集合框架、多线程、内存管理、异常处理以及设计模式等多个方面。下面是对这些知识点的详细说明: 1. **final, finally, finalize的区别**: - `final`:用于修饰类...

    Java(J2SE、J2EE)学习小结

    `HashMap`和`Hashtable`类似,前者非同步,后者同步,因此`Hashtable`在多线程环境中更安全,但性能较差。 4. 多线程与同步:Java中创建线程主要有继承Thread类和实现Runnable接口两种方式。同步方法有`...

    (超赞)JAVA精华之--深入JAVA API

    **1.7 Java 5.0多线程编程** - **高级主题** - 如并发工具类、锁机制等。 **1.8 Java Socket编程** - **网络编程基础** - 使用 `Socket` 和 `ServerSocket` 类实现客户端和服务端通信。 **1.9 Java的内存泄漏*...

    Java并发中的线程安全性

    为了确保程序在多线程环境下的正确性,理解线程安全性(Thread Safety)至关重要。 #### 2. 线程安全性分析 ##### 2.1 定义与概念 线程安全性是指一个对象或类可以在多线程环境下被多个线程共享而不会导致数据不...

Global site tag (gtag.js) - Google Analytics