java多线程

多线程的基本认知

1.什么是多线程?

说多线程得从进程聊起,所谓的进程就是一个可以独立运行的程序单位,一个进程由多个线程构成.

比如打开电脑,打开QQ,这是开启一个进程

打开网页,这类似于打开一个线程,可以同时打开多个网页,类似开启多个线程

多线程的应用可以大幅度提高程序的效率,但是开多线程的同时会消耗更多的系统资源,更多的线程需要更多的内存,使用不当的同时也会引发更多的问题

多线程并不是几个线程同时运行的,实际上是系统不断地在各个线程之间来回的切换,因为切换的速度非常快,所以给人一种线程同时在运行的错觉

线程的三种实现方式
package Sync;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class ThreadNew {

    public static void main(String[] args) {
        new MyThread1().start();
        new Thread(new MyThread2()).start();

        FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());
        new Thread(futureTask).start();
        try {
            Integer integer = futureTask.get();
            //System.out.println(integer);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
//方式一:继承Thread
class MyThread1 extends Thread {
    @Override
    public void run() {
        System.out.println("mythread1");
    }
}
//方式二:实现Runnable接口
class MyThread2 implements Runnable {

    @Override
    public void run() {
        System.out.println("mythread2");
    }
}
//方式三:实现callable接口
class MyThread3 implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        System.out.println("mythread3");
        return 1;
    }
}

程序运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IGJggybL-1666019093833)([外链图片转存失败,源站可能有防盗在这里插入!链机制,建描述]议将图片上https://传(imblog.csdnimg.cNRUdO67c9593b6c94cbebfa52b8c2d2ad1.png3)(https://imgblog.csdnimg.cn/d67c9593b6c94cbebfa52b8c2dd20ad1.png)]]
)

线程的三种实现方式底层都是实现了Runnable接口,重写了Runnable接口中的Run()方法.

其中继承Thread类和实现Runnable接口都没有返回值,只有通过Callable接口实现的call()方法有返回值

第四种实现方式:线程池
package Thread1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

//线程池
public class TestPool {
    public static void main(String[] args) {
        //创建线程池
        //newFixedThreadPool参数:表示线程池的数量
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        executorService.execute(new MyThread());
        executorService.execute(new MyThread());
        executorService.execute(new MyThread());
        executorService.execute(new MyThread());

        //关闭链接
        executorService.shutdownNow();

    }
}

class MyThread implements Runnable{

    @Override
    public void run() {
            System.out.println(Thread.currentThread().getName());
    }
}

执行结果如图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z7wcdy0G-1666018328506)(C:\Users\14178\AppData\Roaming\Typora\typora-user-images\image-20221017180607739.png)]

2.线程的生命周期和五种基本状态

1.新建状态(new): 当线程对象被创建后,就进入了新建状态,如下

Thread thread = new Thread();

2.就绪状态(Runnable): 当调用线程的start()方法后,线程进入就绪状态,此时线程随时可以被cpu调动,并不是说调用start()方法线程会立即执行.

3.运行状态(Running):cpu开始调度处于就绪状态的线程时,线程才开始真正被执行,即运行状态.

4.阻塞状态(Blocked):处于运行状态的线程由于某些原因放弃对cpu的使用权,停止运行,进入阻塞状态.直到再次进入就绪状态,才能再次被cpu调用.

5.死亡状态(Dead):线程执行完或者因为异常退出执行run()方法.该线程生命周期结束.

3.多线程与高并发

高并发是系统运行过程中在短时间内遇到大量的请求操作,当请求的操作过多时,可能导致系统宕机,请求响应时间过长等问题.

多线程在高并发中使计算机的资源能最大程度上利用起来,不至于浪费系统资源

4.Synchronized关键字和Lock锁

Synchronized是Java中的关键字,当用Synchronized修饰方法或代码块时,每次只允许一个线程进入由它修饰的方法或代码块,从而防止多个线程同时操作同一个资源进入死锁状态,Synchronized就像一把锁,线程想进入Synchronized修饰的代码只能先获取到这把锁,其他线程想进去只能等待前一个线程使用完把锁归还后再进入,也就是先释放锁,其他线程才能使用.

同步代码块示例:

  @Override
    public  void run() {
    //同步代码块
        synchronized (account){
            if (account.money - drawingmoney < 0) {
                System.out.println("你的余额不足");
                return;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            account.money = account.money - drawingmoney;
            nowmoney = nowmoney + drawingmoney;
            System.out.println(account.name + "的余额为:" + account.money);
            System.out.println(this.getName() + "手里的钱:" + nowmoney);
        }
        }

同步方法,只需要加一个synchronized关键字

  private synchronized void buy() throws InterruptedException {
        if (TicketNum<=0){
            flag=false;
            return;
        }
        Thread.sleep(200);
        System.out.println(Thread.currentThread().getName()+"抢到了第"+TicketNum--+"张票");
    }

Lock是Java开发中的一个接口,Lock需要手动加锁和释放锁,通过luck()加锁,通过unlock()方法释放锁.ReetrantLock 实现了Lock接口,它是一个可重入锁,内部定义了公平锁和非公平锁,如果使用了luck锁,则必须释放锁,一般来说,使用Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在finally块中进行,以保证锁一定被被释放,防止死锁的发生。

Synchronzied 和 Lock 的主要区别
  • 存在层面:Syncronized 是Java 中的一个关键字,存在于 JVM 层面,Lock 是 Java 中的一个接口
  • 锁的释放条件:1. 获取锁的线程执行完同步代码后,自动释放;2. 线程发生异常时,JVM会让线程释放锁;Lock 必须在 finally 关键字中释放锁,不然容易造成线程死锁
  • 锁的获取: 在 Syncronized 中,假设线程 A 获得锁,B 线程等待。如果 A 发生阻塞,那么 B 会一直等待。在 Lock 中,会分情况而定,Lock 中有尝试获取锁的方法,如果尝试获取到锁,则不用一直等待
  • 锁的状态:Synchronized 无法判断锁的状态,Lock 则可以判断
  • 锁的类型:Synchronized 是可重入,不可中断,非公平锁;Lock 锁则是 可重入,可判断,可公平锁
  • 锁的性能:Synchronized 适用于少量同步的情况下,性能开销比较大。Lock 锁适用于大量同步阶段:
  • Lock 锁可以提高多个线程进行读的效率(使用 readWriteLock)
  • 在竞争不是很激烈的情况下,Synchronized的性能要优于RentrantLock ,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是RentrantLock 的性能能维持常态;
  • RentrantLock 提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。
死锁的成立条件
  • 互斥条件:任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请者等待直到资源被占有者释放。
  • 不可剥夺条件:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放。
  • 请求和保持条件:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。
  • 循环等待条件:就是前一个进程占有后一个进程所需要的资源,导致线程之间的资源无法获取到,形成闭环,等待资源的释放。

预防死锁只需要破坏四个必要条件的其中的任何一个即可

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值