JavaSE—线程介绍(超详细!)

简介: JavaSE—线程介绍(超详细!)



🪅 区分程序、进程、线程


程序:为实现某种功能,使用计算机语言编写的一系列指令的集合。

          指的是静态的代码(安装在电脑上的那些文件)

进程:是运行中的程序(如运行中的原神)进程是操作系统进行资源分配的最小单位。

线程:进程可以进一步细化为线程,是进程中一个最小的执行单元,是cpu进行调度的最小单元

          例如:QQ中的一个聊天窗口

进程和线程的关系:

⑴ 一个进程中可以包含多个线程 (一个QQ程序可以有多个聊天窗口)


⑵ 一个线程只能隶属于一个进程 (QQ的聊天窗口只能属于QQ进程)


⑶ 每一个进程至少包含一个线程,也就是我们的主线程(像java中的main方法就是来启动主线程的)在主线程中可以创建并启动其他线程.


⑷ 一个进程的线程共享该进程的内存资源.

🪅 创建线程


通过继承Thread来创建线程

• 写一个类继承java.lang.Thread

• 重写run( )

线程中要执行的任务都要写在run( )中,或在run( )中进行调用.

public class Demo1 extends Thread{//继承Thread类
    @Override
    public void run() {//重写run方法
        for (int i = 1; i <= 200; i++) {
            System.out.println("run"+i);
        }
    }
}
 public static void main(String[] args) {
        //创建线程
        Demo1 demo1 = new Demo1();
        //启动线程
        demo1.start();
        for (int i = 1; i <= 200; i++) {
            System.out.println("main"+i);
        }
    }

📖 启动线程调用的是start() ; 不是run()

     run()这不是启动线程,只是一个方法调用,没有启动线程,还是单线程模式的。

通过实现Runnable接口来创建线程

• 创建任务,只先创建线程要执行的任务,创建一个类,实现Runnable接口.

• 重写任务执行的Run()

• 创建线程,并为线程指定执行任务.

public class Demo2 implements Runnable {//实现Runnable接口
    @Override
    public void run() {//重写run方法
        for (int i = 0; i < 200; i++) {
            System.out.println("自定义线程");
        }
    }
}
public static void main(String[] args) {
        //创建任务
        Demo2 demo2 = new Demo2();
        //创建线程,并指定执行任务
        Thread thread = new Thread(demo2);
        thread.start();
    }

实现Runnable接口创建的优点:

1. 因为java是单继承,一旦继承一个类就不能在继承其他类,避免单继承的局限。

2. 适合多线程来处理同一份资源时使用

🪅 Thread类中的方法

run()    用来定义线程要执行的任务代码.

start()  启动线程

currentThread() 获取到当前线程(.得到具体信息)

setName()   为线程设置名字

getState()  获取状态

getPriority() setPriority 获取/设置 优先级

sleep() 让当前线程休眠指定时间.

join()  等待当前线程执行完毕,其他线程再执行.

yield() 主动礼让,退出cpu重新回到等待序列.

📖 关于优先级:

【java中默认优先级为5, 设置优先级范围为1~10】 (作用:为操作系统调度算法提供的)

🪅 线程生命周期


线程状态:

新建:刚刚创建了一个线程对象,并没有启动


就绪:调用start() 后线程就进入到了就绪状态(可运行状态),进入到了操作系统的调度队列


运行状态:获得了cpu执行权,进入到cpu执行


阻塞状态:例如调用sleep() ,有线程调用了join(),线程中进行Scanner输入...


死亡/销毁:run()方法中的任务执行完毕了

状态关系图:

🪅 多线程的概念


• 顾名思义指:在一个程序中可以创建多个线程执行.

优点】提高程序执行效率(多个任务可以在不同的线程中同时执行)

             提高了cpu的利用率

             改善程序结构,将复杂任务拆分成若干个小任务

缺点】线程也是程序,线程越多占用内存也越多,cpu开销变大(扩充内存或升级cpu)

             线程之间同时对共享资源的访问会相互影响,若不加以控制会导致数据出错.

那么如何解决多线程操作共享数据的问题?

🪅 线程同步与锁(Lock)



       多个线程同时访问操作同一个共享的数据( 例如买票、抢购等 )时,可能会引起冲突,所以引入线程 “同步” 机制,即各线程间要有先来后到。

即通过【 排队+锁 】 在关键的步骤处,多个线程只能一个一个的执行.

synchronized(同步锁)  

      同步锁对象作用:用来记录有没有线程进入到同步代码块,如果有线程进入同步代码块,那么其他线程就不能进入同步代码块,直到上一个线程执行完同步代码块的内容,释放锁之后,其他线程才能进入。


      同步锁对象要求:同步锁对象必须是唯一的.


  synchronized(同步锁对象) {

        同步代码块      

  }  

● synchronized修饰方法时,同步锁对象不需要我们指定,同步锁对象会默认提供:


  1. 非静态方法 ------  默认是this


  2.  静态方法   ------  锁对象是当前类的class对象(一个类的对象只有一个)


ReentrantLock与synchronized区别?

  • synchronized是一个关键字,控制依靠底层编译后的指令去实现.
  • synchronized可以修饰一个方法或一个代码块.
  • synchronized是隐式的加锁和释放锁,一旦方法或代码块出现异常,会自动释放锁.


  • ReentrantLock是一个,依靠java底层代码去控制 (底层有一个同步队列)
  • ReentrantLock只能修饰代码块.
  • ReentrantLock需要手动的加锁和释放锁, 所以释放锁最好写在finally中, 一旦出现异常, 保证锁能释放.


误区:不是只要有线程就需要加锁,只有多个线程对同一资源共享时才加锁


模拟卖票

两个窗口分别售票,票数为10张

public class MyThread extends Thread{//我们使用了继承Thread的方法
    static int num =10;   //票总数10,且为共享资源,要用static修饰
    static String obj = new String();//可以是任意类对象,但必须唯一。
/*  synchronized(同步锁对象) {
         同步代码块
    }                         */
 
    @Override
    public void run() {//线程要执行的代码块要写在run()中
        while (true){
            synchronized (obj){//加锁,一次只能执行一个线程
                if(num>0){
                    try {
                        Thread.sleep(800);//此处加入休眠为了让运行结果更明显,也可不加
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"买到了第"+num+"张票");
                    num--;  //每抢一张票,总数num(10)就减1
                }else{
                    break;
                }
            }
        }
    }
}

在Main方法中创建线程并启动:

 
public static void main(String[] args) {
        //创建两个线程,分别对应两个窗口
        MyThread myThread1 = new MyThread();
        myThread1.setName("窗口1");//线程1
        myThread1.start();
 
        MyThread myThread2 = new MyThread();
        myThread2.setName("窗口2");//线程2
        myThread2.start();
    }

运行结果:

相关文章
|
7月前
|
安全 Java 调度
【Java】JavaSE实现多线程
【Java】JavaSE实现多线程
90 1
|
Java 调度 开发者
【JavaSE专栏84】线程让步,一种线程调度的机制
【JavaSE专栏84】线程让步,一种线程调度的机制
118 0
|
7月前
|
安全 算法 Java
JavaSE&多线程&线程池
JavaSE&多线程&线程池
173 7
|
7月前
|
Java API 调度
JavaSE基础精选-多线程
JavaSE基础精选-多线程
42 0
|
7月前
|
存储 缓存 Java
JavaSE基础篇:多线程
JavaSE基础篇:多线程
|
JSON 安全 Java
【JavaSE专栏87】线程终止问题,什么情况下需要终止线程,如何终止Java线程?
【JavaSE专栏87】线程终止问题,什么情况下需要终止线程,如何终止Java线程?
120 2
|
监控 Java 数据库连接
【JavaSE专栏86】守护线程的那些事,后台默默地守护,是最长情的告白
【JavaSE专栏86】守护线程的那些事,后台默默地守护,是最长情的告白
|
Java 调度
【JavaSE专栏85】线程优先权,线程调度谁先谁后一目了然
【JavaSE专栏85】线程优先权,线程调度谁先谁后一目了然
346 0
【JavaSE专栏83】线程插队,一个线程在另一个线程执行特定任务之前先执行
【JavaSE专栏83】线程插队,一个线程在另一个线程执行特定任务之前先执行
123 0
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
58 1
C++ 多线程之初识多线程