多线程
创建线程
继承Thread创建线程
//main中
//创建Cat对象,可以当做线程使用
Cat cat = new Cat();
cat.start();//启动线程
//说明 : 当main线程启动一个子线程, 主线程不会阻塞,会继续执行
//这时 主线程和子线程是交替执行的
//1.当一个类继承了Thread类, 该类就可以当成线程使用
//2.我们会重写 run 方法, 写上自己的业务代码
//3. run Theread类 实现了 Runnable 接口的run方法
class Cat extends Thread{
@Override
public void run(){//重写run方法,写上自己的业务逻辑
while(true){
//该线程每隔1秒,在控制台输出喵喵
System.out.println("喵喵");
//休眠一秒
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
实现Runnable接口创建线程
- java是单继承的, 在某些情况下一个类可能已经继承了某个父类, 这时在用继承Thread类方法来创建线程显然不可能了
- 所以通过实现Runnable接口创建线程
//main中
Dog dog = new Dog();
//不能直接调用start
//创建了Thread对象, 把dog对象(实现Runnable),放入Thread
//使用到了代理模式
Thread thread = new Thread(Dog);
thread.start();
class Dog implements Runnable{
int count = 0;
@Override
public void run(){
while(true){
System.out.println("汪汪"+(++count));
//休眠一秒
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
代理模式
//模拟简单代理模式
class ThreadProxy implements Runnable{//你可以把ThreadProxy类当做 真正的Thread类
private Runnable target = null;//属性, 类型是 Runnable
//构造器
public ThreadProxy(Runnable target){//传进来一个实现了Runnable接口的对象
this.target = target;
}
public void start(){
start0();//这个方法是真正实现多线程的方法
}
public void start0(){
run();
}
@Override
public void run(){
if(target != null){
target.run();//动态绑定
}
}
}
多线程机制
- 在多线程之中, 不是主线程结束了就意味着进程结束, 而是要等所有的线程结束.
- 从Java的设计来看, 通过继承Thread或者实现Runnable接口来创建线程本质没有区别
- 实现Runnable接口方式更加适合多线程共享一个资源的情况, 并且避免了单继承的限制
为什么是start?
如果直接调用run()
方法的话, 就是一个普通的方法, 没有真正的启动一个线程, 就会把run()
方法执行完毕, 才向下执行
start()方法执行过程
注意:
线程方法
通知线程退出
设置一个Boolean变量, 在线程外改变该变量. 使线程退出循环.
线程插队
yield
//线程的礼让. 让出cpu, 让其他线程执行, 但礼让的时间不确定, 所以也不一定礼让成功join
//线程的插队. 插队的线程一旦插队成功, 则肯定会先执行完插入的线程所以的任务
用户线程和守护线程
用户线程 : 也叫工作线程, 当线程的任务执行完成或通知方式结束
守护线程 : 一般是为工作线程服务的, 当所有的用户线程结束, 守护线程自动结束, 常见的守护线程是 : GC(垃圾回收机制)
//当我们希望当main线程结束后, 子线程自动结束, 将子线程设置为守护线程就可以 *.setDaemon(true);
线程状态
Synchronized
- 在多线程编程, 一些敏感数据不允许被多个线程同时访问, 此时就使用同步访问技术, 保证数据在任何同一时刻, 最多有一个线程访问, 以保证数据的完整性
- 线程同步, 即当有一个线程在对内存进行操作时, 其他线程都不可以对这个内存地址进行操作, 直到该线程完成操作, 其他线程才能对该内存地址进行操作
//1.同步代码块
synchronized (对象){ //得到对象的锁, 才能操作同步代码
//需要被同步代码
}
//2.同步方法
public synchronized void m (String name){ //同步方法, 在同一时刻, 只能有一个线程来执行m方法
//需要被同步的代码
}
eg :
Synchronized细节
- 同步的局限性 : 导致程序的执行效率降低
- 非静态的方法中同步代码块 的锁可以是this, 也可以是其他对象(要求多个线程的锁是同一个对象)
- 静态的方法中同步代码块 的锁为当前类本身