这边我们使用的到的知识点是 线程加锁、等待、通知执行
一、synchronized来实现
synchronized (lock) 给代码块加锁
lock.notifyAll(lock) 通知其他线程执行
lock.wait(lock)当前线程等待
// notify, notifyAll 需要和synchronized联合使用
@Test
public void method9() {
// 创建阻塞队列
ArrayBlockingQueue<Character> queue = new ArrayBlockingQueue(3);
// 控制线程执行的次数
CountDownLatch latch = new CountDownLatch(9);
char[] chars = new char[] { 'A', 'B', 'C' };
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
queue.put(chars[(i % 3)]);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
).start();
// 创建线程池三个线程
ExecutorService pools = Executors.newFixedThreadPool(3);
// 创建锁的对象,这种写法不支持分布式锁
Object lock = new Object();
for (int j = 0; j < 9; j++) {
pools.execute(() -> {
synchronized (lock) {
try {
// 使用阻塞队列获取字符
Character char1 = queue.take();
latch.countDown();
System.out.println(String.valueOf(char1) + " " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
//通知其他线程执行
lock.notifyAll();
// 当前线程执行等待,下面代码不执行
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
);
}
try {
//等待输出结果
latch.await();
//关闭线程池
pools.shutdown();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("执行结束");
}
输出结果:
A. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
B. 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
C. 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。
拓展学习:Java中Synchronized的用法_阳光日志-CSDN博客_synchronized
二、ReentrantLock 实现
/**
* 使用ReentrantLock 来控制并发
*/
@Test
public void method10() {
// 创建阻塞队列
ArrayBlockingQueue<Character> queue = new ArrayBlockingQueue(3);
char[] chars = new char[] { 'A', 'B', 'C' };
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
queue.put(chars[(i % 3)]);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
).start();
//锁对象
ReentrantLock reen = new ReentrantLock();
// 创建一个线程数是3的线程池
ExecutorService pool = Executors.newFixedThreadPool(3);
// 线程计数控制
CountDownLatch latch = new CountDownLatch(9);
for (int i = 0; i < 9; i++) {
pool.execute(() -> {
// 加锁
reen.lock();
try {
Character out = queue.take();
System.out.println(Thread.currentThread().getName() + " " + out);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 释放锁
reen.unlock();
latch.countDown();
});
}
try {
// 程序等待线程执行完毕
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//关闭线程池
pool.shutdown();
}
执行结果:
三、使用Semaphore
/**
* 使用信号量来控制
* Semaphore Semaphore也是一个线程同步的辅助类,可以维护当前访问自身的线程个数,并提供了同步机制。
* 使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。
*/
@Test
public void method4() {
// 创建阻塞队列
ArrayBlockingQueue<Character> queue = new ArrayBlockingQueue(3);
char[] chars = new char[] { 'A', 'B', 'C' };
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
queue.put(chars[(i % 3)]);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
).start();
Semaphore s1 = new Semaphore(1);
CountDownLatch latch = new CountDownLatch(9);
ExecutorService pool = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
pool.execute(() -> {
try {
//请求获得许可,如果有可获得的许可则继续往下执行,许可数减1。否则进入阻塞状态
s1.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Character out = queue.take();
System.out.println("线程" + Thread.currentThread().getName() + out);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
// 释放许可,许可数加1
s1.release();
});
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
执行结果
扩展资料:理解Semaphore及其用法详解_浩子的博客-CSDN博客_semaphore
线程同步机制有哪些?
1、互斥:互斥机制,保证同一时间,只有一个线程可以操作共享资源。如synchronized,lock
2、临界值:让多线程串行话访问资源
3、事件通知:通过事件的通知去保证大家都有序的访问共享资源
4、信号量:多个任务同时访问,同时限制数量。如发令枪CDL,Semaphore