面8-多线程2

1、线程介绍

2、创建并启动线程

2.1 继承Thread类
2.2 实现Runnable接口
2.3 实现Callable接口

3、线程的生命周期

3.1 生命周期

new ----start—>Runnable----->Running ---->blocked---->terminated

3.2 start方法源码解析
public class TempleteMethod {
    public static void main(String[] args) {
        TempleteMethod t1 = new TempleteMethod(){
            @Override
            protected void wrapPrint(String message) {
                System.out.println(" message : " + message );
            }
        };
        t1.print("hello ");
    }
    public final void print(String message){
        System.out.println("***********");
        wrapPrint(message);
        System.out.println("***********");
    }
    protected void wrapPrint(String message){
        System.out.println(message);
    }
}

此例中:print方法类似于Thread中的start方法

4、Thread-构造函数

4.1 构造函数:Thread()

1、默认有一个线程名,以“Thread-”开头,从0开始计数
2、若没有重写run方法,则不会调用任何东西
3、如果构造线程对象时未传入ThreadGroup,此时Thread会获得父线程的ThreadGroup作为该线程的ThreadGroup,此时子线程和父线程出现在同一个ThreadGroup中

4.2 ThreadGroup
 		ThreadGroup tg = Thread.currentThread().getThreadGroup();
        Thread[] th1 = new Thread[tg.activeCount()];
        tg.enumerate(th1);
        Arrays.asList(th1).forEach(System.out::println);
4.3 StackSize

1、构建Thread的时候传入stackSize代表着该线程占用的stack大小
2、如果没有指定stackSize的大小,默认是0,0代表着会忽略该参数,该参数会被JNI函数去使用,该参数在一些平台有效,在有些平台无效

5、Thread-API

5.1 守护线程
public class ThreadDaemon {
    public static void main(String[] args) {
        Thread thread = new Thread(()->{
            try {
                while (true){
                    System.out.println(" sleep --- ");
                    Thread.sleep(1000);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.setDaemon(true);
        thread.start();
        System.out.println(thread.isDaemon()); // 是否为守护线程
        System.out.println(" main - thread ");
    }
}

主线程生命周期结束,守护线程也结束

5.2 线程优先级
    System.out.println(thread.getPriority()); //5
5.3 合并线程(join)

可以想象成插队(必须放在.start()之后)

thread.join(100); //可以设置时间
Thread.currentThread().join(); // 自己等自己 死等
5.4 中断(interrupt、wait)
thread.isInterrupted()
thread.interrupted()
5.5 采用优雅的方法结束线程生命周期

1、使用标志位

public class ThreadStop {
    public static void main(String[] args) throws InterruptedException {
        ThreadTest thread = new ThreadTest();
        thread.start();
        Thread.sleep(1000);
        thread.shutdown();
    }
}
class ThreadTest extends Thread {
    private boolean flag = true;
    @Override
    public void run(){
        int i = 0;
        while (flag) {
            System.out.println(" i : " + i ++ );
        }
    }
    public void shutdown(){
        this.flag = false;
    }
}

2、捕获InterruptionException异常,直接break
3、Thread.interrupted

5.6 暴力结束线程实战

1、定义为守护线程

5.7 sleep和wait的区别

1、sleep是Thread的方法,wait是所有object的方法
2、sleep不会释放这个锁,而wait会释放锁并且会加入 object等待锁队列中
3、使用wait必须使用synchronized
4、sleep不需要被唤醒,但是wait需要

6、同步、并发、synchronize,死锁

6.1 synchronized
class Ticket implements Runnable{
    private final Object object =new Object();
    @Override
    public void run() {
        while (true) {
            synchronized (object) {
                if (true) {}
            }
        }
    }
}
6.2 同步方法、同步代码块

1、同步代码块

class Ticket implements Runnable{
    private final Object object =new Object();
    @Override
    public void run() {
        while (true) {
            synchronized (object) {
                if (true) {}
            }
        }
    }
}

2、同步方法(就是锁this)

public synchronized void buy(){
        if (flag<= num) {
            System.out.println( Thread.currentThread().getName() +" : "+ flag);
            flag++;
        }
    }
6.3 this锁

1、代码实例:

public class ThisSync {
    public static void main(String[] args) {
        ThisTest thisTest = new ThisTest();
        new Thread("T1") {
            @Override
            public void run(){
                try {
                    thisTest.fun1();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
        new Thread("T2") {
            @Override
            public void run(){
                try {
                    thisTest.fun2();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
}
class ThisTest{
    public synchronized void fun1() throws InterruptedException {
        Thread.sleep(100);
        System.out.println("fun1 ---- ");
    }
    public synchronized void fun2() throws InterruptedException {
//        Thread.sleep(100);
        System.out.println("fun2 ---- ");
    }
}

当 给fun2()加锁时,main方法中按顺序执行
当不给fun2()加锁时,一般先执行完fun2

2、结论
锁方法就是锁 This

6.4 静态代码块加的锁就是 class锁

1、给静态代码块被jvm保证执行一次,对他加锁是否有意义?
没有意义,jvm保证静态代码块执行一次,保证了其的安全性
2、代码实例

static {
        synchronized (ThisTest.class) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public synchronized static void fun3(){
        try {
            Thread.sleep(1000);
            System.out.println("fun 3 ---- ");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
6.5 死锁
6.6 显示的锁Lock

7、线程之间的通信

7.1 线程之间未通信
public class ProduceConsumeV1 {
    public static void main(String[] args) {
        ProduceConsumeV1 p1 = new ProduceConsumeV1();
        new Thread("t1") {
            @Override
            public void run(){
                p1.produce();
            }
        }.start();
        new Thread("t2") {
            @Override
            public void run(){
                p1.consume();
            }
        }.start();
    }
    private int index = 0;
    final Object object = new Object();
    public void produce(){
        synchronized (object) {
            while (true) {
//                if (index < 50)
                    System.out.println("pro -- " + index ++ );
            }
        }
    }
    public void consume(){
        synchronized (object) {
            while (true) {
//                if (index >0)
                    System.out.println("con -- " + index -- );
            }
        }
    }
}
7.2 线程之间的通信(通知:notify,等待:wait)
public class ProduceConsumeV2 {
    public static void main(String[] args) {
        ProduceConsumeV2 p2 = new ProduceConsumeV2();
        new Thread("t1"){
            @Override
            public void run(){
                try {
                    while (true){
                        p2.produce();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
        new Thread("t2"){
            @Override
            public void run(){
                try {
                    while (true){
                        p2.consume();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }

    private int index = 0;
    final Object object = new Object();
    private boolean flag = false;
    public void produce() throws InterruptedException {
        synchronized (object) {
            if (flag) {
                object.wait();
            } else {
                index++;
                System.out.println("pro : " + index );
                object.notify();
                flag = true;
            }
        }
    }
    public void consume() throws InterruptedException {
        synchronized (object) {
            if (flag) {
                System.out.println("com : " + index-- );
                object.notify();
                flag = false;
            } else {
                object.wait();
            }
        }
    }
}

7.3 上述代码存在的问题

1、多个线程同时调用.produce和.consume时会出现错误(程序会假死)
2、问题分析:notify时,不知道notify的是哪一个具体的线程
3、问题解决:使用notifyAll()

public class ProduceConsumeV3 {
    public static void main(String[] args) {
        ProduceConsumeV3 p3 = new ProduceConsumeV3();
        new Thread("push1"){
            @Override
            public void run(){
                while (true){
                    p3.push();
                }
            }
        }.start();
        new Thread("pop1"){
            @Override
            public void run(){
                while (true){
                    p3.pop();
                }
            }
        }.start();
        new Thread("pop2"){
            @Override
            public void run(){
                while (true){
                    p3.pop();
                }
            }
        }.start();
        new Thread("push2"){
            @Override
            public void run(){
                while (true){
                    p3.push();
                }
            }
        }.start();
    }
    private int index = 0;
    final Object object = new Object();
    private boolean flag = false;
    public void push(){
        synchronized (object) {
            while (flag) {
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            index ++ ;
            System.out.println( Thread.currentThread().getName() + " push : " + index);
            object.notifyAll();
            flag = true;
        }
    }
    public void pop(){
        synchronized (object) {
            while (!flag) {
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
            System.out.println(Thread.currentThread().getName() + " pop : " + index );
//            index -- ;
            object.notifyAll();
            flag = false;
        }
    }
}

8、一些方法

8.1 setUncaughtExceptionHandler
public class CatchException {
    public static void main(String[] args) {
        int a = 0;
        int b = 10;
        Thread thread = new Thread(()->{
            try {
                Thread.sleep(1_000);
                int c = b/a;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.setUncaughtExceptionHandler((t, e) -> System.out.println(e)); // java.lang.ArithmeticException: / by zero
        thread.start();
    }
}
8.2 方法追溯(Thread.currentThread().getStackTrace())

9、ThreadGroup

9.1 创建
ThreadGroup t1 = new ThreadGroup("tg1");
        Thread thread = new Thread(t1,"hel") {
            @Override
            public void run(){
                System.out.println(getThreadGroup().getName());// tg1
              System.out.println(getThreadGroup().getParent()); //java.lang.ThreadGroup[name=main,maxpri=10]
            }
        };
        thread.start();
ThreadGroup t2 = new ThreadGroup(t1,"tg2");
        Thread thread1 = new Thread(t2,"hel") {
            @Override
            public void run(){
                System.out.println(getThreadGroup().getName()); // tg2
                System.out.println(getThreadGroup().getParent()); //java.lang.ThreadGroup[name=tg1,maxpri=10]
            }
        };
        thread1.start();
9.2 常用 API

1、activeAccount()
2、activeGroupAccount()

10、线程池

10.1 线程池的原理

1、任务队列(调度)
2、拒绝策略(抛出异常,直接丢弃,阻塞,临时队列)
3、初始化值(init、active、max)

10.2 线程池实现

11、其他

11.1 ThreadLocal
  1. ThreadLocal的作用

他可以解决多线程的数据安全的问题
他可以给当前线程关联一个数据(可以是普通变量,也可以是对象,也可以是数组,集合)

  1. ThreadLocal的特点

ThreadLocal可以为当前线程关联一个数据(他可以像Map一样存取数据,key为当前线程)
每一个ThreadLocal对象,只能为当前线程关联一个数据,如果要为当前线程关联多个数据,就需要使用多个ThreadLocal对象实例
每一个ThreadLocal对象实例定义的时候,一般都是static类型
ThreadLocal中保存的数据,在线程销毁之后,会由JVM虚拟机自动释放

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值