1、使用lock定制化通信
期望:有三个线程,分别为A、B、C线程,A线程打印5次后通知B打印10次后通知C打印15次,以此打印10轮
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class lock02 {
public static void main(String[] args) {
lock33 l=new lock33();
new Thread(()->{
for (int i = 1; i <=10 ; i++) {
l.print5(i);
}
},"A").start();
new Thread(()->{
for (int i = 1; i <=10 ; i++) {
l.print10(i);
}
},"B").start();
new Thread(()->{
for (int i = 1; i <=10 ; i++) {
l.print15(i);
}
},"C").start();
}
}
class lock33{
int falg=1;
Lock lock=new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();
public void print5(int num){
lock.lock();
try {
while (falg!=1){
conditionA.await();
}
for (int i = 1; i <= 5; i++) {
System.out.println(Thread.currentThread().getName()+"打印第"+num+"轮,打印"+i+"次");
}
falg=2;
conditionB.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print10(int num){
lock.lock();
try {
while (falg!=2){
conditionB.await();
}
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName()+"打印第"+num+"轮,打印"+i+"次");
}
falg=3;
conditionC.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print15(int num){
lock.lock();
try {
while (falg!=3){
conditionC.await();
}
for (int i = 1; i <= 15; i++) {
System.out.println(Thread.currentThread().getName()+"打印第"+num+"轮,打印"+i+"次");
}
falg=1;
conditionA.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
2、List集合线程不安全演示
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class lock03 {
public static void main(String[] args) {
List<String>list=new ArrayList<>();
for (int i = 0; i < 30; i++) {
new Thread(()->{
//向集合中添加数据
list.add(UUID.randomUUID().toString().substring(0,8));
//取出数据
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
报错如下
此异常为java.util.ConcurrentModificationException并发修改异常,因为我们是多线程的,有可能是,有现成在存数据的同时有的再取,故此有这个异常
解决方式一:使用Vector ,他是线程安全的,原理是他的方法都加了Synchronized修饰(此方法较老,jdk1.0)
解决方式二:使用Collections工具类(此方法较老)
解决方式三:CopyOnWriteArrayList写时复制技术,原理就是在写时候,复制一份新的数组,在新的数组中写数据,写完后将当前数组覆盖之前的数组(推荐使用)
3、HashSet和HashMap线程不安全案例及解决办法
解决方法: 使用CopyOnWriteArraySet
hashMap线程不安全
解决方法: 使用ConcurrentHashMap
4、synchronized实现同步基础:
Java中每一个对象都一乐作为锁,具体表现为以下三种形式
a、对于普通同步方法,锁是当前实力对象
b、对于静态同步方法,锁是当前类,也就是Class对象
c、对于同步方法块,锁是synchronized括号里配置的对象
5、非公平锁和公平锁
非公平锁:比如有三个窗口卖票,分别是A、B、C,总共三十个票都被A一个窗口卖了,就是非公平锁,默认为非公平锁
公平锁:就是保证A、B、C窗口都会卖出一些票,不会存在饥饿线程问题,实现线程的公平锁只需要ReentrantLock(true)
总结:非公平锁可能会存在饥饿线程,但是效率高,公平锁是阳光普照,都有机会干活,但是效率相对来说会低一点
6、可重入锁
synchronized和Lock都是可重入锁,但是synchronized是自动上锁和解锁的,所以也叫隐式锁,lock上锁和解锁都是需要手动的,所以也叫显示锁
public class SyncLockDemo {
public static void main(String[] args) {
Object o=new Object();
new Thread(()->{
synchronized (o){
System.out.println(Thread.currentThread().getName()+"进入外层");
synchronized (o){
System.out.println(Thread.currentThread().getName()+"进入中层");
synchronized (o){
System.out.println(Thread.currentThread().getName()+"进入内层");
}
}
}
},"A").start();
}
}
lock完成可重入锁代码
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class lockDemo1 {
public static void main(String[] args) {
Lock lock=new ReentrantLock();
new Thread(()->{
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+"进入外层");
try {
lock.lock();
System.out.println(Thread.currentThread().getName()+"进入内层");
}finally {
lock.unlock();
}
}finally {
lock.unlock();
}
},"A").start();
}
}
lock切记需要手动关闭锁,不然后续线程拿不到锁会阻塞,如下图,由于内层锁未解锁,所以下边的线程B拿不到锁,也就无法执行造成堵塞jvm未结束
7、什么叫死锁
死锁就是有两个或两个以上的线程在竞争资源的时候造成的等待状态的现象,无外力干涉的情况下不可解得现象,叫死锁,比如A在等B的锁,B在等A的锁,互相等待
8、产生死锁的原因
a、系统资源不足
b、进程运行推进顺序不合适
c、资源分配不当
public class syncDemo2 {
static Object a=new Object();
static Object b=new Object();
public static void main(String[] args) {
new Thread(()->{
synchronized (a){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"准备获取B资源");
synchronized (b){
System.out.println("A获取到了B");
}
}
},"A").start();
new Thread(()->{
synchronized (b){
System.out.println(Thread.currentThread().getName()+"准备获取A资源");
synchronized (a){
System.out.println("B获取到了A");
}
}
},"B").start();
}
}
由以上代码运行,得到以下图结果,由于线程A已持有a锁,线程B已持有b锁,那么A线程试图去获取b的资源锁失败,B线程也试图去获取a的资源锁也失败,造成了互相等待对方释放的状态,此状态现场就是典型的死锁
9、验证有无死锁
第一步使用 jps -l 命令,查看所有在运行的进程及进程编号
第二步使用 jstack 进程编号,该命令是jvm自带的堆栈跟踪工具,如(jstack 14484) ,如下图所示,Found 1 deadlock.找到一个死锁