线程基础学习小结

创建线程的几种方式:

一:继承

        1. 创建一个类,继承Thread类
        2. 重写父类中的run方法 -> 自己编写线程对象的任务
        3. 创建子类的对象,并调用start()方法启动线程

创建线程类 

public class MyThread extends Thread{
    public MyThread() {
    }

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(this.getName()+"执行:"+i);
        }
    }
}

测试: 

public class ThreadDemo {
    public static void main(String[] args) {
        MyThread mt = new MyThread("线程1");

        mt.start();

        for (int i = 0; i < 100; i++) {
            System.out.println("主线程执行:"+i);
        }
    }
}

 从图中可知线程交替执行。

线程执行的内存图👇

 

二:实现

    Thread类中的构造方法:
    Thread(Runnable target) 

        1. 准备一个Runnable接口的实现类对象(拿类实现/匿名内部类) ,重写run方法   
        2. 创建Runnable的实现类对象
        3. 创建Thread类对象并把Runnable的实现类对象传递进去
        4. 启动线程

public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName()+"hello"+i);
                }
            }
        },"线程名");

        thread.start();

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

 

线程的第三种开启方式(有结果的线程任务):

Callable<V> 接口 -> 线程的任务接口  V: 线程完成任务后,结果的数据类型
    有且仅有一个方法: V call()
        
中间类 : FutureTask
    FutureTask(Callable<V> callable) 
    FutureTask(Runnable runnable, V result) 
    
类和接口之间的关系:
    1. Runnable接口 是 FutureTask 的父接口
    2. FutureTask(Callable<V> callable) 
    3. Thread(Runnable target)
    
1. 创建Callable<V>的实现类对象,重写call方法编写线程的任务
2. 创建FutureTask对象,并把任务对象传递给FutureTask对象
3. 创建Thread对象,并把FutureTask对象传递给Thread对象
4. 线程对象调用start方法,启动线程

FutureTask类中的V get()方法 : (阻塞方法)
    获取线程执行完毕后的结果,必须要等到线程执行完毕后返回结果才执行!

public class ThreadDemo2 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<String> call = new Callable<String>() {
            @Override
            public String call() throws Exception {
                for (int i = 0; i < 100; i++) {
                    System.out.println("有返回值的线程内部执行");
                }
                return "返回值内容";
            }
        };

        FutureTask<String> ft = new FutureTask<String>(call);

        Thread thread = new Thread(ft);
        thread.start();


        for (int i = 0; i < 1000; i++) {
            System.out.println("main主线程执行:"+i);
        }
        System.out.println("ft.get() = " + ft.get());
    }
}

注意:ft.get()方法要放在希望线程交替执行的最后位置,不然会出现

线程并没有交替执行,main主线程执行是在有返回值的线程执行完后再执行了。

线程的名称设置和获取方法:

线程对象名的设置:
    1. 构造方法:
        Thread(String name) 
        Thread(Runnable target, String name) 


    2. 成员方法:
         void setName(String name)  
             
获取线程对象的名称:
    Thread类中的成员方法: String getName()
        
 //若不能直接调用getName(),可以先使用Thread类中的静态方法:
        static Thread currentThread(): 获取当前线程对象

public class ThreadDemo1 {
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName()+"hello"+i);
                }
            }
        },"线程名");

        thread.setName("自定义线程名");
        thread.start();
        Thread thread1 = new Thread("自定义线程名2");
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"PHP"+i);
        }
    }
}

 

 获取线程名:  

String name = thread1.getName();
Thread.currentThread().getName()

线程的优先级:

线程的默认优先级 : 5
线程的最小优先级 : 1
线程的最大优先级 : 10
//优先级越高的线程 优先执行的概率越大    

获取线程优先级的方法:
     int getPriority() 
设置线程优先级的方法:
     void setPriority(int newPriority) 

这个有点玄学了,真的运行也并不一定按照设定的优先级来。。。

守护线程:

void setDaemon(boolean on) : 传入true 设置为守护线程

public class ThreadDemo4 {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println("a ?");
                }
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("shuile");
            }
        });

        t1.setDaemon(true);

        t1.start();
        t2.start();
    }
}

守护线程就是保姆线程,在其他所有线程结束后再执行的线程。你吃肉他喝汤刷碗。

线程休眠的方法:

        static void sleep(long millis) : 让线程休眠 -> 休眠多久由传入的毫秒值决定

        休眠 : 线程对象仅仅释放CPU的执行权,不释放锁资源

线程的安全问题(重点):

        解决线程安全问题的方法有:

        1.同步操作

                同步操作 : 就是上锁的意思

                锁对象具备的特点:
                    1. 唯一
                    2. 能控制所有的线程对象
                    3. 用于做锁的元素必须是引用数据类型 -> 优先推荐: Object
    
                同步后的代码线程对象要执行必须:    
                    1. CPU的执行权
                    2. 锁资源

    2.同步代码块

public class SellTicket1 implements Runnable{
    //总票数 :  共享数据
    int ticket = 100;
    //定义锁对象
    Object obj = new Object();

    @Override
    public void run() {

        //火车站不关门
        while(true){

            synchronized (obj){
                //判断
                if (ticket > 0){

                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    //买票
                    System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票~");
                    //真正的买票
                    ticket--;
                }
            }
        }
    }
}

 3.同步方法

public class SellTicket2 implements Runnable {

    //总票数 :  共享数据
    int ticket = 100;

    @Override
    public void run() {
        //火车站不关门
        while (true) {
            this.sell();
        }
    }

    //同步方法
    //同步方法的锁对象是谁? this
    //静态同步方法的锁对象是谁? 本类的字节码对象 .class对象
    public synchronized void sell() {
        //判断
        if (ticket > 0) {

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //买票
            System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票~");
            //真正的买票
            ticket--;
        }
    }
}

4.同Lock接口(面向对象的方式上锁)

public class SellTicket3 implements Runnable{
    //总票数 :  共享数据
    int ticket = 100;

    //共享的lock对象
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
        //火车站不关门
        while(true){
            //上锁
            lock.lock();
            //判断
            if (ticket > 0){

                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                //买票
                System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票~");
                //真正的买票
                ticket--;
            }
            //解锁
            lock.unlock();
        }
    }
}

线程的生命周期:

NEW : 新建 -> 线程对象创建出来了但是没有调用start
BLOCKED : 阻塞 -> 线程醒着但是没有锁资源或者没有CPU执行权
TIMED_WAITING -> 限时等待
    场景1 : 线程对象被调用了sleep(毫秒值) -> sleep: 仅仅丢失CPU执行权,拥有锁资源
    场景2 : 线程对象被调用了wait(毫秒值) -> wait: 丢失锁资源和CPU执行权
WAITING -> 无限等待
    场景 :  线程对象被调用了wait() -> 只有等待被唤醒才能从无限等待状态中复活
RUNNABLE : 运行 -> 拥有CPU执行权,拥有锁对象 正在执行的线程对象
TERMINATED : 死亡 -> 已经死亡的线程状态

 等待和唤醒的方法:

等待和唤醒的方法来自于 Object 类

一般都是使用锁对象控制线程的执行!!
    
唤醒方法: 只是唤醒线程,线程醒了还需要去抢夺CPU资源和锁资源
    void notify() : 随机唤醒一个"沉睡"的线程对象
     void notifyAll() : 唤醒所有"沉睡"的线程对象

        
等待方法: wait : 释放锁和CPU资源
    void wait() : 无限等待 不被调用notify系列方法线程就不会醒  
    void wait(long timeout)  : 限时等待 时间到自己醒或者在中途被唤醒
    void wait(long timeout, int nanos)      

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值