java进阶7-多线程

意义

在这里插入图片描述

提高cpu的使用率

进程与线程

进程是一个程序,例如 qq,线程是进程任务之一

进程引入线程作用

  1. 提高进程使用率
  2. 一个线程一个栈,共享堆区和方法区

java程序执行原理

java命令启动jvm,jvm启动一个程序,就是启动一个进程,进程启动主线程,主线程调用main方法,main方法中启动分支线程,主线程与分支线程,相互独立,主方法结束,分支线程不一定结束。注意,主方法结束,分支线程不一定结束,但主线程结束,分支线程一定结束。
分支的run方法与main方法平级。

线程是一个任务,一组操作的集合,怎样把要执行的操作放到线程中呢?把执行的操作放到run方法即可。

线程的创建

继承Thread

public class ThreadTest {
    public static void main(String[] args) {
     MyThread t = new MyThread();
     t.start();
}
class MyThread extends Thread {
    @Override
    public void run() {
        // 编写程序,这段程序运行在分支线程中。
        for(int i = 0; i < 1000; i++){
            System.out.println("分支线程--->" + i);
        }
    }
}
       

实现Runnable接口

implements Runnable

public class ThreadTest03 {
    public static void main(String[] args) {
        // 创建一个可运行的对象
        MyRunnable r = new MyRunnable();

        // 将可运行的对象封装成一个线程对象
        //Thread t = new Thread(r);
        Thread t = new Thread(new MyRunnable()); // 合并代码
        // 启动线程
        t.start();
        
// 这并不是一个线程类,是一个可运行的类。它还不是一个线程。
class MyRunnable implements Runnable {

    @Override
    public void run() {
        for(int i = 0; i < 100; i++){
            System.out.println("分支线程--->" + i);
        }
    }
}

注意 实现Runnable接口是作为线程目标使用的。

线程启动

one.start();
start之后,系统进入线程执行机制,自动调用run()方法。

获取当前线程,设置名字,获取名字

//currentThread就是当前线程对象
        Thread currentThread = Thread.currentThread();
    
        // 创建线程对象
        MyThread2 t = new MyThread2();
        // 设置线程的名字
        t.setName("t1");
        // 获取线程的名字
        String tName = t.getName();

线程生命周期

新建 new之后
就绪 start之后
运行
阻塞 运行了一段程序,有被安排到就绪状态,java语言可以控制的部分,控制线程的执行
死亡 运行完毕
注意:线程到了运行转态,就交个CPU安排执行那个线程,此时程序将不起作用。

在这里插入图片描述

**

线程的调度与控制

Thread.sleep() 休眠线程

作用:让当前线程进入休眠,进入“阻塞状态”,放弃占有CPU时间片,让给其它线程使用。
睡眠,进入阻塞转态,如果睡眠被中断,抛出interruptedException异常。

t. interrupt()唤醒线程

比如线程再sleep()之后在阻塞转态,interrupt()之后,中断阻塞,抛出异常interruptException,把线程从阻塞中解救出来,解救出来线程该怎样执行,看获取到异常后的代码怎样写的,就怎样执行
sleep睡眠太久了,如果希望半道上醒来,你应该怎么办?也就是说怎么叫醒一个正在睡眠的线程??
注意:这个不是终断线程的执行,是终止线程的睡眠。

public class ThreadTest08 {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable2());
        t.setName("t");
        t.start();

t.interrupt();

class MyRunnable2 implements Runnable {

    // 重点:run()当中的异常不能throws,只能try catch
    // 因为run()方法在父类中没有抛出任何异常,子类不能比父类抛出更多的异常。
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "---> begin");
        try {
            // 睡眠1年
            Thread.sleep(1000 * 60 * 60 * 24 * 365);
        } catch (InterruptedException e) {
            // 打印异常信息
//            e.printStackTrace();
            System.out.println("========================hhhhhh");
        }


        //1年之后才会执行这里
        System.out.println(Thread.currentThread().getName() + "---> end");
    }

Thread.stop() 停止线程 已启用

正确停止线程

设置私有成员变量flag(举例)boolean类型,在run()方法中,设置在flag为true情况下在代码后 break;然后声明setFlag()方法,供实例调用,调用后停止。

public class ThreadTest10 {
    public static void main(String[] args) {
        MyRunable4 r = new MyRunable4();
        Thread t = new Thread(r);
        t.setName("t");
        t.start();
       
        r.run = false;
    }
}

class MyRunable4 implements Runnable {
    // 打一个布尔标记
    boolean run = true;

    @Override
    public void run() {
        for (int i = 0; i < 10; i++){
            if(run){
                System.out.println(Thread.currentThread().getName() + "--->" + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
              
                return;
            }
        }
    }
}

调度模式

  1. 分时
  2. 抢占式
    java属于抢占式

线程优先级

void setPriority(int newPriority) 设置线程的优先级
int getPriority() 获取线程优先级
最低优先级1
默认优先级是5
最高优先级10
优先级比较高的获取CPU时间片可能会多一些。(但也不完全是,大概率是多的。)
**start之前,setPriority();

public class ThreadTest11 {
    public static void main(String[] args) {
        // 设置主线程的优先级为1
        Thread.currentThread().setPriority(1);

        /*System.out.println("最高优先级" + Thread.MAX_PRIORITY);
        System.out.println("最低优先级" + Thread.MIN_PRIORITY);
        System.out.println("默认优先级" + Thread.NORM_PRIORITY);*/

        // 获取当前线程对象,获取当前线程的优先级
        Thread currentThread = Thread.currentThread();
        // main线程的默认优先级是:5
        //System.out.println(currentThread.getName() + "线程的默认优先级是:" + currentThread.getPriority());

        Thread t = new Thread(new MyRunnable5());
        t.setPriority(10);
        t.setName("t");
        t.start();

        // 优先级较高的,只是抢到的CPU时间片相对多一些。
        // 大概率方向更偏向于优先级比较高的。
        for(int i = 0; i < 10000; i++){
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }

    }
}

class MyRunnable5 implements Runnable {

    @Override
    public void run() {
        // 获取线程优先级
        //System.out.println(Thread.currentThread().getName() + "线程的默认优先级:" + Thread.currentThread().getPriority());
        for(int i = 0; i < 10000; i++){
            System.out.println(Thread.currentThread().getName() + "-->" + i);
        }
    }
}

Thread.yield() 静态方法

让其他同优先级的优先执行,但不代表自己一定不是最先执行。yield()方法之后,回到,就绪转态,而非阻塞转态

t.join()

自己先执行,执行完之后,当前线程再执行。

线程同步

同步就是排队。多个线程共享一个资源,因为实际需要,需一个时间段只允许完整的允许完一个线程或线程的一段代码,可以用加锁的方式实现。
实现方法加锁,关键字synchronize----锁
异步就是并发。

public class Twoxianchengoneduixiang {
    public static void main(String[] args) {
        Zhong zhong1 = new Zhong();
        Thread th1 = new Thread(zhong1, "zhong1");

        Zhong zhong2 = new Zhong();
        Thread th2 = new Thread(zhong1, "zhong2");
        
        th1.start();
        th2.start();
    }
}

class Zhong implements Runnable {
    int j = 0;

    //    @Override
    public  void run() {

        synchronized (this) {
            System.out.println("**************"+this);
            System.out.println("&&&&&&&&&&&&&&&&&&"+this.getClass().toString());

                for (int i = 0; i < 5; i++) {
                    j += i;
            System.out.println(Thread.currentThread().getName() + ":" + j);
                }

                System.out.println(Thread.currentThread().getName() + ":" + j);
                j=0;
            }
        }
    }

锁定对象,注意:锁定的是对象,只能由一个线程对对象先执行完,再执行另一个线程对对象进行操作。

  1. 线程类run方法前修饰词 synchronize修饰方法紧锁定该方法,就是先由一个线程执行完该方法,再由别的线程调用,如果别的线程调用别的方法,与该被锁定的方法无关,别的方法正常执行。用synchonize修饰方法,效率低,一般不用。
    public synchronize void run(){ }
  2. 代码块
    synchonize(this){ }

一个线程一个对象

解决线程安全问题,就是不共享资源

public class TwoxianchengTwoduixiang {
    public static void main(String[] args) {
        Zhi zhi1 = new Zhi();
        Thread th1 = new Thread(zhi1,"zhi1");

        Zhi zhi2 = new Zhi();
        Thread th2 = new Thread(zhi2,"zhi2");
        th1.start();
        th2.start();
    }
}

class Zhi implements Runnable {
    int j = 0;

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            j += i;
            System.out.println(Thread.currentThread().getName() + ":" + j);
        }
        System.out.println(Thread.currentThread().getName() + ":" + j);
    }
}

守护线程

以上讲的都是用户线程。
用户线程结束,守护线程才会结束。比如垃圾回收线程就是守护线程

设置守护线程

t.setDaemon(true)

public class ThreadTest14 {
    public static void main(String[] args) {
        Thread t = new BakDataThread();
        t.setName("备份数据的线程");

        // 启动线程之前,将线程设置为守护线程
        t.setDaemon(true);

        t.start();

        // 主线程:主线程是用户线程
        for(int i = 0; i < 10; i++){
            System.out.println(Thread.currentThread().getName() + "--->" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class BakDataThread extends Thread {
    public void run(){
        int i = 0;
        // 即使是死循环,但由于该线程是守护者,当用户线程结束,守护线程自动终止。
        while(true){
            System.out.println(Thread.currentThread().getName() + "--->" + (++i));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Timer 定时器

定时器
Timer timer = new Timer();
执行定时器的方法
方法 schedule() timer.schedule(new LogTimerTask() , firstTime, 1000 * 10);
执行的任务
class LogTimerTask extends TimerTask

public class TimerTest {
    public static void main(String[] args) throws Exception {

        // 创建定时器对象
        Timer timer = new Timer();
       
        // 指定定时任务
        //timer.schedule(定时任务, 第一次执行时间, 间隔多久执行一次);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date firstTime = sdf.parse("2020-03-14 09:34:30");
        timer.schedule(new LogTimerTask() , firstTime, 1000 * 10);
    }
}

// 编写一个定时任务类

class LogTimerTask extends TimerTask {

    @Override
    public void run() {
      
        System.out.println(strTime + ":成功完成了一次数据备份!");
    }
}

object与线程

wait()让正在对象上活动的线程进入等待状态,无期限等待 等待notify或者等待的时间完成后进入就绪状态
notify()唤醒wait()的对象,进入就绪转态。

调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程。
调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程。

wait()与sleep()使用区别

项目waitsleep
同步只能在同步上下文中调用wait方法,否则或抛出IllegalMonitorStateException异常不需要在同步方法或同步块中调用
作用对象wait方法定义在Object类中,作用于对象本身 sleep方法定义在java.lang.Thread中作用于当前线程
释放锁资源
唤醒条件其他线程调用对象的notify()或者notifyAll()方法超时或者调用interrupt()方法体
方法属性wait是实例方法sleep是静态方法

A线程占用了对象的锁,其他线程进入synchronized块要获取这个这个对象的锁时,由于对象锁被占用,所以让其他线程进入阻塞状态,进入同步队列等锁的释放。
当占有锁的A线程调用某对象的wait方法,线程会进入等待队列,同时释放锁。等到其他其他线程调用这个对象的notify/notifyAll方法时,A线程才重新进入synchronized同步队列去等待锁,此时状态也是阻塞状态

sleep,wait使用场景的区别:

Sleep一般用于使当前线程进入阻塞状态并且继续持有锁,一般是让线程等待。

wait一般用于多线程之间的通信,需要配合notify或者notifyall来进行使用,例如:生产者,消费者模式就需要使用wait,notify方法

public class Wait_notifyAll {
    public static void main(String[] args) {
        List list = new ArrayList();

        Thread t1 = new Thread(new Shengchanzhe(list), "shenchan");
        Thread t2 = new Thread(new Xiaofeizhe(list), "xiaofei");
        t1.start();
        t2.start();
    }
}

class Shengchanzhe implements Runnable {
    private List list;

    public Shengchanzhe(List list) {
        this.list = list;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (list) {

                if (list.size() > 0) {
                    try {
                        list.wait();
                        System.out.println("仓库已有,可以消费");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                Object o = new Object();
                list.add(o);

                System.out.println("已生产,交给库房");
                list.notifyAll();
            }

        }
    }
}

class Xiaofeizhe implements Runnable {
    private List list;

    public Xiaofeizhe(List list) {
        this.list = list;

    }

    @Override
    public void run() {
        while (true) {
            synchronized (list) {
                if (list.size() == 0) {
                    try {
                        list.wait();
                        System.out.println("仓库无,要生产了");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                list.remove(0);

                System.out.println("消费完,请生产");
                list.notifyAll();
            }
        }
    }

}

Thread.currentThread

获取当前类的实例对象。

实现线程的第三种方式:

实现Callable接口
public class ThreadTest15 {
    public static void main(String[] args) throws Exception {

        // 第一步:创建一个“未来任务类”对象。
        // 参数非常重要,需要给一个Callable接口实现类对象。
        FutureTask task = new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception { // call()方法就相当于run方法。只不过这个有返回值
                // 线程执行一个任务,执行之后可能会有一个执行结果
                // 模拟执行
                System.out.println("call method begin");
                Thread.sleep(1000 * 10);
                System.out.println("call method end!");
                int a = 100;
                int b = 200;
                return a + b; //自动装箱(300结果变成Integer)
            }
        });

        // 创建线程对象
        Thread t = new Thread(task);

        // 启动线程
        t.start();

        // 这里是main方法,这是在主线程中。
        // 在主线程中,怎么获取t线程的返回结果?
        // get()方法的执行会导致“当前线程阻塞”
        Object obj = task.get();
        System.out.println("线程执行结果:" + obj);

        // main方法这里的程序要想执行必须等待get()方法的结束
        // 而get()方法可能需要很久。因为get()方法是为了拿另一个线程的执行结果
        // 另一个线程执行是需要时间的。
        System.out.println("hello world!");
    }
}

总结:

在jvm中多个线程,并行运行,同时执行多个线程。也可以多个线程操作一个对象,如果需要对对象操作,有先后顺序,就需要同步,即排队,给需要锁定的对象,加锁,这样就可以排队操作此对象,注意:此对象可以是需要操作的变量,方法,或当前线程。
多线程运行,从启动,到就绪,到运行。到消亡,这是一整套的流程,其中程序员可以控制线程运行的状态,可以让其sleep,wait,退回到阻塞状态,再退回到就绪装填。也可以中断线程,也可以从sleep中唤醒线程。
线程可以设置运行的优先级,也可以设置让自己优先运行,join,也可以让其他线程先运行yield

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值