1.进程与线程:
线程:一个进程可以拥有多个并行的线程,线程是进程中一个程序执行控制的单元
进程:由cpu,data,code三部分组成,每个进程都是独立的,由系统分配,进程间的切换开销较大,进程基于操作系统;
2.并行与并发
并发:多个线程访问同一份资源
并行:在多个cpu情形下,多个线程同时运行
3.线程数与运行时间
线程数:我们看到的虚拟机里面显示的线程数,是虚拟的线程数,例如单线程,他是将时间分成很多小的时间片,不同的时间片会调用不同的虚拟的线程程序段,当调用这个程序是其他程序会挂起
运行时间:运行时间与实际的线程数有直接联系,过多的线程容易引起并发等问题,所以不一定快
4.线程间的通信
通信机制有两种:共享内存和消息传递
共享内存的并发模型里,线程之间共享程序的公共状态,通过写-读内存中的公共状态进行隐式通信。在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过发送消息来显式进行通信。Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式进行,整个通信过程对程序员完全透明,
5.内存的可见性
堆内存在线程之间共享。局部变量,方法定义参数和异常处理器参数不会在线程之间共享,它们不会有内存可见性问题,也不受内存模型的影响。线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存,本地内存中存储了该线程以读/写共享变量的副本。
6.实现多线程的3种方式
Thread类:
步骤:
1>定义类继承Thread类;
2>复写run方法;
3>创建Thread类的子类对象来创建线程对象;
4>调用线程的start方法,开启线程,并执行run方法。
备注 : sleep 和wait的区别,一个会释放锁,一个不会释放锁
public class JoinTest { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread( new JoinTestA(),"线程1"); thread.start(); thread.join(); System.out.println("hello world"); } } class JoinTestA implements Runnable { @Override public void run() { System.out.println("hello join"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class DaemonTest2 { public static void main(String[] args) { Thread thread = new Thread(new DaemonTestB() );
//设置为守护线程的时候 ,需要是没有开启的线程,否则会报错, thread.setDaemon(true); thread.start(); new Thread(new DaemonTestA() ).start(); } } class DaemonTestA implements Runnable { @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(1111); } } class DaemonTestB implements Runnable { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(2222); } }
Runnable接口
步骤:
1>定义类实现Runnable接口。
2>覆盖接口中的run方法。
3>通过Thread类创建线程对象;
4>将实现了Runnable接口的子类对象作为实际参数传递给Thread类中的构造函数。
5>调用线程的start方法,开启线程,并执行run方法。
Callable接口
步骤:
1>定义类实现callable接口。
2>复写接口中的call方法。
3>创建FutureTask对象,并且将实现了callable作为对象传入
4> 将FutureTask对象作为实际参数传递给Thread类中的构造函数
5>调用线程的start方法,开启线程,并执行call方法
备注:Callable具有返回值,但还是相应的FutureTask的get方法是一个阻塞式的方法,需要获取在start之后有值,假如在start之前书写get方法,那么一直会处于阻塞状态,当我们在线程池中使用的是FutureTask对象,那么submit后的引用的get方法取不到值,但可以用FutureTask对象的get方法去得到值,只有使用callable的submit后的引用的get方法能取到值
//jion方法
public class ThreadTest { public static void main(String[] args) throws InterruptedException, ExecutionException { ThreadTestA threadTestA = new ThreadTestA(); threadTestA.setName("A"); threadTestA.start(); Thread threadB = new Thread(new ThreadTestB(), "B"); threadB.start(); FutureTask<String> futureTask = new FutureTask<>(new ThreadTestC()); Thread threadC = new Thread(futureTask, "C"); threadC.start(); String string = futureTask.get(); System.out.println(string); } } class ThreadTestA extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName() + ":hello thread"); } } class ThreadTestB implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + ":hello runnable"); } } class ThreadTestC implements Callable<String> { @Override public String call() throws Exception { return Thread.currentThread().getName() + ":HELLO CALLABLE"; } }
//sleep和wait的区别
//sleep 不会释放锁,也不会释放线程 处于阻塞状态
//wait 会释放锁,不会释放线程,处于阻塞状态
public class SleepAndWait { public static void main(String[] args) throws InterruptedException { SleepTest sleepTest = new SleepTest(); CountDownLatch countDownLatch = new CountDownLatch(2); long start = System.currentTimeMillis(); for (int i = 0; i < 2; i++) { new Thread(new Runnable() { public void run() { try { sleepTest.testA(); } catch (InterruptedException e) { e.printStackTrace(); } countDownLatch.countDown(); } }).start(); } countDownLatch.await(); long end = System.currentTimeMillis(); System.out.println(end - start);// 4001 WaitTest waitTest = new WaitTest(); CountDownLatch countDownLatch1 = new CountDownLatch(2); start = System.currentTimeMillis(); for (int i = 0; i < 2; i++) { new Thread(new Runnable() { public void run() { try { System.out.println(Thread.currentThread().getName() + ":hello"); // 回到阻塞状态 waitTest.testA(); } catch (InterruptedException e) { e.printStackTrace(); } countDownLatch1.countDown(); } }).start(); } countDownLatch.await(); end = System.currentTimeMillis(); System.out.println(end - start);// 4001 } } class SleepTest { public synchronized void testA() throws InterruptedException { Thread.sleep(1000); ; } } class WaitTest { public synchronized void testA() throws InterruptedException { this.wait(); } }
//yield会放弃线程,然后调用优先级较高的,但这个方法不是绝对可行的,只是概率型事件
//相应的也会将相应的位置放入栈中,下次调度的时候回到相应的位置
public static void main(String[] args) { A a = new A(); Thread threadA = new Thread(a,";程A"); B b = new B(); Thread threadB = new Thread(b,";程B"); threadA.start(); threadB.start(); } } class A implements Runnable{ @Override public void run() { System.out.println(1); Thread.yield(); System.out.println(2); } } class B implements Runnable{ @Override public void run() { System.out.println(3); Thread.yield(); System.out.println(4); } }//可能会调用到自己 3 4 1 2的出现
7.线程池基本使用
7.1向线程池中添加线程,需实现了callable接口或者runnable接口
线程池的体系结构:
java.util.concurrent.Executor : 负责线程的使用与调度的根接口
ExecutorService : 线程池的主要接口
ThreadPoolExecutor 线程池的实现类
ScheduledExecutorService :负责线程的调度
ScheduledThreadPoolExecutor :继承 ThreadPoolExecutor, 实现 ScheduledExecutorService
7.2工具类:Executors
ExecutorService newFixedThreadPool() : 创建固定大小的线程池
ExecutorService newCachedThreadPool() : 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。
ExecutorService newSingleThreadExecutor() : 创建单个线程池。线程池中只有一个线程
ScheduledExecutorService newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时的执行任务。
备注一下:api方法前带new表示新建的对象,不带new表示原对象
public class ThreadTest { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(2);//创建固定的线程池 newFixedThreadPool.submit(new ThreadTestA()); newFixedThreadPool.submit(new ThreadTestB()); Future<String> submit = newFixedThreadPool.submit(new ThreadTestC()); String string2 = submit.get(); System.out.println(string2); newFixedThreadPool.shutdown();//线程执行完成后关闭,不在接受新的任务 shutdownNow试图关闭所有正在执行的任务 ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(5); //每个10s执行一次,初始在1s后执行 newScheduledThreadPool.scheduleWithFixedDelay(new ThreadTestA(), 1, 10, TimeUnit.SECONDS); //一秒后执行执行一次 newScheduledThreadPool.schedule(new ThreadTestB(), 1, TimeUnit.SECONDS);
newFixedThreadPool.shutdown();
newScheduledThreadPool.shutdown();
} } class ThreadTestA extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName() + ":hello thread"); } } class ThreadTestB implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName() + ":hello runnable"); } } class ThreadTestC implements Callable<String> { @Override public String call() throws Exception { return Thread.currentThread().getName() + ":HELLO CALLABLE"; } }
8.线程池的基本原理
1)线程池判断核心线程池里的线程是否都在执行任务。如果没有则创建核心线程去执行任务,如果核心线程池里的线程都在执行任务;然后线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,然后线程池判断线程池的最大的线程数是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。线程超出 maximumPoolSize,在这种情况下,任务将被拒绝;然后多于 corePoolSize 的线程,则这些多出的线程在空闲时间超过 keepAliveTime 时将会终止.
线程池的底层实现:
volatile int runState; static final int RUNNING = 0; static final int SHUTDOWN = 1; static final int STOP = 2; static final int TERMINATED = 3;
当线程数小于核心线程数的时候,如下会创建一个线程去执行,创建后处于死循环状态
private boolean addIfUnderCorePoolSize(Runnable firstTask) { Thread t = null; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { if (poolSize < corePoolSize && runState == RUNNING) t = addThread(firstTask); //创建线程去执行firstTask任务 } finally { mainLock.unlock(); } if (t == null) return false; t.start(); return true; }
当大于核心线程数而阻塞队列没有满时,死循环的线程不断从阻塞队列中获取任务
public void run() { try { Runnable task = firstTask; firstTask = null; while (task != null || (task = getTask()) != null) { runTask(task); task = null; } } finally { workerDone(this); } }
Runnable getTask() { for (;;) { try { int state = runState; if (state > SHUTDOWN) return null; Runnable r; if (state == SHUTDOWN) // Help drain queue r = workQueue.poll(); else if (poolSize > corePoolSize || allowCoreThreadTimeOut) //如果线程数大于核心池大小或者允许为核心池线程设置空闲时间, //则通过poll取任务,若等待一定的时间取不到任务,则返回null r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS); else r = workQueue.take(); if (r != null) return r; if (workerCanExit()) { //如果没取到任务,即r为null,则判断当前的worker是否可以退出 if (runState >= SHUTDOWN) // Wake up others interruptIdleWorkers(); //中断处于空闲状态的worker return null; } // Else retry } catch (InterruptedException ie) { // On interruption, re-check runState } } }
如果线程池处于STOP状态、或者任务队列已为空或者设置allowCoreThreadTimeout为true时(核心线程也退出,假如设置为false,默认为false,核心线程数一般不会关闭),并且线程数大于1时,允许worker退出。如果允许worker退出,则调用interruptIdleWorkers()中断处于空闲状态的worker
void interruptIdleWorkers() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { for (Worker w : workers) //实际上调用的是worker的interruptIfIdle()方法 w.interruptIfIdle(); } finally { mainLock.unlock(); } }
void interruptIfIdle() { final ReentrantLock runLock = this.runLock; if (runLock.tryLock()) { //注意这里,是调用tryLock()来获取锁的,因为如果当前worker正在执行任务,锁已经被获取了,是无法获取到锁的 //如果成功获取了锁,说明当前worker处于空闲状态 try { if (thread != Thread.currentThread()) thread.interrupt(); } finally { runLock.unlock(); } } }
备注:也就是说能添加的最大的任务数是最大的线程数+BlockingQueue工作队列
2)被拒绝执行任务时的策略
ThreadPoolExecutor.AbortPolicy 丢弃任务,并抛出 RejectedExecutionException 异常。
ThreadPoolExecutor.CallerRunsPolicy:该任务被线程池拒绝,由调用 execute方法的线程执行该任务。
ThreadPoolExecutor.DiscardOldestPolicy : 抛弃队列最前面的任务,然后重新尝试执行任务。
ThreadPoolExecutor.DiscardPolicy,丢弃任务,不过也不抛出异常。
3)线程池都是通过 ThreadPoolExecutor这个核心类来创建的,我们自定义线程池也可以用这个类来实现,最终是通过ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)来实现的,在newFixedThreadPool和newSingleThreadExecutor以及newCachedThreadPool都是使用的默认的RejectedExecutionHandler defaultHandler =new AbortPolicy()策略。
4)自定义线程池
public class MyExcutorsTest { public static void main(String[] args) { // ThreadPoolExecutor(int corePoolSize, 核心线程数 // int maximumPoolSize, 最大线程数 // long keepAliveTime, 超过核心线程数小于最大线程数的保持空闲时间 // TimeUnit unit, 时间单位 // BlockingQueue<Runnable> workQueue, 阻塞队列 // ThreadFactory threadFactory, 线程工厂 // RejectedExecutionHandler handler) 被拒绝执行任务时的策略 ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 2, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<>(3), Executors.defaultThreadFactory(), new AbortPolicy()); for (int i = 0; i < 6; i++) { threadPoolExecutor.submit(new MyExcutorsTest().new MyExcutorsTestA()); /* * 结果 2(最大)+3(阻塞队列)=5大于五抛出异常 pool-1-thread-1 pool-1-thread-2 * pool-1-thread-1 pool-1-thread-2 pool-1-thread-1 * //抛出异常java.util.concurrent.RejectedExecutionException异常 */ } threadPoolExecutor.shutdown(); } class MyExcutorsTestA implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
9.BlockingQueue:阻塞队列,实现是线程安全的
1)定义:支持阻塞的插入方法:当阻塞队列已满时,队列会阻塞插入元素的线程,直到队列不满;支持阻塞的移除方法:队列为空时,获取元素的线程会等待队列变为非空。类似于一个生产者和消费者模式,生产者类似于插入,消费者类似于移除,而队列就是仓库
2)对于队列已满时继续插入具有4种策略
抛出异常:当队列满时,如果再往队列里插入元素,会抛出IllegalStateException("Queuefull")异常。当队列空时,从队列里获取元素会抛出NoSuchElementException异常。add,remove,element方法
返回特殊值:当往队列插入元素时,会返回元素是否插入成功,成功返回true。如果是移除方法,则是从队列里取出一个元素,如果没有则返回null。offer poll peek 方法
一直阻塞:当阻塞队列满时,如果生产者线程往队列里put元素,队列会一直阻塞生产者线程,直到队列可用或者响应中断退出。当队列空时,如果消费者线程从队列里take元素,队列会阻塞住消费者线程,直到队列不为空。 put take
超时退出:当阻塞队列满时,如果生产者线程往队列里插入元素,队列会阻塞生产者线程一段时间,如果超过了指定的时间,生产者线程就会退出。 offer poll方法
备注:如果是无界阻塞队列,队列不可能会出现满的情况,所以使用put或offer方法永远不会被阻塞,而且使用offer方法时,该方法永远返回true。
3)java中提供了7个阻塞队列
ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。按照先进先出(FIFO)的原则对元素进行排序。在默认插入队列时并不保证公平性,可以设置
LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列则,默认值是 Integer.MAX_VALUE,(newFixedThreadPool和newSingleThreadExecutor使用的就是这种队列),此队列按照先进先出的原则对元素进行排序
PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。默认情况下元素采取自然顺序升序排列
DelayQueue:一个使用优先级队列实现的无界阻塞队列。
SynchronousQueue:一个不存储元素的阻塞队列,newCachedThreadPool使用的就是这种队列),每一个put操作必须等待一个take操作,否则不能继续添加元素,默认情况下线程采用非公平性策略访问队列,使用以下构造方法可以创建公平性访问的SynchronousQueue,如果设置为true,则等待的线程会采用先进先出的顺序访问队列。
LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。
10.volatile与synchronized及atomic
volatile:只保证内存的可见性,并不保证原子性,synchronized的轻量级算法
synchronized:保证内存的可见性以及原子性
atomic:cas算法保证内存的可见性以及原子性
cas算法:有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
public class VolatileTest2 { //死循环输出,因为while(true)的效率是非常的高的,验证共享机制 @Test public void test() { VolatileTestC volatileTestC = new VolatileTestC(); new Thread(volatileTestC).start(); while(true){ if(VolatileTestC.flag2){ System.out.println("flag2"); break; } } } } //每个线程自己有自己的内存,--验证共享内存机制 class VolatileTestC implements Runnable{ public static boolean flag2; @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } flag2=true; System.out.println("flag=true"); } }
public class VolatileTest2 { //死循环输出,因为while(true)的效率是非常的高的 @Test public void test() { VolatileTestC volatileTestC = new VolatileTestC(); new Thread(volatileTestC).start(); while(true){ if(VolatileTestC.flag2){ System.out.println("flag2"); break; } } } } //每个线程自己有自己的内存,--验证共享内存机制 --volatile保证内存的可见性 class VolatileTestC implements Runnable{ public static volatile boolean flag2; @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } flag2=true; System.out.println("flag=true"); } }
//volatile不能保证原子性
public class VolatileAtomicTest { public static void main(String[] args) {
VolatileAtomicTestA volatileAtomicTestA=new VolatileAtomicTestA(); for (int i = 0; i < 10; i++) { new Thread(volatileAtomicTestA).start(); } } } class VolatileAtomicTestA implements Runnable { public volatile static int i=10; @Override public void run(){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+":"+test()); } public int test(){ return i--; } }
/*输出
Thread-0:10
Thread-2:10
Thread-1:9
Thread-5:8
Thread-6:5
Thread-3:6
Thread-9:7
Thread-4:4
Thread-8:3
Thread-7:2
*/
public class AtomicTest { public static void main(String[] args) { AtomicTestA atomicTestA = new AtomicTestA(); for (int i = 0; i < 10; i++) { new Thread(atomicTestA).start(); } } } class AtomicTestA implements Runnable { //AtomicInteger 使用的是对象锁 public AtomicInteger atomicInteger =new AtomicInteger (10); @Override public void run(){ System.out.println(Thread.currentThread().getName()+":"+test()); } //atomicInteger public int test(){ return atomicInteger.decrementAndGet(); } }
/*
Thread-0:8
Thread-5:4
Thread-3:9
Thread-4:5
Thread-1:7
Thread-2:6
Thread-7:2
Thread-6:3
Thread-8:1
Thread-9:0
*/
public class TestCompareAndSwap { public static void main(String[] args) { CompareAndSwap cas = new CompareAndSwap(); for (int i = 0; i < 100; i++) { new Thread(new Runnable() { @Override public void run() { int expectedValue = cas.get(); //会争抢锁 //假如在这里加一个线程延时那么会基本上是false boolean b = cas.compareAndSet(expectedValue, (int)(Math.random() * 101)); System.out.println(b); } }).start(); } } } class CompareAndSwap{ private int value; //获取内存值 public synchronized int get(){ return value; } //比较 public synchronized int compareAndSwap(int expectedValue, int newValue){ int oldValue = value; if(oldValue == expectedValue){ this.value = newValue; } return oldValue; } //设置 public synchronized boolean compareAndSet(int expectedValue, int newValue){ return expectedValue == compareAndSwap(expectedValue, newValue); } }
public class StaticLockAndLock { public static void main(String[] args) throws InterruptedException { StaticLockAndLockTest staticLockAndLockTest = new StaticLockAndLockTest(); CountDownLatch countDownLatch = new CountDownLatch(2); long start = System.currentTimeMillis(); new Thread(new Runnable() { public void run() { try { StaticLockAndLockTest.testA(); } catch (InterruptedException e) { e.printStackTrace(); } countDownLatch.countDown(); } }).start(); new Thread(new Runnable() { public void run() { try { staticLockAndLockTest.testB(); } catch (InterruptedException e) { e.printStackTrace(); } countDownLatch.countDown(); } }).start(); countDownLatch.await(); long end = System.currentTimeMillis(); System.out.println(end - start); } } class StaticLockAndLockTest { // 使用的锁是字节码--类锁 public static synchronized void testA() throws InterruptedException { Thread.sleep(1000); System.out.println(111111111); } // 使用的锁是对象---对象锁 public synchronized void testB() throws InterruptedException { Thread.sleep(1000); System.out.println(222222222); } } /* * 输出结果 111111111 * 22222222 * 1002 */
11.Lock
public class ReentrantlockTest { public static void main(String[] args) { ReentrantlockTestA reentrantlockTestA = new ReentrantlockTestA(); for (int i = 0; i < 2; i++) { new Thread(new Runnable() { public void run() { try { reentrantlockTestA.testA(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } } } class ReentrantlockTestA { Lock lock = new ReentrantLock(); int i; public void testA() throws InterruptedException { lock.lock(); try { System.out.println(Thread.currentThread().getName() + ":" + i); Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + ":" + ++i); } finally { lock.unlock(); } } } /* * 输出 * Thread-0:0 * Thread-0:1 * Thread-1:1 * Thread-1:2 */ /* * 去除lock后的输出 * Thread-0:0 * Thread-1:0 * Thread-1:1 * Thread-0:1 */
//abc输出
public class demo7 { public static void main(String[] args) { print print = new print(); new Thread(new Runnable() { public void run() { for (int i = 1; i <=3; i++) { print.loopA(i); } } }, "A").start(); new Thread(new Runnable() { public void run() { for (int i = 1; i <= 3; i++) { print.loopB(i); } } }, "B").start(); new Thread(new Runnable() { public void run() { for (int i = 1; i <= 3; i++) { print.loopC(i); } } }, "C").start(); } } // alternate class print { private int number = 1; private Lock lock = new ReentrantLock(); private Condition condition1 = lock.newCondition(); private Condition condition2 = lock.newCondition(); private Condition condition3 = lock.newCondition(); public void loopA(int i) { lock.lock(); try { if (number != 1) { condition1.await(); } for (int j = 1; j <= 10; j++) { System.out.println(Thread.currentThread().getName() + " " + j + " " + i); } number = 2; condition2.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void loopB(int i) { lock.lock(); try { if (number != 2) { condition2.await(); } for (int j = 1; j <= 10; j++) { System.out.println(Thread.currentThread().getName() + " " + j + " " + i); } number = 3; condition3.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void loopC(int i) { lock.lock(); try { if (number != 3) { condition3.await(); } for (int j = 1; j <= 10; j++) { System.out.println(Thread.currentThread().getName() + " " + j + " " + i); } number = 1; condition1.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }
public class ReadAndWriteLock { // 读写互斥 // 写写互斥 // 读读不互斥 public static void main(String[] args) { ReadAndWriteLockA readAndWriteLockA = new ReadAndWriteLockA(); new Thread(new Runnable() { public void run() { readAndWriteLockA.testWrite(); } }).start(); for (int i = 0; i < 100; i++) { new Thread(new Runnable() { public void run() { readAndWriteLockA.testRead(); } }).start(); } } } class ReadAndWriteLockA { private int i; ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); public void testRead() { readWriteLock.readLock().lock(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i); readWriteLock.readLock().unlock(); } public void testWrite() { readWriteLock.writeLock().lock(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("write"); this.i = 1; readWriteLock.writeLock().unlock(); } }
12.其他锁语义说明
互斥锁:只能一个进入其他不可以进入,当进入失败,就进入休眠等待休眠结束后才能去尝试访问
自旋锁:只能一个进入其他不可以进入,当进入失败,不停的循环去尝试访问
悲观锁:表锁,原理见redis linux 基础入门
乐观锁:行锁,原理见redis linux 基础入门
13.同步异步,阻塞和非阻塞
同步:我叫你某件事,你没去,我就一直喊
异步:我叫你做某件事后我做别的事情去了,至于你有没有做跟我无关
阻塞:我去做某事,堵车我就一直等着
非阻塞:我去做某事,堵车我就做别的事情,不堵车了再来
14.ConcurrentHashMap
分段锁机制(16段),较hashtable效率盖
15.死锁,活锁和饥饿
死锁:一个需要资源A,一个需要资源B,拿到资源A的人需要拿资源B,不释放A,拿到B的人需要拿资源A,不释放B,这样就都拿不到
活锁:一个需要资源A,一个需要资源B,拿到资源A的人需要拿资源B而拿不到资源B,所以释放A,拿到B的人需要拿资源A,而拿不到资源A,所以释放B,然后发现拿到了B,另一个拿到了A,拿到A的人结果发现拿不到B,拿到B的人结果发现拿不到A,依次循环,就是活锁
饥饿:由于某些原因导致某些线程不能被调用,例如某一个线程的优先级极低,而一直不被调度