多线程小结
1.多线程实现方法有三种
1.继承Thread类 不建议使用,避免oop单继承局限性 启动方法:子类对象.start();
2.实现Runnable接口 推荐使用,避免单继承局限性,灵活方便,方便同一对象被多个线程使用
3.实现Callable接口 是Runnable接口的继承接口,有返回值。
class MyThread1 extends Thread{
@Override
public void run() {
System.out.println(1);
}
}
class MyThread2 implements Runnable{
@Override
public void run() {
System.out.println(2);
}
}
class MyThread3 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println(3);
return 100;
}
}
三种线程的启动方法
new MyThread1().start();
new Thread(new MyThread2()).start();
FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());
new Thread(futureTask).start();
2.静态代理
例子 new Thread(对象).start;
1)上述是实现Runnable接口后的线程启动方法,将Runnable实现类的对象传到Thread,实现静态代理。
2)真实对象和代理对象都需要实现同一个接口,(即Runnable接口)
3)代理角色要代理真实角色
4)代理对象可以做很多真实对象做不到的事情,
5)个人理解是将重复的代码归到代理对象上,针对不同的对象,可以通过同一个代理实现同样的方法,同时只需要修改这个代理的方法就可以改变所有对象的执行效果。
3.Lambda表达式
1)使用前提是接口为函数式接口(比如Runnable接口,只包含唯一一个抽象方法的接口)
2)避免匿名内部类太多
3)本质是属于函数式编程的概念
4)jdk1.8以上才能使用
5)去除无意义的代码,保留核心的逻辑
6)若去掉参数类型,则都去掉,必须加上括号
(params)->expression[表达式] (params)->statement[语句] (params)->{statements}
4.线程的状态
进入就绪并不意味着立即执行调度,而是等待CPU的分配
1)线程休眠 sleep
2)线程礼让 yield
3)线程强制执行(插队)join
4)建议通过设置循环次数或者设置标志位来控制线程的结束,不建议使用stop/destory等过时或jdk不推荐的方法
5)线程的优先级:
1.getPriority().setPriority(int xx) //其中1为最低,10为最高
2.优先级的设置建议在线程启动之前
3.优先级低并不意味着一定比优先级高的后执行,只是CPU先执行的概率低
6)守护(daemon)线程和用户线程
1.简单的说就是,虚拟机必须得保证用户线程执行完毕,但不需要等待守护线程执行完毕
2.用户线程 main 守护线程 gc(垃圾回收线程)
5.线程同步
1.多个线程操作同一个资源
1)并发:同一个对象被多个线程操作, 就是一个收银台,一堆人结账
2)并行:两个收银台,两个收银台之间为并行。
3)为了解决并发的问题,天然的解决方法,排队
4)给收银台上锁,你要去结账,得先拿到收银台的锁你才能去结账,不然就排队,等上一个结完账后,锁被释放,下一个接上去。
5)线程同步即一种等待机制,d多个x徐通同时访问此对象的线程进入这个线程的等待池,形成队列,
6)sleep模拟网络延时,不会释放锁,wait会(wait用于线程之间相互通信,下文)
2.锁机制,synchroniezd
1)共享的资源,在哪个对象手里,就锁谁。就是说,谁增删改了,锁谁。
2)锁会降低执行效率,如果一个优先级高的线程d等待一个优先级低的线程执释放锁,会导致性能倒置的问题。
3)同步方法及其同步块:方法里面需要修改的内容才需要锁
synchronized(obj){} obj可以是任意对象,但推荐s使用共享资源作为同步监视器。
4)JUC中的一个关于list的安全的集合 CopyOnWriteArrayList
5) 死锁 多个线程互相抱有对方想拥有的资源,卡死。
3Lock锁
比较常用的是ReentrantLock,可以显式释加锁、释放锁。
private final ReentrantLock = new ReentranLock(); 即创建锁对象
try{
lock.lock();
执行代码块;}
finally{
lock.unlock();
}
注意lock是显式锁,需要手动开启和关闭锁,synchronized式隐式锁,出了作用域自动释放。
lock只有代码块,synchronized有代码块和方法锁。
使用lock锁,jvm花费更少的时间来调度线程,性能更好,并且有更好的扩展性。
优先使用顺序:lock>同步代码块>同步方法
6 线程协作
线程之间相同通信
1)管程法
分为三块,消费者;数据缓冲区;生产者。
生产者:负责生产数据的模块
消费者:负责数据处理的模块
缓冲区:消费者在缓冲区拿到数据;生产者将生产的数据放入缓冲区
synchronized可以阻止并发更新同一个共享资源但不能实现不同线程之间的消息传递
wait 和 notifyAll
2)信号灯法
通过一个标志位
7 线程池
如下图
public class Pool { public static void main(String[] args) throws InterruptedException { ExecutorService service = Executors.newFixedThreadPool(10);//创建大小为10的线程池 service.execute(new MyThread()); service.execute(new MyThread()); service.execute(new MyThread()); service.execute(new MyThread()); //关闭连接池 Thread.sleep(3000); service.shutdown(); } } class MyThread implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()); } }