Java技术栈 —— 线程知识(一)

一、基础知识

1.1 创建线程

/**
 * (1)更符合面向接口编程的原则,因为Runnable是一个独立的接口,可以更好地支持代码的扩展和复用。
 * (2)不强制你继承Thread类,可以保持代码的简洁和灵活性。
 * *(3)可以将Runnable对象传递给其他线程,实现多线程之间的共享和协作。这点是关键点
 */
class MyRunnable implements Runnable{ //更灵活更推荐
    // 线程执行的代码逻辑
    @Override
    public void run() {
        System.out.println("MyRunnable...");
    }
}

/**
 * 线程类已经继承了Runnable接口,所以你不需要再显式地实现Runnable接口。
 * 可以直接使用Thread类的方法,如start、sleep、join等。
 * 由于继承了Thread类,你的类将与Thread类有直接的继承关系,可能导致代码的耦合性较高。
 * Thread本身也是 implements Runnable接口的,也就是说这两种方式的本质都是要implements Runnable接口的
* */
class MyThread extends Thread{ //不关心复用Runnable对象或不关心代码灵活性时使用该方式
    // 线程执行的代码逻辑
    @Override
    public void run() {
        System.out.println("MyThread...");
    }
}

class Test{ //发现在运行test_lambda_Runnable的时候,进入Thread构造函数时,这个类是implements Runnable接口的
   void test_lambda_Runnable(){
       Thread thread = new Thread(new Runnable() { //相当于内部新建了一个匿名对象并implements了Runnable接口,但是从debug效果上看是有区别的
           @Override
           public void run() {
               System.out.println("等价实现: Lambda 表达式实现 Runnable...");
           }
       });
       thread.start();
   }
}

public class Runnable_Thread_Test {
    /**
     * 总体而言,实现Runnable接口是更灵活和推荐的方式,因为它更好地符合面向对象编程的原则。
     * 但在某些情况下,继承Thread类可能更方便,特别是当你不需要复用Runnable对象或不关心代码的灵活性时。
     * 需要注意的是,无论你选择哪种方式,都需要重写run方法来定义线程的执行逻辑。
     * */
    public static void main(String[] args) {
        //创造线程的方法1
//        MyRunnable myRunnable = new MyRunnable();
//        Thread thread = new Thread(myRunnable);
//        thread.start();
        //方法1的等价实现(1)
//        Thread thread = new Thread(new Runnable() { //相当于内部新建了一个匿名对象并implements了Runnable接口,但是从debug效果上看是有区别的
//            @Override
//            public void run() {
//                System.out.println("等价实现: Lambda 表达式实现 Runnable...");
//            }
//        });
//        thread.start();
        //方法1的等价实现(2)
        Test test = new Test();
        test.test_lambda_Runnable();


        //创造线程的方法2
        MyThread myThread = new MyThread();
        myThread.start();        //与Runnable的区别是可以直接在这个类上调用.start()方法

    }
}

1.2 线程方法start()与join()

public class JoinExample {
    public static void main(String[] args) {
        int count = 1000;
        while (count-- > 0){
            // 创建并启动线程
            Thread thread = new Thread(new MyRunnable());
            thread.start();

            // 等待线程执行完毕
            try {
                thread.join(); // 这句话意味着until thread.join() main thread,要等到thread执行完加入到main线程中
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("Main thread exiting.");
            System.out.println("注意两句话的打印先后顺序,有没有join()是不一样的,没有join完全就是随机的,看thread执行情况,有join后线程执行顺序就会固定了");
            System.out.println();
        }
    }

    static class MyRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println("Thread is running.");
        }
    }
}

二、线程高级

2.1 volatile关键字(涉及硬件特性)

/**
 * 多线程内存模型,每个线程会把自己用到的变量,拷贝一份副本放到自己所在CPU的working_cache中
 * CPU1(Thread1) -> CPU1_working_cache -> RAM
 * CPU2(Thread2)-> CPU2_working_cache -> RAM
 * 总线的缓存一致性协议MESI,其它总线通过嗅探机制感知到数据变化,从而使自己CPU内存的变量失效
 * 把这个类的汇编代码打印出来
 * 并发编程:可见性,有序性,原子性
 * */
//JMM内存模型
//讲解地址 https://v.shanhuketang.com/772388bdvodtranscq1317978474/b6e05eb83270835013110899340/v.f1440843.mp4
public class VolatileVisibilityTest {
//    private static boolean initFlag = false;

    /**
     * volatile保证底层可见性,实际上是开启了硬件的一些特殊作用,说的就是lock
     * */
    private static volatile boolean initFlag = false; //加一个volatile关键字去解决问题,就会打印success

    public static void main(String[] args) throws InterruptedException {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("waiting data...");
                while(!initFlag){

                }
                System.out.println("=================success");

            }
        }).start();

        Thread.sleep(2000);

        //虽然没有明确地实现Runnable接口,但 Java 中的 Lambda 表达式可以自动推断并隐式地实现Runnable接口。
//        new Thread(()->prepareData()).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                prepareData();
            }
        }).start(); //上面的等价实现
    }

    public static void prepareData(){
        System.out.println("prepare data...");
        initFlag = true;
        System.out.println("prepare data end...");
    }
}
  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值