1.实现多线程的方法有多少种:
实现runnable接口, 继承thread类
runnable方法更好,:
1)从代码架构角度和thread类解耦,
2)不需要新建线程节省资源,
3)java单继承,影响继承其他类
区别:runnable接口是调用传入的target的run,继承thread是重写了run
总结:创建线程只有一种方法就是构造thread类,实现线程的执行单元有两种方式:
方式一:实现runnable接口的run方法,并把runnable实例传给thread类
方式二:重写thread类的run方法
2.正确的线程启动方式:
两次调用start方法会出现什么:异常,start有状态检查Threadstatus = 0,
为什么不直接调用run:start是真正启动了,native start0, 经历生命周期,run只是方法
3.如何停止线程:
使用interrupt通知
两种最佳实践:
1)catch了之后的优先选择:在方法签名中抛出异常,那么在run方法中就会强制try catch
public class Right implements Runnable{ @Override public void run() { while (true){ System.out.println("go"); try { throwInMethod(); } catch (InterruptedException e) { e.printStackTrace(); } } } private void throwInMethod() throws InterruptedException { Thread.sleep(2000); } public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new Right()); thread.start(); Thread.sleep(1000); thread.interrupt(); } }
2)在catch子语句中调用Thread.currentThread().interrupt()来恢复设置中断状态,以便于在后续的执行中,依然能够检查到刚才发生了中断,
public class Right2 implements Runnable{ @Override public void run() { while (true){ if(Thread.currentThread().isInterrupted()){ System.out.println("end"); break; } throwInMethod(); } } private void throwInMethod() { try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new Right2()); thread.start(); Thread.sleep(1000); thread.interrupt(); } }
错误的方法:stop, suspend, resume
volatile 设置boolean标记,无法处理长时间阻塞:
例如:生产者生产的快,消费者消费的慢,队列满了以后生产者会阻塞
package thread.stopThread.volatileDemo; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; /** * @param: * @return: * @time: 2021/7/26 */ public class WrongWayCant { public static void main(String[] args) throws InterruptedException { ArrayBlockingQueue<Object> storage = new ArrayBlockingQueue<>(10); Producer producer = new Producer(storage); Thread producerThread = new Thread(producer); producerThread.start(); Thread.sleep(1000); Consumer consumer = new Consumer(storage); while (consumer.need()){ System.out.println(consumer.storage.take()+"被消费了"); Thread.sleep(100); } System.out.println("不再需要"); producer.canceled = true; } } class Producer implements Runnable{ volatile boolean canceled = false; BlockingQueue storage; public Producer(BlockingQueue storage) { this.storage = storage; } @Override public void run() { int num = 0; try { while (num <= 100000 && !canceled) { if (num % 100 == 0) { storage.put(num); System.out.println(num + "是100的倍数放到仓库"); } num++; } } catch (InterruptedException e) { e.printStackTrace(); }finally { System.out.println("producer end"); } } } class Consumer { BlockingQueue storage; public Consumer(BlockingQueue storage) { this.storage = storage; } public boolean need() { if (Math.random() > 0.95) { return false; } else return true; } }
面试题:
如何停止线程:
用中断来请求,要请求方、被停止方、子方法被调用方相互配合,最后说错误的方法
如何处理不可中断的阻塞:
根据不同的类调用不同的方法
4.线程生命周期:
习惯把右边三种称为阻塞状态
面试题:
线程状态,生命周期
5.Thread和Object类的方法:
wait方法, notify方法:wait释放了锁
public class Wait { public static final Object object = new Object(); static class Thread1 extends Thread{ public void run(){ synchronized (object){ System.out.println(Thread.currentThread().getName()+"开始执行"); try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"获取到了锁"); } } } static class Thread2 extends Thread{ public void run(){ synchronized (object){ object.notify(); System.out.println(Thread.currentThread().getName()+" notify"); } } } public static void main(String[] args) throws InterruptedException { Thread1 thread1 = new Thread1(); Thread2 thread2 = new Thread2(); thread1.start(); Thread.sleep(200); thread2.start(); } }
notify和notifyall:
public class WaitAll implements Runnable { public static final Object resourceA = new Object(); @Override public void run() { synchronized (resourceA){ System.out.println(Thread.currentThread().getName()+"get A"); try { System.out.println(Thread.currentThread().getName()+" start"); resourceA.wait(); System.out.println(Thread.currentThread().getName()+" end"); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { WaitAll runna = new WaitAll(); Thread thread1 = new Thread(runna); Thread thread2 = new Thread(runna); Thread thread3 = new Thread(new Runnable() { @Override public void run() { synchronized (resourceA) { resourceA.notifyAll(); System.out.println("c notify"); } } }); thread2.start(); thread1.start(); Thread.sleep(200); thread3.start(); } }
wait只释放当前的锁:
package thread.Wait; /** * @param: * @return: * @time: 2021/7/29 */ public class WaitAll2 implements Runnable { public static final Object resourceA = new Object(); public static final Object resourceB = new Object(); @Override public void run() { synchronized (resourceA){ System.out.println(Thread.currentThread().getName()+"get A"); synchronized (resourceB){ System.out.println(Thread.currentThread().getName()+"get B"); try { resourceA.wait(); System.out.println(Thread.currentThread().getName()+" A end"); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) throws InterruptedException { WaitAll2 runna = new WaitAll2(); Thread thread1 = new Thread(runna); Thread thread2 = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (resourceA){ System.out.println("rel A"); synchronized (resourceB){ System.out.println("rel B"); } } } }); thread1.start(); thread2.start(); } }
wait原理:
生产者消费者:
import java.util.ArrayList; import java.util.Date; import java.util.LinkedList; import java.util.List; public class ProModel { static class EventStorage{ private int maxSize; private LinkedList<Date> storage; public EventStorage() { this.maxSize = 10; this.storage = new LinkedList<>(); } synchronized void put(){ while (storage.size() == maxSize){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } storage.add(new Date()); System.out.println("有了"+storage.size()+"个"); notify(); } synchronized void take(){ while (storage.size() == 0){ try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("取到了"+storage.poll()); System.out.println("剩下了"+storage.size()+"个"); notify(); } } static class Producer implements Runnable{ private EventStorage storage; public Producer(EventStorage storage) { this.storage = storage; } @Override public void run() { for (int i = 0; i < 100; i++) { storage.put(); } } } static class Consumer implements Runnable{ private EventStorage storage; public Consumer(EventStorage storage) { this.storage = storage; } @Override public void run() { for (int i = 0; i < 100; i++) { storage.take(); } } } public static void main(String[] args) { EventStorage storage = new EventStorage(); Producer producer = new Producer(storage); Consumer consumer = new Consumer(storage); Thread thread1 = new Thread(producer); Thread thread = new Thread(consumer); thread.start(); thread1.start(); } }
面试:交替打印0-100
package thread.Wait; /** * @param: * @return: * @time: 2021/7/29 */ public class PrintOddEven implements Runnable{ private static int count; private static final Object lock = new Object(); public static void main(String[] args) { new Thread(new PrintOddEven(), "偶数").start(); new Thread(new PrintOddEven(), "奇数").start(); } public void run() { while (count <= 100) { synchronized (lock) { System.out.println(Thread.currentThread().getName() + ":" + count++); lock.notify(); if (count <= 100) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }
面试:生产者消费者
为什么wait需要在同步代码块中使用,sleep不需要:通信可靠,防止死锁
为什么wait三个定义在object,sleep定义在thread: 每个类都是一把锁,对象头保存了monitor预留
调用Thread.wait会怎样:
sleep方法让线程进入waiting,不释放锁,休眠期间被中断抛出异常并清除中断。
面试题:wait, sleep,异同
相同:阻塞,响应中断
不同:wait在同步方法中,释放锁,指定时间,所属类
join:新的线程加入了我们,所以要等他执行完再出发。
用法:main等待子线程执行完毕
join期间线程处于waiting状态
yield方法:释放我的cpu时间片
6.线程的各个属性:
守护线程和普通线程:整体无区别,是否影响jvm退出,作用不同
是否需要设置为守护线程:不应该
7.未捕获异常:
主线程可以发现异常,子线程不可以:
子线程异常无法用传统方法捕获
解决方法:
1)手动在每个run里try
2)uncaughtExceptionHandler:
自己实现处理异常:
public class MyUncaughtException implements Thread.UncaughtExceptionHandler { private String name; public MyUncaughtException(String name) { this.name = name; } @Override public void uncaughtException(Thread t, Throwable e) { Logger logger = Logger.getAnonymousLogger(); logger.log(Level.WARNING, "异常"+t.getName(), e); System.out.println(name + "捕获了异常"+t.getName()+"异常"+e); } }
public class Use implements Runnable{ public static void main(String[] args) throws InterruptedException { Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtException("捕获器1")); new Thread(new Use(), "MyThread-1").start(); Thread.sleep(300); new Thread(new Use(), "MyThread-2").start(); Thread.sleep(300); new Thread(new Use(), "MyThread-3").start(); Thread.sleep(300); new Thread(new Use(), "MyThread-4").start(); } @Override public void run() { throw new RuntimeException(); } }
8.多线程导致的问题:
线程安全:
线程不安全:
1、运行结果错误:a++多线程消失请求现象
public class Multi implements Runnable{ static Multi instance = new Multi(); int index; static AtomicInteger realIndex = new AtomicInteger(); static AtomicInteger wrongCount = new AtomicInteger(); final boolean[] marked = new boolean[100000]; static volatile CyclicBarrier cyclicBarrier1 = new CyclicBarrier(2); static volatile CyclicBarrier cyclicBarrier2 = new CyclicBarrier(2); public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(instance); Thread thread2 = new Thread(instance); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("表面:"+instance.index); System.out.println("real : " + realIndex.get()); System.out.println("error : " + wrongCount.get()); } @Override public void run() { marked[0]=true; for (int i = 0; i < 10000; i++) { try { cyclicBarrier2.reset(); cyclicBarrier1.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } index++; try { cyclicBarrier1.reset(); cyclicBarrier2.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } realIndex.incrementAndGet(); synchronized (instance){ if (marked[index] && marked[index-1]){ System.out.println("发生错误"+index); wrongCount.incrementAndGet(); } } marked[index] = true; } } }
2、活跃性问题:死锁、活锁、饥饿
public class Multi2 implements Runnable{ int flag; static Object o1 = new Object(); static Object o2 = new Object(); public static void main(String[] args) { Multi2 r1 = new Multi2(); Multi2 r2 = new Multi2(); r1.flag = 1; r2.flag = 0; Thread thread1 = new Thread(r1); Thread thread2 = new Thread(r2); thread1.start(); thread2.start(); } @Override public void run() { if (flag==1){ synchronized (o1){ System.out.println("flag = "+flag); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o2){ System.out.println("1"); } } } if (flag==0){ synchronized (o2){ System.out.println("flag = "+flag); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o1){ System.out.println("0"); } } } } }
3、对象发布和初始化:
1)、方法返回一个private对象
public class Multi3 { private Map<String , String > states; public Multi3() { this.states = new HashMap<>(); states.put("1", "周一"); } public Map<String , String > getStates(){ return states; } public static void main(String[] args) { Multi3 multi3 = new Multi3(); Map<String, String> states = multi3.getStates(); states.remove("1"); System.out.println(states.get("1")); } }
2)、未完成初始化把对象提供给外界
构造函数没初始化
class Point{ private final int x, y; public Point(int x, int y) throws InterruptedException { this.x = x; Multi4.point = this; Thread.sleep(10); this.y = y; } @Override public String toString() { return "Point{" + "x=" + x + ", y=" + y + '}'; } } public class Multi4 { static Point point; public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(new PointMaker()); thread.start(); Thread.sleep(5); if (point != null) System.out.println(point); } } class PointMaker implements Runnable{ @Override public void run() { try { new Point(1, 1); } catch (InterruptedException e) { e.printStackTrace(); } } }
注册监听
public class MultiThreadsError5 { int count; public MultiThreadsError5(MySource source) { source.registerListener(new EventListener() { @Override public void onEvent(Event e) { System.out.println("\n我得到的数字是" + count); } }); for (int i = 0; i < 10000; i++) { System.out.print(i); } count = 100; } public static void main(String[] args) { MySource mySource = new MySource(); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } mySource.eventCome(new Event() { }); } }).start(); MultiThreadsError5 multiThreadsError5 = new MultiThreadsError5(mySource); } static class MySource { private EventListener listener; void registerListener(EventListener eventListener) { this.listener = eventListener; } void eventCome(Event e) { if (listener != null) { listener.onEvent(e); } else { System.out.println("还未初始化完毕"); } } } interface EventListener { void onEvent(Event e); } interface Event { } }
构造函数中运行线程
public class Multi6 { private Map<String , String > states; public Multi6() { new Thread(new Runnable() { @Override public void run() { states = new HashMap<>(); states.put("1", "周一"); } }).start(); } public Map<String , String > getStates(){ return states; } public static void main(String[] args) throws InterruptedException { Multi6 multi6 = new Multi6(); Thread.sleep(100); String s = multi6.getStates().get("1"); System.out.println(s); } }
解决方法:
1、返回副本,解决(方法返回一个private对象)
public Map<String , String > getImStates(){ return new HashMap<>(states); }
2、工厂模式修复(注册监听)
/** * 描述: 用工厂模式修复刚才的初始化问题 */ public class MultiThreadsError7 { int count; private EventListener listener; private MultiThreadsError7(MySource source) { listener = new EventListener() { @Override public void onEvent(MultiThreadsError5.Event e) { System.out.println("\n我得到的数字是" + count); } }; for (int i = 0; i < 10000; i++) { System.out.print(i); } count = 100; } public static MultiThreadsError7 getInstance(MySource source) { MultiThreadsError7 safeListener = new MultiThreadsError7(source); source.registerListener(safeListener.listener); return safeListener; } public static void main(String[] args) { MySource mySource = new MySource(); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } mySource.eventCome(new MultiThreadsError5.Event() { }); } }).start(); MultiThreadsError7 multiThreadsError7 = new MultiThreadsError7(mySource); } static class MySource { private EventListener listener; void registerListener(EventListener eventListener) { this.listener = eventListener; } void eventCome(MultiThreadsError5.Event e) { if (listener != null) { listener.onEvent(e); } else { System.out.println("还未初始化完毕"); } } } interface EventListener { void onEvent(MultiThreadsError5.Event e); } interface Event { } }
四种情况总结:
1、访问共享的变量资源
2、所有依赖时序的操作
3、数据之间存在捆绑关系 ip和端口号
4、使用其他类的时候,对方没有声明线程安全 hashmap
多线程性能
调度:上下文切换:内核再cpu上对于进程线程进行
1)存储进程状态 2)检索恢复进程状态 3)跳转程序计数器指向的位置
-
上下文:保存现场
-
缓存开销:缓存失效
-
密集上下文切换时机:抢锁,io
面试总结:
java内存模型
1、jvm内存结构 ,java内存模型,java对象模型
jvm内存结构和java虚拟机的运行时区域有关
java内存模型和并发有关
java对象模型和java对象在虚拟机中的表现形式有关
JMM规范
重排序
代码
*/ public class OutOfOrderExecution { private static int x = 0, y = 0; private static int a = 0, b = 0; public static void main(String[] args) throws InterruptedException { int i = 0; for (; ; ) { i++; x = 0; y = 0; a = 0; b = 0; CountDownLatch latch = new CountDownLatch(3); Thread one = new Thread(new Runnable() { @Override public void run() { try { latch.countDown(); latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } a = 1; x = b; } }); Thread two = new Thread(new Runnable() { @Override public void run() { try { latch.countDown(); latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } b = 1; y = a; } }); two.start(); one.start(); latch.countDown(); one.join(); two.join(); String result = "第" + i + "次(" + x + "," + y + ")"; if (x == 1 && y == 1) { System.out.println(result); break; } else { System.out.println(result); } } } }
可见性
/** * 描述: 演示可见性带来的问题 */ public class FieldVisibility { //错误的b=3;a=1 int a = 1; int b = 2; private void change() { a = 3; b = a; } private void print() { System.out.println("b=" + b + ";a=" + a); } public static void main(String[] args) { while (true) { FieldVisibility test = new FieldVisibility(); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } test.change(); } }).start(); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } test.print(); } }).start(); } } }
volatile 强制线程看到更改的变量
主内存和本地内存
happens-before
原则:锁操作,volatile,join
volatile
不适用a++:
/** * 描述: 不适用于volatile的场景 */ public class NoVolatile implements Runnable { volatile int a; AtomicInteger realA = new AtomicInteger(); public static void main(String[] args) throws InterruptedException { Runnable r = new NoVolatile(); Thread thread1 = new Thread(r); Thread thread2 = new Thread(r); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(((NoVolatile) r).a); System.out.println(((NoVolatile) r).realA.get()); } @Override public void run() { for (int i = 0; i < 10000; i++) { a++; realA.incrementAndGet(); } } }
不适用于依赖于之前的状态
/** * 描述: volatile不适用的情况2 */ public class NoVolatile2 implements Runnable { volatile boolean done = false; AtomicInteger realA = new AtomicInteger(); public static void main(String[] args) throws InterruptedException { Runnable r = new NoVolatile2(); Thread thread1 = new Thread(r); Thread thread2 = new Thread(r); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(((NoVolatile2) r).done); System.out.println(((NoVolatile2) r).realA.get()); } @Override public void run() { for (int i = 0; i < 10000; i++) { flipDone(); realA.incrementAndGet(); } } private void flipDone() { done = !done; } }
适用纯赋值:
/** * 描述: volatile适用的情况1 */ public class UseVolatile1 implements Runnable { volatile boolean done = false; AtomicInteger realA = new AtomicInteger(); public static void main(String[] args) throws InterruptedException { Runnable r = new UseVolatile1(); Thread thread1 = new Thread(r); Thread thread2 = new Thread(r); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println(((UseVolatile1) r).done); System.out.println(((UseVolatile1) r).realA.get()); } @Override public void run() { for (int i = 0; i < 10000; i++) { setDone(); realA.incrementAndGet(); } } private void setDone() { done = true; } }
适用触发器:
/** * 描述: */ public class FieldVisibility { int a = 1; volatile int b = 2; private void change() { a = 3; b = a; } private void print() { System.out.println("b=" + b + ";a=" + a); } public static void main(String[] args) { while (true) { FieldVisibility test = new FieldVisibility(); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } test.change(); } }).start(); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } test.print(); } }).start(); } } }
对比syn:
原子性
单例模式
饿汉式:jvm保证线程安全,类一加载就完成了
public class Singleton1 { private final static Singleton1 INSTANCE = new Singleton1(); private Singleton1(){ } public static Singleton1 getInstance(){ return INSTANCE; } } public class Singleton2 { private final static Singleton2 INSTANCE; static { INSTANCE = new Singleton2(); } private Singleton2(){ } public static Singleton2 getInstance(){ return INSTANCE; } }
懒汉式:
线程安全,性能不好
public class Singleton3 { private static Singleton3 INSTANCE; private Singleton3(){ } public synchronized static Singleton3 getInstance(){ if (INSTANCE == null){ INSTANCE = new Singleton3(); } return INSTANCE; } }
线程不安全
public class Singleton4 { private static Singleton4 INSTANCE; private Singleton4(){ } public static Singleton4 getInstance(){ if (INSTANCE == null){ INSTANCE = new Singleton4(); } return INSTANCE; } } public class Singleton5 { private static Singleton5 INSTANCE; private Singleton5(){ } // 解锁后其余线程会继续创建覆盖 public static Singleton5 getInstance(){ if (INSTANCE == null){ synchronized (Singleton5.class){ INSTANCE = new Singleton5(); } } return INSTANCE; } }
推荐使用:双重检查,
public class Singleton6 { private volatile static Singleton6 INSTANCE; private Singleton4(){ } public static Singleton6 getInstance(){ if (INSTANCE == null){ synchronized (Singleton6.class){ if (INSTANCE == null) INSTANCE = new Singleton6(); } } return INSTANCE; } }
一次检查,第四种会创建多次实例,第三种安全但是性能不好
最安全的,volatile可见性保证第二个线程能看到第一个线程给Instance做的改变,禁止重排序保证创建对象不会返回空指针
静态内部类:
public class Singleton7 { private volatile static Singleton7 INSTANCE; private Singleton7(){ } private static class SingletonInstance{ private static final Singleton7 INSTANCE = new Singleton7(); } public static Singleton7 getInstance(){ return SingletonInstance.INSTANCE; } }
枚举:推荐用
public enum Singleton8 { INSTANCE; public void whatever(){ } }
总结:
饿汉:简单,但是没有懒加载
懒汉:有线程安全问题
静态内部类:可用
双重检查:面试用
枚举:最好,写法简单,线程安全,反编译是静态对象,避免反序列化破坏单例
面试题:
总结
死锁
public class DeadLock implements Runnable{ static Object o1 = new Object(); static Object o2 = new Object(); int flag; @Override public void run() { if (flag == 1){ synchronized (o1){ System.out.println("flag = "+flag); synchronized (o2){ System.out.println("拿到o2"); } } } if (flag == 0){ synchronized (o2){ System.out.println("flag = "+flag); synchronized (o1){ System.out.println("拿到o1"); } } } } public static void main(String[] args) { DeadLock lock1 = new DeadLock(); DeadLock lock2 = new DeadLock(); lock1.flag = 1; lock2.flag = 0; Thread thread1 = new Thread(lock1); Thread thread2 = new Thread(lock2); thread1.start(); thread2.start();; } }
银行转账:
public class DeadLock1 implements Runnable{ static Account a = new Account(500); static Account b = new Account(500); int flag; static class Account{ int balance; public Account(int balance) { this.balance = balance; } } @Override public void run() { if (flag == 1){ try { transferMoney(a, b, 200); } catch (InterruptedException e) { e.printStackTrace(); } } if (flag == 0){ try { transferMoney(b, a, 200); } catch (InterruptedException e) { e.printStackTrace(); } } } private void transferMoney(Account from, Account to, int amount) throws InterruptedException { synchronized (from){ // Thread.sleep(500); synchronized (to){ if (from.balance - amount < 0){ System.out.println("余额不足"); } from.balance -= amount; to.balance += amount; System.out.println("成功转账"+amount+"元"); } } } public static void main(String[] args) throws InterruptedException { DeadLock1 lock1 = new DeadLock1(); DeadLock1 lock2 = new DeadLock1(); lock1.flag = 1; lock2.flag = 0; Thread thread1 = new Thread(lock1); Thread thread2 = new Thread(lock2); thread1.start(); thread2.start();; thread1.join(); thread2.join(); System.out.println("a的"+a.balance); System.out.println("b的"+b.balance); } }
定位死锁
threadMXBean ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); long[] deadlockedThreads = threadMXBean.findDeadlockedThreads(); if (deadlockedThreads != null && deadlockedThreads.length > 0) { for (int i = 0; i < deadlockedThreads.length; i++) { ThreadInfo threadInfo = threadMXBean.getThreadInfo(deadlockedThreads[i]); System.out.println("发现死锁" + threadInfo.getThreadName()); } }
修复死锁
避免策略:
/** * 描述: 转账时候遇到死锁,一旦打开注释,便会发生死锁 */ public class TransferMoney implements Runnable { int flag = 1; static Account a = new Account(500); static Account b = new Account(500); static Object lock = new Object(); public static void main(String[] args) throws InterruptedException { TransferMoney r1 = new TransferMoney(); TransferMoney r2 = new TransferMoney(); r1.flag = 1; r2.flag = 0; Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("a的余额" + a.balance); System.out.println("b的余额" + b.balance); } @Override public void run() { if (flag == 1) { try { transferMoney(a, b, 200); } catch (InterruptedException e) { e.printStackTrace(); } } if (flag == 0) { try { transferMoney(b, a, 200); } catch (InterruptedException e) { e.printStackTrace(); } } } private void transferMoney(Account from, Account to, int amount) throws InterruptedException { class Helper{ public void transfer(){ if (from.balance - amount < 0){ System.out.println("余额不足"); } from.balance -= amount; to.balance += amount; System.out.println("成功转账"+amount+"元"); } } int fromHash = System.identityHashCode(from); int toHash = System.identityHashCode(to); if (fromHash < toHash){ synchronized (from){ synchronized (to){ new Helper().transfer(); } } } else if (fromHash > toHash){ synchronized (to){ synchronized (from){ new Helper().transfer(); } } }else { synchronized (lock){ synchronized (to){ synchronized (from){ new Helper().transfer(); } } } } } static class Account { public Account(int balance) { this.balance = balance; } int balance; } }
哲学家问题
public class Dining { public static class Philosopher implements Runnable{ private final Object leftChopstick; private final Object rightChopstick; public Philosopher(Object leftChopstick, Object rightChopstick) { this.leftChopstick = leftChopstick; this.rightChopstick = rightChopstick; } private void doActon(String acton) throws InterruptedException { System.out.println(Thread.currentThread().getName()+" "+acton); Thread.sleep((long) (Math.random()*10)); } @Override public void run() { try { while (true){ doActon("thinking "); synchronized (leftChopstick){ doActon("拿左筷子"); synchronized (rightChopstick){ doActon("拿右筷子"); doActon("吃饭"); doActon("放下右筷子"); } doActon("放下左筷子"); } } } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { Philosopher[] philosophers = new Philosopher[5]; Object[] chopsticks = new Object[philosophers.length]; for (int i = 0; i < 5; i++) { chopsticks[i] = new Object(); } for (int i = 0; i < 5; i++) { Object left = chopsticks[i]; Object right = chopsticks[(i+1)%5]; philosophers[i] = new Philosopher(left, right); new Thread(philosophers[i], "哲学家"+(i+1)).start(); } } }
解决:一个哲学家更换拿的顺序
if (i == philosophers.length-1) philosophers[i] = new Philosopher(right, left); else
检测与恢复
实际开发避免死锁
lock:
public class tryLock implements Runnable { int flag; static Lock lock1 = new ReentrantLock(); static Lock lock2 = new ReentrantLock(); @Override public void run() { for (int i = 0; i < 100; i++) { if (flag == 1){ try { if (lock1.tryLock(800, TimeUnit.MILLISECONDS)){ System.out.println("线程1获取1"); Thread.sleep(new Random().nextInt(1000)); if (lock2.tryLock(800, TimeUnit.MILLISECONDS)){ System.out.println("线程1获取2"); System.out.println("线程1获取两把"); lock2.unlock(); lock1.unlock(); break; }else { System.out.println("线程1获取2失败"); lock1.unlock(); Thread.sleep(new Random().nextInt(1000)); } }else { System.out.println("线程1获取1失败"); } } catch (InterruptedException e) { e.printStackTrace(); } } if (flag == 0){ try { if (lock2.tryLock(800, TimeUnit.MILLISECONDS)){ System.out.println("线程2获取2"); Thread.sleep(new Random().nextInt(1000)); if (lock1.tryLock(800, TimeUnit.MILLISECONDS)){ System.out.println("线程2获取1"); System.out.println("线程2获取两把"); lock2.unlock(); lock1.unlock(); break; }else { System.out.println("线程2获取1失败"); lock2.unlock(); Thread.sleep(new Random().nextInt(1000)); } }else { System.out.println("线程2获取2失败"); } } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { tryLock r1 = new tryLock(); tryLock r2 = new tryLock(); r1.flag = 1; r2.flag = 0; new Thread(r1).start(); new Thread(r2).start(); } }
活锁
/** * 描述: 演示活锁问题 */ public class LiveLock { static class Spoon { private Diner owner; public Spoon(Diner owner) { this.owner = owner; } public Diner getOwner() { return owner; } public void setOwner(Diner owner) { this.owner = owner; } public synchronized void use() { System.out.printf("%s吃完了!", owner.name); } } static class Diner { private String name; private boolean isHungry; public Diner(String name) { this.name = name; isHungry = true; } public void eatWith(Spoon spoon, Diner spouse) { while (isHungry) { if (spoon.owner != this) { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } continue; } Random random = new Random(); if (spouse.isHungry && random.nextInt(10) < 9) { System.out.println(name + ": 亲爱的" + spouse.name + "你先吃吧"); spoon.setOwner(spouse); continue; } spoon.use(); isHungry = false; System.out.println(name + ": 我吃完了"); spoon.setOwner(spouse); } } } public static void main(String[] args) { Diner husband = new Diner("牛郎"); Diner wife = new Diner("织女"); Spoon spoon = new Spoon(husband); new Thread(new Runnable() { @Override public void run() { husband.eatWith(spoon, wife); } }, "牛郎").start(); new Thread(new Runnable() { @Override public void run() { wife.eatWith(spoon, husband); } }, "织女").start(); } }
饥饿
面试
syn关键字:
用法:对象锁和类锁
对象锁包括方法锁和同步代码块锁:
同步代码块:this和自己建对象,手动指定锁的对象
package syn; /** * @param: * @return: * @time: 2021/7/26 */ public class ThreadTest implements Runnable{ static ThreadTest instance = new ThreadTest(); Object lock1 = new Object(); Object lock2 = new Object(); @Override public void run() { synchronized (lock1){ System.out.println("lock1 "+Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"lock1 end"); } synchronized (lock1){ System.out.println("lock2"+Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"lock2 end"); } } public static void main(String[] args) { Thread t1 = new Thread(instance); Thread t2 = new Thread(instance); t1.start(); t2.start(); while (t1.isAlive() || t2.isAlive()){ } System.out.println("end"); } }
方法锁:修饰普通方法,锁对象默认为this
类锁:是Class对象的锁,只能在同一时刻被一个对象拥有
加在static方法上:
package syn; /** * @param: * @return: * @time: 2021/7/26 */ public class ThreadTest2 implements Runnable{ static ThreadTest2 instance1 = new ThreadTest2(); static ThreadTest2 instance2 = new ThreadTest2(); @Override public void run() { try { method(); } catch (InterruptedException e) { e.printStackTrace(); } } public static synchronized void method() throws InterruptedException { System.out.println("方法"+Thread.currentThread().getName()); Thread.sleep(3000); System.out.println(Thread.currentThread().getName()+" end"); } public static void main(String[] args) { Thread t1 = new Thread(instance1); Thread t2 = new Thread(instance2); t1.start(); t2.start(); while (t1.isAlive() || t2.isAlive()){ } System.out.println("end"); } }
.class代码块:
package syn; /** * @param: * @return: * @time: 2021/7/26 */ public class ThreadTest3 implements Runnable{ static ThreadTest3 instance1 = new ThreadTest3(); static ThreadTest3 instance2 = new ThreadTest3(); @Override public void run() { try { method(); } catch (InterruptedException e) { e.printStackTrace(); } } public void method() throws InterruptedException { synchronized (ThreadTest3.class){ System.out.println("方法"+Thread.currentThread().getName()); Thread.sleep(3000); System.out.println(Thread.currentThread().getName()+" end"); } } public static void main(String[] args) { Thread t1 = new Thread(instance1); Thread t2 = new Thread(instance2); t1.start(); t2.start(); while (t1.isAlive() || t2.isAlive()){ } System.out.println("end"); } }
面试常考:
1.两个线程同时访问一个对象的同步方法:非并行
package syn; /** * @param: * @return: * @time: 2021/7/26 */ public class ThreadTest4 implements Runnable{ static ThreadTest4 instance = new ThreadTest4(); @Override public void run() { synchronized (this){ System.out.println("this"+Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"lock end"); } } public static void main(String[] args) { Thread t1 = new Thread(instance); Thread t2 = new Thread(instance); t1.start(); t2.start(); while (t1.isAlive() || t2.isAlive()){ } System.out.println("end"); } }
2.两个线程同时访问2个对象的同步方法:非并行
package syn; /** * @param: * @return: * @time: 2021/7/26 */ public class ThreadTest4 implements Runnable{ static ThreadTest4 instance1 = new ThreadTest4(); static ThreadTest4 instance2 = new ThreadTest4(); @Override public void run() { synchronized (this){ System.out.println("this"+Thread.currentThread().getName()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"lock1 end"); } } public static void main(String[] args) { Thread t1 = new Thread(instance1); Thread t2 = new Thread(instance2); t1.start(); t2.start(); while (t1.isAlive() || t2.isAlive()){ } System.out.println("end"); } }
3.两个线程同时访问syn静态方法:非并行
package syn; /** * @param: * @return: * @time: 2021/7/26 */ public class ThreadTest2 implements Runnable{ static ThreadTest2 instance1 = new ThreadTest2(); static ThreadTest2 instance2 = new ThreadTest2(); @Override public void run() { try { method(); } catch (InterruptedException e) { e.printStackTrace(); } } public static synchronized void method() throws InterruptedException { System.out.println("方法"+Thread.currentThread().getName()); Thread.sleep(3000); System.out.println(Thread.currentThread().getName()+" end"); } public static void main(String[] args) { Thread t1 = new Thread(instance1); Thread t2 = new Thread(instance2); t1.start(); t2.start(); while (t1.isAlive() || t2.isAlive()){ } System.out.println("end"); } }
4.同时访问同步与非同步方法:并行
package syn; /** * @param: * @return: * @time: 2021/7/26 */ public class ThreadTest5 implements Runnable{ static ThreadTest5 instance = new ThreadTest5(); @Override public void run() { if (Thread.currentThread().getName().equals("Thread-0")){ try { method(); } catch (InterruptedException e) { e.printStackTrace(); } }else { try { method1(); } catch (InterruptedException e) { e.printStackTrace(); } } } public synchronized void method() throws InterruptedException { { System.out.println("方法"+Thread.currentThread().getName()); Thread.sleep(3000); System.out.println(Thread.currentThread().getName()+" end"); } } public void method1() throws InterruptedException { { System.out.println("无锁方法"+Thread.currentThread().getName()); Thread.sleep(3000); System.out.println(Thread.currentThread().getName()+" end"); } } public static void main(String[] args) { Thread t1 = new Thread(instance); Thread t2 = new Thread(instance); t1.start(); t2.start(); while (t1.isAlive() || t2.isAlive()){ } System.out.println("end"); } }
5.访问同一个对象的不同的普通同步方法:非并行
package syn; /** * @param: * @return: * @time: 2021/7/26 */ public class ThreadTest6 implements Runnable{ static ThreadTest6 instance = new ThreadTest6(); @Override public void run() { if (Thread.currentThread().getName().equals("Thread-0")){ try { method(); } catch (InterruptedException e) { e.printStackTrace(); } }else { try { method1(); } catch (InterruptedException e) { e.printStackTrace(); } } } public synchronized void method() throws InterruptedException { { System.out.println("方法1"+Thread.currentThread().getName()); Thread.sleep(3000); System.out.println(Thread.currentThread().getName()+" end"); } } public synchronized void method1() throws InterruptedException { { System.out.println("方法2"+Thread.currentThread().getName()); Thread.sleep(3000); System.out.println(Thread.currentThread().getName()+" end"); } } public static void main(String[] args) { Thread t1 = new Thread(instance); Thread t2 = new Thread(instance); t1.start(); t2.start(); while (t1.isAlive() || t2.isAlive()){ } System.out.println("end"); } }
6.同时访问静态syn方法和非静态syn方法:并行
package syn; /** * @param: * @return: * @time: 2021/7/26 */ public class ThreadTest7 implements Runnable{ static ThreadTest7 instance = new ThreadTest7(); @Override public void run() { if (Thread.currentThread().getName().equals("Thread-0")){ try { method(); } catch (InterruptedException e) { e.printStackTrace(); } }else { try { method1(); } catch (InterruptedException e) { e.printStackTrace(); } } } public static synchronized void method() throws InterruptedException { { System.out.println("静态方法1"+Thread.currentThread().getName()); Thread.sleep(3000); System.out.println(Thread.currentThread().getName()+" end"); } } public synchronized void method1() throws InterruptedException { { System.out.println("方法2"+Thread.currentThread().getName()); Thread.sleep(3000); System.out.println(Thread.currentThread().getName()+" end"); } } public static void main(String[] args) { Thread t1 = new Thread(instance); Thread t2 = new Thread(instance); t1.start(); t2.start(); while (t1.isAlive() || t2.isAlive()){ } System.out.println("end"); } }
7.方法抛出异常,释放锁:并行
package syn; /** * @param: * @return: * @time: 2021/7/26 */ public class ThreadTest8 implements Runnable{ static ThreadTest8 instance = new ThreadTest8(); @Override public void run() { if (Thread.currentThread().getName().equals("Thread-0")){ try { method1(); } catch (Exception e) { e.printStackTrace(); } }else { try { method2(); } catch (InterruptedException e) { e.printStackTrace(); } } } public synchronized void method1() { { System.out.println("异常方法1"+Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (Exception e) { e.printStackTrace(); } throw new RuntimeException(); } } public synchronized void method2() throws InterruptedException { { System.out.println("方法2"+Thread.currentThread().getName()); Thread.sleep(3000); System.out.println(Thread.currentThread().getName()+" end"); } } public static void main(String[] args) { Thread t1 = new Thread(instance); Thread t2 = new Thread(instance); t1.start(); t2.start(); while (t1.isAlive() || t2.isAlive()){ } System.out.println("end"); } }
总结:
1.一把锁只能被一个线程获取,没有拿到锁的必等待,对应1和5
2.每个实例都对应自己的一把锁,不同实例之间不影响,锁对象是.class和static时候,所有对象共同同一把类锁,对应2,3,4,6.
3.抛出异常会释放锁。
被syn的方法里调用没有被syn的方法,不是线程安全的。
syn的性质:
可重入性,不可中断
原理:加锁和释放锁的原理:内置锁
等价:
public synchronized void method(){ System.out.println("syn"); } void method2(){ lock.lock(); try { System.out.println("lock"); }finally { lock.unlock(); } }
可重入原理:加锁次数计数器
jvm跟踪对象被加锁的次数,相同的线程在此对象上再次获得锁时候,锁会减少,到0了释放
可见性原理:java内存模型
缺陷:
效率低:锁的释放情况少,试图获得锁时不能设定超时,不能中断一个正在试图获得锁的线程。
不灵活:加锁和释放的时机单一,每个锁仅有单一的条件,可能是不够的。
无法知道是否成功得到锁
面试题:注意点:锁对象不能为空,作用域不宜过大,避免死锁
Lock和syn选择:线程工具、syn
多线程访问同步方法的各种情况
总结:jvm自动通过monitor加锁解锁,保证同时只有一个线程可以执行指定代码,从而保证了线程安全,同时具有可重入和不可中断