打怪升级捡装备-java基础-多线程

多线程

概念

进程:一个进程对应一个应用程序(每个进程都有独立的内存和资源)

线程:程序中的实际运作单位(一个程序中的线程共享其内存和资源,堆内存和方法内存共享,栈内存独立,一个线程一个栈)

创建方式

1、(推荐)实现Runnable接口,重写run()方法(保留类的继承)

//MyClass实现Runnable接口,这里用到了静态代理
new Thread(new MyClass).start();

2、继承Thread类,重写run()方法

//也可以直接创建MyClass对象
Thread t = new MyClass();  
t.start();

3、实现Callable接口
好处:可以返回结果,可以抛出异常

//实现Callable接口 重写call()方法 call()方法的返回值类型和实现类的泛型相同

//建立实现类的实例
MyClass mc1 = new MyClass();
MyClass mc2 = new MyClass();
MyClass mc3 = new MyClass();
//创建执行服务:
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1 = ser.submit(mc1);
Future<Boolean> r2 = ser.submit(mc2);
Future<Boolean> r3 = ser.submit(mc3);
//获取结果
boolean rs1 = r1.get();
boolean rs2 = r2.get();
boolean rs3 = r3.get();
//关闭服务
ser.shutdownNow();

线程的生命周期

获取CPU资源
run方法结束
施放CPU资源
run遇到阻塞
阻塞解除
新建
就绪
运行
消亡
阻塞

新建
new 表示新建状态

就绪
调用start()方法进入就绪状态
就绪状态的线程表示有权利去获取CPU的时间片。CPU时间片是执行权。当线程拿到CPU时间片之后就马上执行run()方法,这个时间就进入了运行状态。

运行
run方法运行
在运行状态时,如果此时CPU的时间到了,但是run()方法还没有执行完成,那么就回到就绪状态,等待线程获取CPU时间片,获取到CPU时间片后继续运行run()方法。以上过程是JVM在调度。

消亡
run()方法结束线程消亡

阻塞
如果run()运行时遇到阻塞事件就进入阻塞状态,阻塞解除就进入就绪状态

线程的调度

调度方式

抢占式调度:(Java使用)优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取到的CPU时间片会相对多一些

分时调度: 所有线程轮流得到CPU的使用权,平均分配给每个线程CPU的时间片

线程优先级

1-10
其中
NORM_PRIORITY(标准)默认5
MAX_PRIORITY(最高级)10
MIN_PRIORITY(最低级)1

常用方法

sleep()静态方法

休眠,入参是毫秒时间。将该方法写在哪,哪的线程将休眠,即将该方法写在重写的run方法中,则run所在线程将休眠,如果写在main中,那么main将休眠

打断休眠(“t”是该线程),在其他位置使用 t.interrupt()方法,打断该线程,该线程的sleep()方法会抛出一个打断异常终止休眠,执行后续代码

终止一个线程,1,使用一个属性判断,然后修改属性终止。2,不推荐使用stop()等方法过时的方法

yield()静态方法

没有入参,让位给同优先级的其他线程,让位的时间不固定,大意和sleep方法相同

join()成员方法

合并线程,如果 t.join(); 在main方法中,那么t 线程将于main线程合并,将以上代码都执行完毕后才执行join之后的代码,就变成了单线程

wait()成员方法

表示线程一直等待,直到其他线程通知,与sleep()不同,wait()会施放锁

wait(long timeout)成员方法

指定等待的毫秒数

notify()成员方法

唤醒一个处于等待状态的线程

notifyAll()成员方法

唤醒同一对象上所有调用wait()方法的线程,优先级高的线程优先调度

setPriority(int newPriority)成员方法

更改优先级

currentThread() 静态方法

返回当前线程对象

getName() 成员方法

返回此线程的名称

isAlive()成员方式

测试这个线程是否活着
is系列
是否是守护线程
是否被中断

setDaemon()成员方法

设置当前线程为守护线程
虚拟机必须确保用户线程执行完毕
虚拟机不用等待守护线程执行完毕

synchronized

作用

当线程执行到该关键字修饰的代码时候,会去获取线程的锁,然后执行代码,执行结束后归还锁。这期间其他线程无法获取到该锁,所以会等待前面线程执行,即执行方式从多线程异步暂时变为了同步执行

使用

推荐:synchronized(this) {“do something”} 将需要同步的代码框柱

synchronized修饰需要同步的方法

类锁,修饰在静态方法、静态变量上,可以对类加锁

注意

类MyClass中有两个方法m1()和m2()
类Processor继承Thread并且构造器中需要传入一个MyClass对象
m1()和m2()都有synchronized修饰

情况1:

建立两个线程

MyClass mc = new MyClass();
Processor p1 = new Processor(mc);
Processor p2 = new Processor(mc);

此时 p1执行m1()方法(死循环)
p2执行m2()方法
问: p2是否需要等待p1执行结束
答: 需要,p1和p2都是通过一个mc对象构造的,所以m1()和m2()方法是同一个对象 synchronized加了锁

情况2:

建立两个线程

Processor p1 = new Processor(new MyClass());
Processor p2 = new Processor(new MyClass());

此时 p1执行m1()方法(死循环)
p2执行m2()方法
问: p2是否需要等待p1执行结束
答: 不需要,p1和p2是通过两个不同的对象构造的

情况3:

m1()有static synchronized修饰
m2()只有有static修饰
建立两个线程

MyClass mc = new MyClass();
Processor p1 = new Processor(mc);
Processor p2 = new Processor(mc);

此时 p1执行m1()方法(死循环)
p2执行m2()方法
问: p2是否需要等待p1执行结束
答: 不需要,m2没有被synchronized修饰

上述的特殊情况一定要考虑好synchronized修饰的是对象锁还是类锁
对象锁: 不同线程共享同一个对象需要等待,不同享同一个对象不需要等待
类锁: 不同线程不同享同一个对象也需要等待

lock(子类->可重入锁ReentrantLock)

使用示例

private final ReentrantLock lock = new ReentrantLock();

public void m(){    
    lock.lock();    
    try{        
         //要保证线程安全的代码    
    }finally {        
        lock.unlock();       
    //如果同步代码有异常需要将unlock()写入finally    
    }
}
lock 与synchronized对比
  • Lock是显示锁(手动开关,别忘记关)synchronized是隐式锁,出了作用域自动施放
  • Lock只有代码块锁,synchronized有代码块锁和方法锁
  • 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
  • 优先使用顺序:Lock>同步代码块(已经进入方法体,分配了相应资源)>同步方法(在方法体之外)

死锁

线程t1锁住o1对象,然后还要去锁o2对象
线程t2锁住o2对象,然后还要去锁o1对象
就形成了死锁,要避免死锁的发生
示例代码:

    public static void main(String[] args) throws InterruptedException {
        Object o1 = new Object();
        Object o2 = new Object();
        new Thread(() -> {
            synchronized (o1) {
                System.out.println(Thread.currentThread().getName() + "已锁o1");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o2) {
                    System.out.println(Thread.currentThread().getName() + "已锁o2");
                }
            }
        }).start();
        Thread.sleep(1000);
        new Thread(() -> {
            synchronized (o2) {
                System.out.println(Thread.currentThread().getName() + "已锁o2");
                synchronized (o1) {
                    System.out.println(Thread.currentThread().getName() + "已锁o1");
                }
            }
        }).start();
    }

守护线程

概念 线程分为用户线程和守护线程,只有当所有用户线程结束生命周期,守护线程才会结束。守护线程一般都无限循环
使用 setDaemon(Boolean b)方法

线程池

Executor: 工具类、线程池的工厂类,用于创建并返回不同类型的线程池
ExecutorService: 真正的线程池接口。常见子类ThreadPoolExecutor
常用方法:
coid execute(Runnable command) 执行任务/命令,没有返回值,一般用来执行Runnable
Futuresubmi(Callable task) 执行任务,有返回值,一般用来执行Callable
void shutdown() 关闭线程池

实现Runnable接口

//1、创建服务,创建线程池
ExecutorService service = Executors.newFixedThreadPool(10);

//执行
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());

//2、关闭连接
service.shutdown();

实现Callable接口

//建立实现类的实例
MyClass mc1 = new MyClass();
MyClass mc2 = new MyClass();
MyClass mc3 = new MyClass();
//创建执行服务:
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行
Future<Boolean> r1 = ser.submit(mc1);
Future<Boolean> r2 = ser.submit(mc2);
Future<Boolean> r3 = ser.submit(mc3);
//获取结果
boolean rs1 = r1.get();
boolean rs2 = r2.get();
boolean rs3 = r3.get();
//关闭服务
ser.shutdownNow();
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值