public class MyCallable implements Callable<Integer> { @Override public Integer call() throws Exception { //线程要执行的方法 return total; } } public static void main(String[] args) throws ExecutionException, InterruptedException { /* * 多线程的第三种实现方式 * 1.定义一个类实现callable接口 * 2.重写里面的call方法 * * 3.创建mycallable对象 - 任务 * 4.创建futuretask对象 - 管理线程结果 * 5.创建thread对象 - 创建线程 * * */ MyCallable myCallable = new MyCallable(); FutureTask<Integer> integerFutureTask = new FutureTask<Integer>(myCallable); Thread thread = new Thread(integerFutureTask); }
3.4应用场景总结
如果要用到线程的处理结果(返回值),就用第三种实现方法
如果你想继承其它子类,用第二种或第三种
如果是单纯的继承Thread类,用第一种
因为java只支持单继承,不支持多继承
四、多线程应用场景
🖌用多线程模拟三个窗口卖100张票的过程
思路:1.定义一个线程类 2.重写里面的run方法模拟买票 3.创建3个对象模拟3个窗口
public class Mythread extends Thread { static int ticket = 0;//静态修饰 @Override public void run() { while (ticket++ < 100) { try { //休眠1秒 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "窗口正在卖第" + ticket + "张票"); } } } public class Test1 { public static void main(String[] args) { //实现三个窗口卖100张票的 Thread t1 = new Mythread("窗口1"); Thread t2 = new Mythread("窗口2"); Thread t3 = new Mythread("窗口3"); t2.start(); t1.start(); t3.start(); }
我们把代码跑起来
这和现实生活中的完全不一样,这其实引发了并发异常,简单说一下,开启了3个进程,每个进程被cpu选中是随机的,当线程1被选中并且代码运行到while循环里,cpu又去执行线程2,还有可能去执行线程三等等,所以导致同时卖出了第三张票
五、解决并发问题的方法
5.1 synchronized()关键字 - 同步代码块
原理分析:
给可能发生异常的代码加上锁之后,其他线程只能等待该线程执行完释放该锁。
代码分析:
synchronized()
括号要求是一个对象,什么类型都可,但针对该类必须唯一,也就是这把锁是管理该类的一把锁,可以是静态修饰得对象,可以是Mythread得字节码文件。
还是以买票举例
public class Mythread extends Thread { static int ticket = 0;//静态修饰 @Override public void run() { while (ticket++ < 100) { //synchronized()括号里写一个锁对象,必须针对该类唯一 synchronized (Mythread.class) { try { //休眠1秒 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "窗口正在卖第" + ticket + "张票"); } } } }
把可能引发并发异常的代码抽象成方法 用synchronized修饰
这是的锁对象会默认创建,如果是静态的方法,该对象是ths 如果是非静态,该对象是该类的class文件
@Override public void run() { while (ticket++ < 100000) { //同步方法 method(); } } private synchronized void method() { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "窗口正在卖第" + ticket + "张票"); }
5.2使用lock锁
获取静态锁对象
调用lock方法把并发代码锁起来
并发代码用try-catch包裹
释放锁放在finally代码块里
public class MyThread extends Thread{ static int ticket = 0; static Lock lock = new ReentrantLock(); @Override public void run() { while(ticket++ <= 100){ lock.lock(); try { if(ticket <= 100){ System.out.println(Thread.currentThread().getName() + "窗口卖出第" + ticket + "张票"); Thread.sleep(10); } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } } }
这样修改后的买票系统就能正常实现啦
注意:不要使用锁嵌套,可能出现死锁。看下面代码
public class DeadlockExample { private static final Object lock1 = new Object(); private static final Object lock2 = new Object(); public static void main(String[] args) { Thread thread1 = new Thread(() -> { synchronized (lock1) { System.out.println("Thread 1 acquired lock1"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock2) { System.out.println("Thread 1 acquired lock2"); } } }); Thread thread2 = new Thread(() -> { synchronized (lock2) { System.out.println("Thread 2 acquired lock2"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock1) { System.out.println("Thread 2 acquired lock1"); } } }); thread1.start(); thread2.start(); } }
📕总结
为什么会有并发异常,并发异常会导致什么后果
如何解决并发异常