Java中的每一个对象都可以作为锁。
具体表现为以下3种形式。
//创建非公平锁
//可能一个线程吃掉所有任务 其他线程被“饿死” 但效率高
private final ReentrantLock lock = new ReentrantLock(false);
//创建公平锁
//每个线程都领到任务 但效率降低
private final ReentrantLock lock = new ReentrantLock(true);
同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提是锁对象得是同一个对象),不会因为之前已经获取过锁还没有释放而发生阻塞
class RLock {public synchronized void add(){add();}
}
public class Demo06 {public static void main(String[] args) {new RLock().add();}
}
此时不会堵塞 只会栈溢出java.lang.StackOverflowError
两个或者两个以上进程在执行过程中,因为争夺资源而造成一种互相等待的现象。如果没有外力干涉,他们无法再执行下去。

产生死锁原因:
import java.util.concurrent.TimeUnit;public class DeadLock {static Object o1=new Object();static Object o2=new Object();public static void main(String[] args) {new Thread(()->{synchronized (o1){System.out.println(Thread.currentThread().getName()+"持有o1,试图获取o2");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}synchronized (o2){System.out.println(Thread.currentThread().getName()+"获取o2");}}},"aa").start();new Thread(()->{synchronized (o2){System.out.println(Thread.currentThread().getName()+"持有o2,试图获取o1");try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}synchronized (o1){System.out.println(Thread.currentThread().getName()+"获取o1");}}},"bb").start();}
}
jps -l找到进程号jstack 进程号在最后提示Found xxx deadlock.发现xx个死锁

通过使用Runnable 创建线程。但是,Runnable 缺少的一项功能是,当线程终止时(即run ( )完成时),我们无法使线程返回结果。为了支持此功能,Java中提供了Callable接口。
| Runnable | Callable | |
|---|---|---|
| 是否有返回值 | 无 | 有 |
| 是否抛出异常 | 无 | 有 |
| 实现方法 | run() | call() |
FutureTask类 实现了RunnableFuture接口,而RunnableFuture 继承了Runnable和Future接口,所以本质上 FutureTask是Runnable的一种实现。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;//比较Callable Runable
class MyThread1 implements Runnable{@Overridepublic void run() {}
}
class MyThread2 implements Callable{@Overridepublic Integer call() throws Exception {return 200;}
}
public class Demo1 {public static void main(String[] args) throws ExecutionException, InterruptedException {//创建Runable线程new Thread(new MyThread1(),"aa").start();//创建Callable线程//不能直接替换runnable的方式,因为Thread类的构造方法根本没有Callable.//FutureTask构造可以传递callable//创建FutureTask线程FutureTask futureTask1=new FutureTask<>(new MyThread2());//lambda简化FutureTask线程FutureTask futureTask2=new FutureTask<>(()->{System.out.println(Thread.currentThread().getName()+" in Callable");return 1024;});new Thread(futureTask2,"bb").start();while (!futureTask2.isDone()){System.out.println("wait");}System.out.println(futureTask2.get());System.out.println(futureTask1.get());System.out.println(Thread.currentThread().getName()+" over");}
}
CountDownLatch类可以设置一个计数器,然后通过countDown方法来进行减1的操作,使用await方法等待计数器不大于0,然后继续执行await方法之后的语句。
案例:6个同学陆续离开教室后才可以关门
正常写法
public class CountDownLatchDemo {public static void main(String[] args) {//6个同学陆续离开教室之后for (int i = 1; i <=6; i++) {new Thread(()->{System.out.println(Thread.currentThread( ).getName()+"号同学离开了教室");},String.valueOf(i)).start();}System.out.println(Thread.currentThread( ).getName()+"锁门了");}
}
会出现问题:

更改之后:
import java.util.concurrent.CountDownLatch;public class CountDownLatchDemo {public static void main(String[] args) throws InterruptedException {//创建CountDownLatchCountDownLatch countDownLatch=new CountDownLatch(6);//6个同学陆续离开教室之后for (int i = 1; i <=6; i++) {new Thread(()->{System.out.println(Thread.currentThread( ).getName()+"号同学离开了教室");countDownLatch.countDown();},String.valueOf(i)).start();}//等待countDownLatch.await();System.out.println(Thread.currentThread( ).getName()+"锁门了");}
}

允许一组线程全部等待彼此达到共同屏障点的同步辅助。循环阻塞在涉及固定大小的线程方的程序中很有用,这些线程必须偶尔等待彼此。屏障被称为循环 ,因为它可以在等待的线程被释放之后重新使用。
例子:集齐7颗龙珠就可以召唤神龙
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;public class CyclicBarrierDemo {//创建固定值private static final int NUMBER = 7;public static void main(String[] args) {CyclicBarrier cyclicBarrier=new CyclicBarrier(NUMBER,()->{System.out.println("已经集齐7颗龙珠,可以召唤神龙了");});for (int i = 1; i <=7; i++) {new Thread(()->{try {System.out.println(Thread.currentThread( ) .getName()+"星龙被收集到了");//等待cyclicBarrier.await();} catch (Exception e) {e.printStackTrace();}},String.valueOf(i)).start();}}
}

一个单向隧道能同时容纳10个小汽车或5个卡车通过(1个卡车等效与2个小汽车), 而隧道入口记录着当前已经在隧道内的汽车等效比重. 比如1个小汽车和1个卡车, 则隧道入口显示3. 若隧道入口显示10表示已经满了. 当汽车驶出隧道之后, 隧道入口显示的数字则会相应的减小. 于这个示例相符合场景非常适合用信号量.
案例:抢车位,6部汽车3个停车位
import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;public class SemaphoreDemo {public static void main(String[] args) {Semaphore semaphore=new Semaphore(3);for (int i = 1; i <=6; i++) {new Thread(()->{try {//抢占semaphore.acquire();System.out.println(Thread.currentThread( ) .getName()+"抢到了车位");//设置随机停车时间TimeUnit.SECONDS.sleep(new Random().nextInt(5));System.out.println(Thread.currentThread( ) .getName()+"离开了车位");} catch (InterruptedException e) {e.printStackTrace();}finally {semaphore.release();}},String.valueOf(i)).start();}}
}