概念
每个线程都有优先级,但优先级只代表优先的概率更大
对于一次性的方法: 外部类-》静态内部类-》局部内部类-》匿名内部类-》lambda
import java.lang.Thread;
interface iLike{
void lambda();
}
public class Test2 {
//静态内部类
// static class Like2 implements iLike{
// public void lambda() {
// System.out.println("Like2");
// }
// }
public static void main(String[] args) {
iLike like = null;
// iLike like = new Like(); //外部类
// like.lambda();
// like = new Like2(); //静态内部类
// like.lambda();
// class Like3 implements iLike{ //方法内部类
// public void lambda() {
// System.out.println("Like3");
// }
// }
// like = new Like3();
// like.lambda();
// like = new iLike() { //匿名内部类
// public void lambda() {
// System.out.println("Like4");
// }
// };
// like.lambda();
//Lambda,是对接口的实现
like = ()->{
System.err.println("labmda Like");
};
like.lambda();
}
}
//外部类
//class Like implements iLike{
//
// public void lambda() {
// System.out.println("Like1");
//
// }
//
//}
lambda是对接口方法的实现,且该接口仅允许有一个方法。
() -> {}():
new Thread(()->System.out.println(“多线程中的lambda”)).start().
进入就绪状态的情况:
进入阻塞状态的情况
线程死亡(一般不要使用自带的stop、destroy摧毁)
注意
State state = t.getState(); (t为写好的线程实例)
同步锁 synchronized:
锁的是可能被多个人修改的“资源”,而非方法
死锁:相互等待对方的资源,一般发生在锁套锁的情况。
Java提供了以下方法:注意只能在同步方法或同步块中使用
方法名 | 作用 |
---|---|
final void wait() | 表示线程一直等待,直到其他线程通知;与sleep不同,wait会释放锁 |
final void wait(long timeout) | 指定等待毫秒数 |
final void notify() | 唤醒一个处于等待状态的线程 |
final void notifyAll() | 幻想同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度 |
1.一个生产者消费者模型,共有4个类:生产者,消费者,容器,数据。3+N个实例:生产者,消费者,容器,N个数据。 2.生产者和消费者通过容器实现通信,即,双方共用一个容器(双方传入相同的容器) 3.容器实现生产和消费的方法(实际就是容器本身的增删方法) 4.生产者和消费者为多线程,调用容器的增删。容器中的增删方法为同步方法。
public class Consumer2Producter {
public static void main(String[] args) {
Container container = new Container();
Comsumer comsumer = new Comsumer(container);
Product product = new Product(container);
comsumer.start();
product.start();
}
}
//生产者
class Product extends Thread{
Container container;
public Product(Container container) {
this.container = container;
}
public void run() {
for (int i = 1; i <= 10; i++) {
Steamebun bun = new Steamebun(i);
//push是同步方法,存在等待的过程,为保证输出正确,这句话要在push之前
System.out.println("生产了馒头"+bun.id+"号");
container.push(bun);
}
}
}
//消费者
class Comsumer extends Thread{
Container container;
Comsumer(Container container){
this.container = container;
}
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println("消费了馒头:"+container.pop().id+"号");
}
}
}
//数据
class Steamebun{
int id;
Steamebun(int id){
this.id = id;
}
}
//缓冲区(容器)
class Container{
Steamebun[] steamebuns = new Steamebun[10];
int count;
//生产
public synchronized void push(Steamebun steamebun){
if (steamebuns.length==count) {
try {
this.wait();
} catch (InterruptedException e) {
}
}
steamebuns[count++] = steamebun;
this.notify();
}
//消费
public synchronized Steamebun pop() {
if (count==0) {
try {
this.wait();
} catch (InterruptedException e) {
}
}
Steamebun bun = steamebuns[--count]; //先减再用
this.notify();
return bun;
}
}
跟管程法差不多,区别就在于在容器中利用flag真假来控制线程切换。(没有具体容器上限的时候需要使用信号灯法,因为管程法是利用是否达到容器上限来控制线程切换的)
类实现:
任务调度框架:
当多行代码没有依赖的时候,为了减少“内存操作速度慢而CPU速度快导致CPU等待内存而影响效率“的情况,jvm和CPU会尝试重排指令顺序,加快运行速度。这就导致代码不一定按你写的顺序执行。
语句的执行的最后一步,从CPU中将 计算好的值传回内存,在这个过程中,由于CPU速度快,内存速度慢,可能发生计算好的值还没传回内存,新的指令就使用了该值的旧值。
简单理解:语句A和语句B按AB排列,若A执行完毕会非常慢,而jvm判断B又与A没有依赖,那么为了保证CPU不闲置,jvm会提前执行B。
最常见的例子:
public class HappenBefore {
static int a;
static boolean flag;
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
a=0;
flag=false;
Thread t1 = new Thread(()-> {
a=1;
flag=true;
});
Thread t2 = new Thread(()-> {
if (flag) {
a*=1;
}
if (a==0) {
System.out.println("HappenBefore a-->"+a);
}
});
t1.start();
t2.start();
}
}
}
上面的代码,理想情况下,只会输出a–>0,但实际还会输出a–>1;
为了避免多线程中的数据的HappenBefore,volatile保证了多线程之间变量的可见性。(轻量级的synchronize)
保证原子性,使用CAS原子操作 对于数据A,在时间轴上有三个值:内存中的A1,”看到的“A,要更新的目标值A2 CAS操作时,将A1与A进行比较,若不相同,则不进行操作,返回flase 若相同,则修改为A2,返回true。
ThreadLocal【本地线程】
在多线程的程序中,我们一般会申请共享变量,给所有线程使用。 但是很多情况下,每个线程会对共享变量进行改变。 比如,一个String 类型的static变量,线程一将String 赋值为 “线程一”,然后在跑其他的逻辑的时候,第二个线程将String 又改成了“线程二”,那么就会出个问题,线程一跑到后面在对该String进行使用的时候,就发现值已经被改变了。 此时就需要ThreadLocal,相当于,每个线程拥有了独立的数据空间。
优点: 1.通过当前线程的前进,传递对象(而不需要通过一路传参到处传递)(创建线程的局部变量) 2.每个线程使用各自的数据空间,保证线程之间的变量处理互不影响
ThreadLocal的源码非常简单,就是通过一个HashMap存放数据。
public class ThreadLocal{
private Map<Runnable,Object> container = new HashMap<Runnable,Object>();
public void set(Object value){
container.put(Thread.currentThread(),value);//用当前线程作为key
}
public Object get(){
return container.get(Thread.currentThread());
}
public void remove(){
container.remove(Thread.currentThread());
}
}