多线程 -Thread -Runnable -Callable

多线程(Thread)

  1. 线程简介
  2. 线程实现
  3. 线程状态
  4. 线程同步
  5. 线程通信问题
  6. 高级主题

核心概念

  1. 线程就是独立的执行路径;
  2. 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc程;
  3. main()称之为主线程,为系统的入口,用于执行整个程序;
  4. 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能认为的干预的。
  5. 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
  6. 线程会带来额外的开销,如cpu调度时间,并发控制开销。
    每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。

线程的创建

Thread 、Runnable、Callable


Thread

  1. 自定义线程类继承Thread
  2. 重写run()方法,编写线程执行体
  3. 创建新线程对象,调用start()方法启动线程

**总结:**线程开启不一定立即执行,由CPU调度执行
多线程 -Thread -Runnable -Callable_第1张图片
Runnable

  1. 定义MyRunnable类实现Runnable接口
  2. 实现run()方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程

Comparing Thread And Runnable
多线程 -Thread -Runnable -Callable_第2张图片
总结:

继承 Thread 类:

  1. 子类继承Thread类具备多线程能力
  2. 启动线程:子类对象.start()
  3. 不建议使用,避免OOP单继承局限性

实现Runnable接口:

  1. 实现接口Runnable具有多线程能力
  2. 启动线程:传入目标对象+Thread对象.start()
  3. 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

并发

Callable

实现callable接口:

  1. 实现callable接口,需要返回值类型
  2. 实现call方法,需要抛出异常
  3. 创建目标对象
  4. 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
  5. 提交执行: Future result1 = ser.submit(t1);
  6. 获取结果: boolean r1 = result1.get()
  7. 关闭服务: ser.shutdownNow();

静态代理

  1. 真实对象好代理对象都要实现同一个接口
  2. 代理对象要实现真实的角色
  3. 代理对象可以做很多真实的对象做不了的事情
  4. 真实对象专注于做自己的事情

线程状态

5大状态

多线程 -Thread -Runnable -Callable_第3张图片
主要方法
多线程 -Thread -Runnable -Callable_第4张图片
Eg 1:倒计时

public class test2 {
    public static void main(String[] args) throws InterruptedException {
        tenDow();
    }
    public static void tenDow() throws InterruptedException {
        int num = 10;
        while (true){
            Thread.sleep(1000);
            System.out.println(num--);
            if(num <= 0){
                break;
            }
        }
    }
}

Eg2 :获取当前时间

public class test2 {
    public static void main(String[] args) throws InterruptedException {
        tenDow();
        // 打印当前时间,获取当前系统时间
        Date startTime = new Date(System.currentTimeMillis());
        while (true){
            Thread.sleep(1000);
            System.out.println(new SimpleDateFormat("hh:mm:ss").format(startTime));
            startTime = new Date(System.currentTimeMillis());
        }
    }
    public static void tenDow() throws InterruptedException {
        int num = 10;
        while (true){
            Thread.sleep(1000);
            System.out.println(num--);
            if(num <= 0){
                break;
            }
        }
    }
}

线程休眠

  1. sleep(时间)指定当前线程阻塞的毫秒数
  2. sleep存在异常interruedException
  3. sleep时间达到后线程进入就绪状态
  4. sleep可以模拟网络延时,倒计时等,倒计时详见上面的例子
  5. 面一个对象都有一个锁,sleep不会释放锁

线程礼让

  1. 线程礼让,让当前正在执行的线程暂停,但不阻塞
  2. 将线程从运行状态转为就绪状态
  3. 让CPU重新调度,但不一定礼让成功,看CPU的心情
public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();

        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();
        new Thread(myYield,"c").start();
        new Thread(myYield,"d").start();

    }
}

class MyYield implements Runnable{

    /**
     * When an object implementing interface Runnable is used
     * to create a thread, starting the thread causes the object's
     * run method to be called in that separately executing
     * thread.
     * 

* The general contract of the method run is that it may * take any action whatsoever. * * @see Thread#run() */ @Override public void run() { System.out.println(Thread.currentThread().getName()+"线程开始执行"); // 礼让线程 Thread.yield(); System.out.println("线程暂停执行"); } }

小栗子
多线程 -Thread -Runnable -Callable_第5张图片

Join

Join合并线程,待此线程执行完成后,再执行其他线程

public class TestJoin implements Runnable {

    /**
     * When an object implementing interface Runnable is used
     * to create a thread, starting the thread causes the object's
     * run method to be called in that separately executing
     * thread.
     * 

* The general contract of the method run is that it may * take any action whatsoever. * * @see Thread#run() */ @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println("线程来了" + i); } } public static void main(String[] args) throws InterruptedException { TestJoin testJoin = new TestJoin(); Thread thread = new Thread(); thread.start(); for (int i = 0; i < 500; i++) { if(i == 200){ thread.join(); } System.out.println("main " + i); } } }

线程状态

public class TestState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("==========");
        });
        // 观察装填
        Thread.State state =thread.getState();
        System.out.println(state);
        //  观察启动后的状态
        thread.start();
        state = thread.getState();
        System.out.println(state);
        while (state != Thread.State.TERMINATED){
            Thread.sleep(1000);
            // 更新线程状态
            state = thread.getState();
            System.out.println(state);
            // 线程死亡后便不能再开启
            thread.start();
        }
    }
}

多线程 -Thread -Runnable -Callable_第6张图片

public class TestPrioriy {
    public static void main(String[] args) {
        // 主线程的优先级
        System.out.println(Thread.currentThread().getPriority());
        Mypriority mypriority = new Mypriority();
        Thread thread = new Thread(mypriority);
        Thread thread2 = new Thread(mypriority);
        Thread thread3= new Thread(mypriority);
        Thread thread4 = new Thread(mypriority);
        Thread thread5 = new Thread(mypriority);
        Thread thread6 = new Thread(mypriority);

        // 先设置优先级,再启动
        thread.start();
        thread2.setPriority(1);
        thread2.start();

        thread3.setPriority(Thread.MAX_PRIORITY);
        thread3.start();

        thread4.setPriority(Thread.MIN_PRIORITY);
        thread4.start();

        thread5.setPriority(5);
        thread5.start();

        thread6.setPriority(Thread.NORM_PRIORITY);
        thread6.start();
    }
}
class Mypriority implements Runnable{

    /**
     * When an object implementing interface Runnable is used
     * to create a thread, starting the thread causes the object's
     * run method to be called in that separately executing
     * thread.
     * 

* The general contract of the method run is that it may * take any action whatsoever. * * @see Thread#run() */ @Override public void run() { System.out.println(Thread.currentThread().getName() + Thread.currentThread().getState()); } }

多线程 -Thread -Runnable -Callable_第7张图片

守护(daemon)线程

线程分为用户线程和守护线程

虚拟机必须确保用户线程执行完毕

虚拟机不必等待守护线程执行完毕

public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();
        Thread thread = new Thread(god);
        thread.setDaemon(true); // 默认是false表示用户线程,正常线程是用户线程
        thread.start(); // 上帝守护线程

        new Thread(you).start(); // 你 启动用户线程
    }
}

// 上帝
class God implements Runnable{

    /**
     * When an object implementing interface Runnable is used
     * to create a thread, starting the thread causes the object's
     * run method to be called in that separately executing
     * thread.
     * 

* The general contract of the method run is that it may * take any action whatsoever. * * @see Thread#run() */ @Override public void run() { while (true){ System.out.println("上帝永生"); } } } // 你自己 class You implements Runnable{ /** * When an object implementing interface Runnable is used * to create a thread, starting the thread causes the object's * run method to be called in that separately executing * thread. *

* The general contract of the method run is that it may * take any action whatsoever. * * @see Thread#run() */ @Override public void run() { for (int i = 0; i < 3000; i++) { System.out.println("人生不过三万天"); } System.out.println("上帝保佑着你"); } }

多线程 -Thread -Runnable -Callable_第8张图片

线程同步

多个线程操作同一个资源

**并发:**同一个对象被多个线程同时操作

注:

处理多线程时,多个线程访问同一个对象,并且某些线程还想修改这个对象。这时,我们就需要线程同步,线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。

队列和锁

由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized ,当一个线程获得对象的排它锁,独占资源﹐其他线程必须等待,使用后释放锁即可

存在以下问题:
一个线程持有锁会导致其他所有需要此锁的线程挂起;
在多线程竞争下﹐加锁﹐释放锁会导致比较多的上下文切换和调度延时,引起性能问题;
如果一个优先级高的线程等待一个优先级低的线程释放锁 会导致优先级倒置﹐引起性能问题。

同步方法

synchronized方法和synchronized块

同步块
多线程 -Thread -Runnable -Callable_第9张图片

死锁

多个线程各自占有一些共享的资源,并且等待其他线程占有资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情况,某一个同步块同时拥有“两个以上对象的锁”时,就可能发生“死锁”的问题

**产生死锁的四个必要条件

  1. 互斥条件:一个资源每次只能被一个进程使用
  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
  3. 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

上面四个条件破解1个,即可破解死锁

lock(锁)

多线程 -Thread -Runnable -Callable_第10张图片

public class DeadLock {
    public static void main(String[] args) {
        Makeup g1 = new Makeup(0,"李亚东");
        Makeup g2 = new Makeup(1,"臭屁东子");

        g1.start();
        g2.start();

    }

}
class Lipstick{

}
class Mirror{

}
class Makeup extends Thread{
    static Lipstick lipstick =new Lipstick();
    static Mirror mirror = new Mirror();

    int chose ;
    String girlName;

    Makeup(int chose,String girlName){
        this.chose = chose;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        // 化妆
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
    private void makeup() throws InterruptedException {
        if(chose == 0){
            synchronized (lipstick){
                // 获得口红的锁
                System.out.println(this.girlName + "获得了口红锁");
                Thread.sleep(100);
            }
            synchronized (mirror){
                System.out.println(this.girlName + "获得了镜子的锁");
            }
        }else {
            synchronized (mirror){
                // 获得口红的锁
                System.out.println(this.girlName + "获得了镜子锁");
                Thread.sleep(200);
            }
            synchronized (lipstick){
                System.out.println(this.girlName + "获得了口红的锁");
            }
        }
    }
}

synchronized和Lock 的对比

  1. Lock是显式锁(手动开启和关闭锁,不能忘记关闭锁)synchronized是隐式锁,出了作用域自动释放
  2. Lock只有代码块锁,synchronized有代码块锁和方法锁
  3. 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好地扩展性(提供更多的子类)
  4. 优先使用顺序:
    ​ Lock > 同步代码块 (已经进入了方法体,分配了相应资源) > 同步方法(在方法体之外)

线程通信

多线程 -Thread -Runnable -Callable_第11张图片

线程池

多线程 -Thread -Runnable -Callable_第12张图片

public class TestPool {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(2);
        // 执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        // 关闭
        service.shutdown();
    }
}
class MyThread implements Runnable{
    @Override
    public void run() {

        System.out.println(Thread.currentThread().getName());

    }
}

你可能感兴趣的:(java)