Java 多线程

1、引入

  • 操作系统他的发展史:

    1. 手工操作

    2. 批处理系统

    3. 多道批处理

    4. 分时系统

    5. 实时系统

  • 进程和线程:

    1. 进程:正在执行的程序,其实就是一块儿内存区域,内部存储着程序的资源

    2. 线程:程序被CPU调度的最小单位。

2、java多线程

  1. 继承Thread类,重写run方法。

  2. 实现Runnable接口,实现run方法。

必须:run(), start()

run(): 线程执行的时候要执行的代码 start():启动一个线程

  1. 继承Thread类 实现MyThread类

    public class MyThread extends Thread {
        @Override
        public void run() {
            // 要把子线程执行的内容写在run里面
            for(int i = 0; i < 1000; i ++) {
                System.out.println("我是子线程:" + i);
            }
        }
    }

    线程的使用与创建

    // 1. 创建线程对象
    MyThread mt = new MyThread();
    // 2.调用start方法启动一个线程
    mt.start();

  2. 实现Runable接口

    实现Runable中fun方法

    public class MyRunnable implements Runnable {
        public void run() {
            for(int i = 0; i < 1000; i ++) {
                System.out.println("我是子线程:" + i);
            }
        }
    }

    线程的使用与创建

    // 1. 先创建Runnable类
    Runnable r = new MyRunnable();
    // 2.创建线程对象必须指向我的Runnable
    Thread td = new Thread(r);
    // 3.调用start方法启动一个线程
    td.start();

3、线程相关操作

3.1、设置优先级

优先级高的线程,是会有一定程度的先执行的权限。但是具体是操作系统来实现的,不同的操作系统可以效果不同。 至于先执行哪个线程,还是看操作系统是怎么分的。优先级相当于只是让操作系统稍微参考一下。

public class test {
    public static void main(String[] args) {
        MyThread mt1 = new MyThread("A线程");
        MyThread mt2 = new MyThread("B线程");
        mt2.setPriority(10);
        mt1.setPriority(1);
        mt1.start();
        mt2.start();
    }
}

3.2、睡眠

睡眠函数,是让当前线程等待一段时间再去执行之后的语句。

sleep里面的单位是毫秒,下面代码中相当于是每一秒执行一次。

@Override
public void run() {
    while (true) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date now = new Date();
        System.out.println(sdf.format(now));
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

3.3、join(主线程等待子线程)

mt.join相当于是会程序卡在这个位置,等到mt线程执行完毕之后再去执行后面的语句。

public class MyThread extends Thread {
    @Override
    public void run() {
        for(int i = 0; i < 1000; i ++) {
            System.out.println("我是子线程:" + i);
        }
    }
​
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();
        for(int i = 0; i < 100; i++) {
            System.out.println("------我是主线程:" + i);
        }
        try {
            mt.join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("---------------------------");
    }
}

3.4、yield(让出CPU一下)

相当于是在可以把当前的cpu让出去,让别人先用一下,只是让一下,而不会一直让下去。 主要是实现一个交替执行的效果。 具体效果,主要还是看操作系统的分配。

public class MyThread extends Thread {
    public MyThread(String name) {
        super.setName(name);
    }
    @Override
    public void run() {
        for(int i= 0; i < 500; i++) {
            System.out.println(super.getName() + "--" + i);
            if(i % 10 == 0) {
                Thread.yield();
            }
        }
    }
    public static void main(String[] args) {
        MyThread mt1 = new MyThread("A线程");
        MyThread mt2 = new MyThread("B线程");
        mt1.start();
        mt2.start();
    }
}

3.5、interrupt(打断睡眠)

相当于是子线程因为一些原因进行睡眠操作,但是可以用interrupt来打断子线程的睡眠操作。继续执行之后的工作。

public class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("我要睡觉了");
        try {
            Thread.sleep(1000000000);
        } catch (InterruptedException e) {
            System.out.println("为什么不上睡了");
        }
        System.out.println("清醒了,开始工作了。");
    }
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();
        for(int i = 0; i < 1000; i ++) {
            System.out.println(i);
        }
        mt.interrupt(); // 打断正在睡眠的子线程
    }
}

4、线程同步

线程同步: 当多个线程共享同一个资源的时候,我们可以在某个一个线程访问到这个资源的时候,把这个资源暂时封锁,等待执行结束,释放这个锁,其他线程才可以进行执行,线程同步。

总结:等待其它线程释放锁,让线程变得更加安全。

例子:如果在不同线程中操作同一个资源,比如ATM机以及银行柜台同时取钱,同时查询都是有钱的,同时取钱的话会让银行赔钱。所以需要对这个公共资源进行处理,不能有多个人同时使用这个资源,就需要在使用的时候对资源进行封锁。当其他人要用的时候需要等之前的人用完才能用,这样可以避免产生问题。

封锁的方法如下:

4.1、synchronized关键字

synchronized关键字可以将整个方法上锁,只要有人使用就上锁,用完之后自动释放。

public class Account {
    private double blance;
    public Account(double blance) {
        this.blance = blance;
    }
    public synchronized void getMoney() {
        if(this.blance <= 0) {
            System.out.println("没钱了!!");
            return;
        }
        System.out.println("我要取钱了,目前还剩下:" + this.blance);
        this.blance -= 1000;
        System.out.println("取完了,还剩下:" + this.blance);
    }
}

4.2、synchronized语句

通过synchronized() {}语句将需要上所的位置上锁。 相比于synchronized关键字的话,语句比较灵活,所以用synchronized的话首推语句。

public class Account {
    private double blance;
    public Account(double blance) {
        this.blance = blance;
    }
    public void getMoney() {
        synchronized (this){
            if(this.blance <= 0) {
                System.out.println("没钱了!!");
                return;
            }
            System.out.println("我要取钱了,目前还剩下:" + this.blance);
            this.blance -= 1000;
        }
        System.out.println("取完了,还剩下:" + this.blance);
    }
}

4.3、手动上锁

建一个锁、lock,使用的时候上锁,用完后解锁。

public class Account {
    private double blance;
    private Lock lock = new ReentrantLock();
    public Account(double blance) {
        this.blance = blance;
    }
    public void getMoney() {
        lock.lock();
        if(this.blance <= 0) {
            System.out.println("没钱了!!");
            return;
        }
        System.out.println("我要取钱了,目前还剩下:" + this.blance);
        this.blance -= 1000;
        System.out.println("取完了,还剩下:" + this.blance);
        lock.unlock();
    }
}

5、死锁 (了解)

Java 多线程_第1张图片

当两个线程都需要两个资源的时候,A拥有1 申请2,B拥有2 申请1,这个时候就会产生死锁。

  • 注意事项:使用synchronized的时候一定要格外注意,有没有互相调用的方法被锁定,慎重使用synchronized。

死锁实例代码:

分别锁定一个资源之后申请第二个资源。资源被占用,并且也是等待资源状态。

public class ResourceObject {
    public static final Object obj1 = new Object();
    public static final Object obj2 = new Object();
}
public class DeadLock1 extends Thread{
    @Override
    public void run() {
        synchronized(ResourceObject.obj1) {
            System.out.println("锁定资源1");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            synchronized (ResourceObject.obj2) {
                System.out.println("锁定资源2");
                System.out.println("使用完毕");
            }
        }
    }
}
public class DeadLock2 extends Thread{
    @Override
    public void run() {
        synchronized(ResourceObject.obj2) {
            System.out.println("锁定资源2");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            synchronized (ResourceObject.obj1) {
                System.out.println("锁定资源1");
​
                System.out.println("使用完毕");
            }
        }
    }
}
public class Test {
    public static void main(String[] args) {
        DeadLock1 dl1 = new DeadLock1();
        DeadLock2 dl2 = new DeadLock2();
        dl1.start();
        dl2.start();
    }
}

6、线程的生命周期

Java 多线程_第2张图片

Thread t = new Thread(); // 创建一个线程 t.start(); //开启一个线程,线程处于就绪状态

7、生产者消费者模型

是一个非常常用的模型,增加资源的利用率以及效率。

Java 多线程_第3张图片

图中上半部分是一个在进行的时候另一个必须要等待。效率非常低,总会有一个是在等待。

异步,左边不用等右边,右边不用等左边。

生产者消费者模型:读取视频这一方被称为生产者,产品就是中间的视频,右边发送给公安局的就是消费者,消费的是视频。

Queue:队列,BlockingQueue阻塞队列,当队列中没有数据的时候,需要拿出数据,队列将会将程序阻塞,阻塞到有数据,队列继续工作。

AtomicInteger 线程安全的数字类型。

public class Video { // 封装一个类用来储存单一的数据,仅仅用来表示数据。
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Video(String name) {
        this.name = name;
    }
}
public class ReadVideoThread extends Thread { // 生产者,读取视频
    // private static int i = 0; // 虽然这个语句是所有线程共享的,因为是静态的,但是不安全,因为多个线程同时i++的时候可能会产生冲突
    private static AtomicInteger i = new AtomicInteger(); // 线程安全的数字类型
    private BlockingQueue

你可能感兴趣的:(java,jvm,开发语言)