多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理” 。
实现Runnable接口
实现run()方法
创建线程对象,调用start()方法启动线程
推荐使用Runnable接口,方便一个对象被多个线程使用
public interface Runnable { public abstract void run(); }
public class TestLambda2 { public static void main(String[] args) { ILove love = (int a)-> { System.out.println("I Love You !!!" + a); }; love.love(521); } } // 函数式接口 interface ILove { void love(int a); }
真实对象
class You implements Marry{ @Override public void HappyMarry() { System.out.println("刘艺今天结婚,超级开心!!!"); } }
代理对象
class WeddingCommpany implements Marry { private Marry target; public WeddingCommpany(Marry target) { this.target = target; } @Override public void HappyMarry() { before(); this.target.HappyMarry(); after(); } private void after() { System.out.println("结婚之后,结尾收款。"); } private void before() { System.out.println("结婚之前,布置婚礼现场。"); } }
运行时,只需要创建一个代理对象,和一个真实对象,将真实对象传给代理对象,调用代理对象的方法即可。
代理对象可以做很多真实对象做不了的事情
真实对象专注做自己的事情 (对原有的方法增强!!)
Runnable接口和Thread代理都有run方法,最后调用的是Thread中的start方法,但实际执行的还是Runnable中的run方法中的方法体。
new 新生状态,线程对象一旦创建就进入到了新生状态
就绪状态,调用start()方法,线程立即进入就绪状态,但不意味着立即调度执行
阻塞状态,当调用sleep,wait或同步锁定时线程进入阻塞状态,就是代码不往下执行,阻塞事件解除后,重新进入就绪状态,等待CPU调度执行
运行状态,进入运行状态,线程才真正执行线程体的代码块
死亡状态dead,线程中断或者结束,一旦进入死亡状态就不能再次启动
不推荐直接使用JDK提供的stop()、destroy()方法
推荐线程自己停下来
建议使用一个标志位进行终止变量当flag = false则总之线程运行
设置一个循环当达到要求时,改变他的值,停止线程
sleep(时间)指定当前线程阻塞的毫秒数
sleep存在异常InterruptedException
sleep结束后线程进入就绪状态
sleep可以模拟网络延迟,倒计时等
每一个对象都有一个锁,sleep不会释放锁
Thread.sleep(毫秒数);
礼让线程,让当前正在执行的线程暂停,但不阻塞
将线程从运行状态转换为就绪状态
让CUP重新调度,礼让不一定成功!让了之后还会和其他线程继续相互竞争。
Thread.yield();
Join合并线程,待该线程执行完成后,再执行其他线程,它执行时其他线程阻塞
可以想象成插队,调用该线程对象的join()方法
在join之前要是没有其他限制,那么线程都处于并行状态,会出现互相插队的情况
Thread thread = new Thread(new Runnable()); ······ thread.join();
NEW :尚未启动的线程处于该状态
RUNNABLE :在JAVA虚拟机中执行的线程处于此状态
BLOCKED :被阻塞等待监视器锁定的线程处于该状态
WAITING :正在等待另一个线程执行特定的动作的线程处于该状态
TIMED_WAITING :正在等待另一个线程执行动作到达指定等待时间的线程处于该状态
TERMINATED :已退出的线程处于该状态
Thread thread = new Thread(); Thread.State state = thread.getState(); System.out.println(state);
获取优先级 getPriority()
改变优先级 setPriority(int XXX)
线程的优先级用数字表示,范围从1~10,优先级大了只是概率大,不是一定会先运行
Thread thread = new Thread(); thread.setPriority(1~10);
Thread thread = new Thread(); thread.setDaemon(true);
默认是false表示是用户线程,正常的线程都是用户线程,改成true即为守护线程
守护线程就是不被虚拟机守护的线程,虚拟机只会关心等待用户线程运行完毕,只要用户线程执行完毕那么虚拟机就会关闭,不会等待守护线程
多个线程操作同一个资源(并发),线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。
public synchronized void method(int ages){}
synchronized 方法控制对象访问,每个synchronized方法必须获得调用该方法对象的锁才能执行,否则线程阻塞,方法一旦执行,就独占锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁继续执行 缺陷:将一个方法申明为synchronized会降低效率
synchronized默认锁的是this对象
sychronized(obj){}
Obj 称为同步监视器
同步监视器的执行过程
某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生死锁问题
死锁产生的四个必要条件:
互斥条件:一个资源每次只能被放进一个进程使用
请求与保持条件:一个进程因请求资源而堵塞时,对已获得的资源保持不放
不剥夺条件:进程已获得的资源,在未使用之前,不能强行剥夺
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
只要想办法破解其中的任意一个或者多个条件就可以避免死锁发生
java.util.confurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
ReentrantLock**(可重入锁)**类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁
应用场景:生产者和消费者模式问题
Java中提供了几个方法解决线程之间的通信问题:
背景:经常创建和销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似于生活中的公共交通工具
好处:
线程池相关API:ExecutorService和Executors
ExecutorServer:真正的线程池接口。常见子类:ThreadPoolExecutor
Executors:工具类、线程池的工厂类