一个程序运行有一个进程,进程里有多个线程:main函数,JC(垃圾回收线程)。
使用:
//1. 自定义类继承Thread类 //2. 重写run()方法 //3. .start()开始运行线程(调用run()则不是多线程,是顺序运行)
因为Java单继承的原因,接口方便同一个对象被多个线程使用
使用:
//1. 定义MyRun实现Runnable接口(重写run()方法) MyRun implements Runnable{} //2. 创建实现类对象 MyRun myRun = new MyRun(); //3. 创建代理类对象,并启动 new Thread(myRun()).start(); //or Thread thread = new Thread(myRun);//代理 thread.start();
好处:可以抛出异常,可以带返回值
使用:
//1. 自定义类实现callable接口 MyCallable implements callable<返回值类型>{} //2. 重写call方法(必须有返回值,和上面的范形一样) //3. 创建执行服务 ExcutorService ser = Excutors.newFixedThreadPool(3);//创建的线程个数 //4. 提交执行 Future<> f1 = ser.submit(myCallable);//有几个就提交几个 //5. 获取结果 返回值类型 rf1 = f1.get(); //6. 关闭服务 ser.shutdownNow();
对数据number处理问题
public class TichetProblem implements Runnable{ private int number = 10; @Override public void run() { while(true){ if(number<= 0){ break; } try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+ "拿到了第" + number-- +"张ticket");} } public static void main(String[] args) { //只有一个实例对象 TichetProblem tichetProblem = new TichetProblem(); //四个人抢票: new Thread(tichetProblem, "北京人").start(); new Thread(tichetProblem, "广东人").start(); new Thread(tichetProblem, "新疆人").start(); new Thread(tichetProblem, "台湾人").start(); } }
类似python装饰器,重点:真实对象和代理对象都要实现相同的接口, 类似于组合
一个实现了结婚接口的人,去找实现了结婚接口的婚前公司包装,结婚公司代理这个人结婚。
真实对象专注于一件事,
代理对象可以对真实对象进行包装。
public class StaticProxy { public static void main(String[] args) { new weddingCompany(new Person()).getMarry(); } } interface Marry { void getMarry(); } class Person implements Marry{ @Override public void getMarry() { System.out.println("today"); } } class weddingCompany implements Marry{ private Person person; public weddingCompany(Person person) { this.person=person; } @Override public void getMarry() { before(); this.person.getMarry(); after(); } private void before(){ System.out.println("before"); } private void after(){ System.out.println("after"); } }
函数式接口:只有一个方法的接口:如Runnable接口只有一个run()方法。
对函数式接口,可以使用lambda表达式创建实现该接口的对象。
(可以用接口类型去new对象,但此时对象只有接口中所定义的方法,和父类引用指向子类对象一样)
InterfaceType user1 = new User1();//其中user1没有User1中定义的其他函数
简化流程:
public class LambdaTest { public static void main(String[] args) { //1. 接口下方写class实现接口,再调用 普通调用 //2. main方法前面写静态类,然后再调用 静态内部类 //3. 把类写到main方法里 然后调用 局部内部类 //4. 用接口创建,匿名内部类 IL il = new IL() { @Override public void l(int a) { System.out.println(a); } }; il.l(123); //5. lambda 表达式 IL il2 = (int a) -> { System.out.println(a); }; il2.l(123); //简化一,去掉参数类型//常用的表达式 IL il_1= a->{ System.out.println(a); }; //简化二,去掉括号(如果有多行,则不能简化{},只能简化() IL il_2 = a-> System.out.println(a); } } interface IL{ void l(int a); } class il implements IL{ @Override public void l(int a) { System.out.println(a); } }
5个状态,创建状态,就绪状态,运行状态,阻塞状态,死亡状态
.start()从创建到就绪,
//1. 实现runnalble接口的自定义类中定义一个flag //2. run方法中写当flag为true时运行 //3. 自定义类中写一个方法使flag为False //4. main函数中调用3方法,使得线程停止
会抛出异常,休眠时间到达之后进入就绪状态
每个对象都有一把锁,sleep不会释放锁
1000ms = 1s
//获取当前时间 Date startData = new Date(System.currentTimeMillis()); System.out.println(new SimpleDateFormat("HH:mm:ss").format(startData));
礼让是指,CPU正在运行的线程从运行状态转变为就绪状态,不是阻塞状态!,
然后让两个线程继续竞争,所以礼让不一定成功,看CPU调度哪一个
Thread thread = new Thread(new Myrun()); thread.start(); //在主线程中可写: thread.join();//来强制执行此线程
写主函数里,要有实例化的线程或者代理名。
死亡之后的线程不能再次被启动
Thread.State state = new thread.getState();//新建一个state,new的是已经实例化的Thread state = thread.getState();//状态更新
java提供一个线程调度器监控启动后所有进入就绪状态的线程
优先级只是一个参考,并不是完全按照优先级进行调度的性能倒置,
优先级范围:1-10 Thread.MIN_PRIORITY =1, Thread.MAX_PRIORITY = 10, Thread.NORM_PRIORITY = 5,参考jdk文档
获取和改变:
getPriority(), setPriority(int priorityNumber)
线程分为用户线程和守护线程,(用户线程有:main,自己的线程;守护线程有gc垃圾回收,自己设置的守护线程)
虚拟机必须确保用户线程执行完毕,不需要确保守护线程执行完毕。
所有的线程默认都是用户线程,thread.setDaemon 默认值为 false,
当值设置为true时,设置为守护线程
虚拟机不需要等待守护线程执行完毕
//thread时实例化的线程 thread.setDaemon(true);
多个线程对同一个资源进行操作
同步解决: 队列+ 锁机制 synchronized
ticket问题出现负数的问题:所有人将1拷贝到自己的线程内存中,线程内存操作之后再修改station中的余额,出现-1问题
线程的不安全:对同一资源操作覆盖问题
通过修饰,当获取到对象的锁的时候,才能被执行。缺点:当一个大方法被修饰时,运行效率很低
同步方法和同步块锁的对象时变化的统一资源
//1. 同步方法 // 同步方法锁了之后,默认锁的是该方法所在的对象,如果修改的统一资源存在于该对象中,则用此方法 public synchronized void method(){} //2. 同步块 // 把同步的对象名放在obj位置,后面是修改统一资源的方法体 synchronized(obj){}
CopyOnWriteArrayList//JUC里的安全类型list
产生死锁的四个必要条件:
(口红,镜子问题)
ReentrantLock类实现了Lock锁
可重入锁
class A{ private final ReentrantLock lock = new ReentrantLock(): public void m(){ lock.lock(); try{ //主代码 }finally{ lock.unlock(); } } }
wait();//线程一直等待,知道有其他线程唤醒 notifyAll();//唤醒别的所有等待的线程
ExecutorService excutor = Executors.newFixedThreadPool(10); excutor.execute(); excutor.shutdown();