ReadWriteLock是一个读写锁的接口,ReentrantLock是ReadWriteLock接口的一个具体实现,实现了读写的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写、写和读、写和写之间才会互斥,提高了读写的性能。
Thread.interrupt()来中断一个线程就会设置中断标记为true,当中断线程调用静态方法Thread.interrupted()来检查中断状态时,中断状态会被标记为false;非静态方法isInterrupted()用来查询其他线程的中断状态且不会改变中断状态标记,任何抛出InterruptedException异常的方法都会将中断状态置为false。
interrupt()通知线程需要结束,如果线程在waiting会抛出InterruptedException,线程自己决定要不要结束,异常会触发复位。
isInsterrupted()获取线程的中断标记,但是不会进行复位操作。
interrupted()获取线程的中断标记,并且主动执行复位操作。
public class InterruptDemo implements Runnable {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
System.out.println("异常里的isInterrupted:" + Thread.currentThread().isInterrupted());
Thread.currentThread().interrupt();//再次中断
System.out.println("再次中断:" + Thread.currentThread().isInterrupted());
}
}
System.out.println("线程结束");
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new InterruptDemo());
t1.start();
Thread.sleep(1000);
t1.interrupt();
}
}
public class InterruptedDemo {
public static void main(String[] args) {
Thread.currentThread().interrupt();
//查询中断状态,中断标记被标记为true
System.out.println(Thread.interrupted());
//获取线程的中断状态,本来应该是true,但是interrupted()触发了复位 所以为false
System.out.println(Thread.currentThread().isInterrupted());
}
}
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(5);
for (int i = 0; i < 10; i++) {
new Car(i, semaphore).start();
}
}
static class Car extends Thread {
private int num;
private Semaphore semaphore;
public Car(int num, Semaphore semaphore) {
this.num = num;
this.semaphore = semaphore;
}
public void run() {
try {
semaphore.acquire();//获取一个许可
System.out.println("第" + num + "占用 一个停车位");
TimeUnit.SECONDS.sleep(2);
System.out.println("第" + num + "俩车走喽");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class CyclicBarrierDemo extends Thread {
@Override
public void run() {
System.out.println("开始进行数 据分析");
}
public static void main(String[] args) {
CyclicBarrier cycliBarrier = new CyclicBarrier(3, new CyclicBarrierDemo());
new Thread(new DataImportThread(cycliBarrier, "file 1")).start();
new Thread(new DataImportThread(cycliBarrier, "file 2")).start();
new Thread(new DataImportThread(cycliBarrier, "file 3")).start();
}
}
public class DataImportThread extends Thread{
private CyclicBarrier cyclicBarrier;
private String path;
public DataImportThread(CyclicBarrier cyclicBarrier, String path) {
this.cyclicBarrier = cyclicBarrier;
this.path = path;
}
@Override
public void run() {
System.out.println("开始导入: "+path+" 位置的数据");
try {
cyclicBarrier.await();//阻塞
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
前者对后者可见
fail-safe和fail-fast是多线程并发操作集合时的一种失败处理机制。
fail-fast表示快速失败,在集合遍历过程中,一旦发现容器中的数据被修改了,会立刻抛出ConcurrentModificationException异常,从而导致遍历失败。
例如:定义一个Map集合,使用Iterator迭代器进行数据遍历,在遍历过程中对集合数据做变更时,就会发生fail-fast。java.util包下的集合类都是快速失败机制的,常见的使用fail-fast方式遍历的容器有HashMap和ArrayList。
fail-safe表示失败安全,也就是在这种机制下,出现集合元素的修改,不会抛出ConcurrentModificationException,原因是采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历,由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到。
例如:定义一个CopyOnWriteArrayList,在对这个集合遍历过程中,对集合元素做修改后,不会抛出异常,但同时也不会打印出增加的元素。java.util.concurrent包下的容器都是安全失败的,可以在多线程下并发使用,并发修改。常见的使用fail-safe方式遍历的容器有ConcurrentHashMap和CopyOnWriteArrayList。
阻塞队列:当队列为空时,获取队列中元素的消费者线程被阻塞,同时唤醒生产者线程,当队列满了,向队列中添加元素的生产者线程被阻塞,同时唤醒消费者线程。ArrayBlockingQueue是基于数组结构的阻塞队列,也就是队列元素是存储在一个数组结构里面,并且由于数组有长度限制,为了达到循环生产和循环消费的目的,ArrayBlockingQueue用到了循环数组。
有界:阻塞队列中能够容纳的元素个数,通常情况下是有界的,ArrayBlockingList,可以在构造方法中传入一个整形的数字,表示这个基于数组的阻塞队列中能够容纳的元素个数,这种就是有界队列。
无界:就是没有设置固定大小的队列,不过并不是像我们理解的那种元素没有任何限制,而是它的元素存储量很大,像LinkedBlockingQueue,它的默认长度是Integer.Max_Value。
分布式锁,是一种跨进程的跨机器节点的互斥锁,它可以用来保证多机器节点对于共享资源的排他性。分布式锁和线程锁本质上是一样的,线程锁的生命周期是单进程多线程,分布式锁的生命周期是多进程多机器节点。
二者都满足以下特性:
Redis里面提供了SETNX命令可以实现锁的排他性,当key不存在就返回1,存在就返回0,可以用expire命令设置锁的失效时间,从而避免死锁问题。
Redisson这个开源组件,就提供了分布式锁的封装实现,并且内置了一个Watch Dog机制来对key做续期
幂等是指一个方法被多次重复执行的时候产生的影响和第一次执行的影响相同。
幂等的核心思想就是保证这个接口的执行结果只影响一次,后续即便再次调用,也不能对数据产生影响。
在线程池内部,当我们把一个任务丢给线程池去执行,线程池会调度工作线程来执行这个任务的run方法,run方法正常结束,也就意味着任务完成了,所以线程池中的工作线程是通过同步调用任务的run()方法并且等待run方法返回后,再去统计任务的完成数量。
如果想在线程池外部去获得线程池内部任务的执行状态,有几种方法可以实现:
每天都要有新的希望,如同重获新生一般!
| 留言与评论(共有 0 条评论) “” |