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,依次循环,就是活锁
饥饿:由于某些原因导致某些线程不能被调用,例如某一个线程的优先级极低,而一直不被调度