首页 > 其他分享 >线程

线程

时间:2023-08-06 16:31:32浏览次数:42  
标签:Thread void start 线程 new public

程序、进程、线程

程序:为完成某些特定任务,用某种语言编写的一组指令的集合,代码

进程:程序的一次执行过程,正在运行的一个程序

【这是一个动态过程,产生,存在,完成某些功能,消亡】

线程:由进程创建的,是进程的一个实体,一个进程可以拥有多个线程

【举个例子:一个正在读书并同时听音乐的人正在努力学习】

【这个正在努力学习的这个行为对应进程,而读书、听英语对应线程】

单线程、多线程、并发、并行

单线程:同一个时刻,只允许执行一个线程

【读书不能听音乐,听音乐不能读书】

多线程:同一个时刻,可允许执行多个线程

【边听音乐边读书】

并发:同一个时刻,多个任务交替执行,形成“同时”的错觉

简单来说,单核CPU实现的多任务就是并发

并行:同一个时刻,多个任务同时执行,多核CPU可实现

【一个CPU对应一个任务】

线程_synchronized线程_线程同步机制_02

【可以在计算机的设备管理器中查看自己的电脑是几核几个处理器】

线程的基本使用

1)继承Thread类,重写run方法

2)实现Runnable接口,重写run方法(推荐)

【推荐原因,java是单继承,继承Thread类不能再继承,而接口可以多实现】

第一种方式:

class Cat extends Thread{
    @Override
    public void run() {
        while(true){
            int count = 0;
            count++;
            System.out.println("子线程");
            try {
                Thread.sleep(100); // 线程休眠 单位ms
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (count == 50){
                break;
            }
        }
    }
}
public class Test1 {
    
    public static void main(String[] args) 
        throws InterruptedException {
        //main函数 --- 看作为主线程
        Cat c = new Cat();
        c.start(); // 开启一个独立的子线程
        for (int i =0 ;i<50;i++){
            System.out.println("主线程");
            Thread.sleep(100); // 线程休眠 单位ms
        }
        // 主程序运行是否结束 --- 不会影响子线程
        // 子线程和主线程都运行结束,程序才会停止

    }  
}

//注意:
//底层源码 --- 调start -> start0 --->调run  而不是直接调用run方法
//这里的run方法,由JVM机调用
//我们只需调用Thread中单start方法即可

第二种方式:

class Cat implements Runnable{
    @Override
    public void run() {
        while(true){
            int count = 0;
            count++;
            System.out.println("子线程");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (count == 50){
                break;
            }
        }
    }
}
public class Test2 {
    public static void main(String[] args) 
        throws InterruptedException {
        //main函数 --- 看作为主线程
        Cat c = new Cat();
        Thread T = new Thread(c);
        //接口中没有start方法,但是可以通过创建Thread对象
        //使用Thread的start方法调用
        //本质上与方式一,没有太多区别
        T.start();//开启一个独立的子线程
        for (int i =0 ;i<50;i++){
            System.out.println("主线程");
            Thread.sleep(100);
        }
        // 主程序运行是否结束 --- 不会影响子线程
        // 子线程和主线程都运行结束,程序才会停止
    }
}

//注意: 无论方式一还是方式二,其中实现多线程的方式都不是run方法
// 	   而是start0方法,其底层是由C/C++实现的
// 在start方法调用start0方法后,该线程不一定立马执行
// 只是将线程变为可运行状态,什么时候执行,取决于CPU,由CPU统一调度

线程终止

当线程完成任务后会自动退出,可以通过使用变量来控制run方法的方式停止线程,即通知方式

将方式二中例子进行修改

public class Test2 {
    public static void main(String[] args) 
        throws InterruptedException {
        //main函数 --- 看作为主线程
        Cat c = new Cat();
        Thread T = new Thread(c);// 开启一个独立的子线程
        T.start();
        for (int i =0 ;i<10;i++){
            System.out.println("主线程");
            Thread.sleep(100);
        }
        // 在主线程中对子线程通知停止
        c.setLoop(false);
        System.out.println("program is over !");
    }
}
class Cat implements Runnable{

    private boolean loop = true;

    public void setLoop(boolean loop) { 
        // 通过给loop赋值,是子线程停止运行,通知方式
        this.loop = loop;
    }

    @Override
    public void run() {
        while(loop){ // 子线程是一个死循环 ---- 永远不会退出
            System.out.println("子线程");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

用户线程、守护线程

用户线程:也叫工作线程,线程的任务执行完或由通知方式结束

守护线程:为工作线程服务,当所有的用户线程结束,守护线程自动结束

【垃圾回收机制 就是常见的守护线程】

// 守护线程
class Cat implements Runnable{
    @Override
    public void run() {
        while(true){ // 子线程是一个死循环 ---- 永远不会退出
            System.out.println("子线程");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Test2 {
    public static void main(String[] args)
        throws InterruptedException {
        //main函数 --- 看作为主线程
        Cat c = new Cat();
        Thread T = new Thread(c);// 开启一个独立的子线程
        // 我们希望当主线程结束后,子线程自动结束
        // 在主线程中 将子线程设置为守护线程
        T.setDaemon(true);
        T.start();
        for (int i =0 ;i<10;i++){
            System.out.println("主线程");
            Thread.sleep(100);
        }
        System.out.println("program is over !");
    }
}

线程的状态

线程_线程同步机制_03

线程常用方法

1)setName 设置线程名称,可使其与参数name相同

2)getName 返回该线程的名称

3)start使线程开始执行 Java虚拟机底层调用start0方法

4)run 调用线程对象run方法

5)setPriority 更改线程优先级

6)getPriority 获取线程优先级

7)sleep 让当前正在执行的线程休眠(暂停执行)

8)interrupt 中断线程,中断不是终止线程,用于提前中断子线程的休眠

9)yield 线程的礼让,让出CPU,让其他线程先执行,但不一定成功

10)join 线程的插队,优先实现插队子线程的所有内容,完成后,再继续其他线程

线程同步机制(synchronized)

为什么需要线程同步?

// 三个窗口卖100票,出现了超卖现象
package com.TEST;

public class Test2 {
    public static void main(String[] args) {
        SellTicket s1 = new SellTicket();
        Thread T1 = new Thread(s1);
        Thread T2 = new Thread(s1);
        Thread T3 = new Thread(s1);
        T1.start();
        T2.start();
        T3.start();
    }
}

//3个售票窗口,总售票100张,多线程实现
class SellTicket implements Runnable {
    boolean loop = true;
    Object obj = new Object();
    private static int Ticket = 10;//总票数10张
    @Override
    public void run() {
        while (loop) {
          	if (Ticket <= 0) {
                System.out.println("售票结束~");
                loop=false;
                return;
        	}
        
        //休眠50ms
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println("窗口"
                + Thread.currentThread().getName()
                + "售出一张票剩余"
                + (--Ticket)
                + "张票");

        }
    }
    
}

线程_多线程_04

如何解决该问题,往下看

线程同步机制

1)在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就需要使用同步访问技术,保证任何时刻,最多有一个线程访问,以保证数据的完整性,当然了,它会牺牲一些访问效率

2)线程同步:当有一个线程在对内存操作时,其他线程不能对该内存操作,直到该线程完成操作,其他线程才能对该内存地址操作

线程同步具体方法

1)同步代码块

synchronized (对象) { // 得到对象的锁,才能操作同步代码
  									// 锁指的就是,对象,谁得到了这个对象,谁就可以访问
  									// 该区域的内容,且该对象只有一个  							
		//需要同步的代码
}
//多个线程争夺唯一的一个锁,谁得到了锁,谁就可以访问

//简单来说就是,谁拿到了 对象(锁), 谁才能访问同步的代码
// 非静态同步方法、同步代码块(锁:默认为本类对象)
// 静态同步方法(锁:默认为类本身)

//始终只有一个线程可以得到该对象

2)同步方法

public synchronized void ok (传入参数){
	  //	需要同步的代码
}

【这种在任何时刻,只能有一个线程进行访问的状态,就是互斥锁的概念】

互斥锁

1)每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能由一个线程来访问对象

2)synchronized关键字来与对象的互斥锁练习,当一个对象用synchronized修饰时,表名该对象在任一时刻只能由一个线程访问

3)局限性:程序的执行效率会降低

4)非静态的同步方法的锁可以是this本类对象,也可以是其他对象

5)静态的同步方法的锁为当前类本身

使用互斥锁解决超卖现象的方式

// 利用同步方法 解决超卖现象
// 三个窗口卖100票,出现了超卖现象
package com.TEST;

public class Test2 {
    public static void main(String[] args) {
        SellTicket s1 = new SellTicket();
        Thread T1 = new Thread(s1);
        Thread T2 = new Thread(s1);
        Thread T3 = new Thread(s1);
        T1.start();
        T2.start();
        T3.start();
    }
}

//3个售票窗口,总售票100张,多线程实现
class SellTicket implements Runnable {
    boolean loop = true;
    Object obj = new Object();
    private static int Ticket = 10;//总票数10张
    @Override
    public void run() {
        while (loop) {
            sell();
        }
    }
    public synchronized void sell(){ 
        // 在任何时刻,只能有一个线程使用该方法 ,这也是互斥锁的体现
        if (Ticket <= 0) {
            System.out.println("售票结束~");
            loop=false;
            return;
        }
        //休眠50ms
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("窗口"
                + Thread.currentThread().getName()
                + "售出一张票剩余"
                + (--Ticket)
                + "张票");
    }
}

线程_多线程_05

注意事项

1)同步方法如果没有使用static,默认锁对象为this

2)同步方法有static修饰,默认锁对象为当前类.class

3)多个线程锁的对象需为同一个

死锁

多个线程都占用了对方锁的资源,但不肯想让,导致了死锁

package com.TEST;

public class Test2 {
    public static void main(String[] args) {
        Dead d1 = new Dead(true);
        Dead d2 = new Dead(false);
        d1.start();
        d2.start();
    }
}

class Dead extends Thread{
    static Object o1 = new Object();
    static Object o2 = new Object();
    private boolean flag;
    public Dead(boolean flag){
        this.flag = flag;
    }
    public void run(){
        if (flag){
            synchronized (o1){
            System.out.println(Thread.currentThread().getName() 
                               + "1");
            synchronized (o2) {
            System.out.println(Thread.currentThread().getName() 
                               + "2");
            }
            }
        }else{
            synchronized (o2){
            System.out.println(Thread.currentThread().getName() 
                               + "3");
            synchronized (o1) {
            System.out.println(Thread.currentThread().getName() 
                               + "4");
            }
            }
        }
    }
}

线程_多线程_06

释放锁

1)当线程的同步方法、同步代码块执行结束时,自动释放锁

2)当前线程在循环中遇到了break或return

3)当前线程在循环中出现了未处理的error或Exception导致异常结束

4)当前线程执行了wait()方法,当前线程暂停,并释放锁

注意事项:

线程调用sleep()、yield()方法只是暂停当前线程的执行,不会释放锁。线程执行同步代码块时,其他线程调用了suspend()方法将该线程挂起,该线程不会释放锁。

【应尽量避免使用线程的suspend()和resume()方法,

且编译器也不在推荐使用,已过时】


标签:Thread,void,start,线程,new,public
From: https://blog.51cto.com/u_16188762/6985226

相关文章

  • 为什么不建议或不能用Executor去创建线程池?
    答:会存在内存溢出的风险。因为Executors中的核心方法,默认创建线程池的最大线程数是Integer.MAX_VALUE即int类型的最大值2^32-1,最大线程数允许这么多,几乎相当于不限制线程数,而这样的后果就是,如果瞬间请求量非常大,如果达到这个上限,没有任何服务器能够继续工作,肯定会抛出OOM异常。Ex......
  • linux修改线程数
    vim/etc/security/limits.d/90-nproc.conf查看核心线程数量ulimit-u......
  • 进程与线程
        ......
  • 第二章进程和线程
    2.1任务在操作系统层面,任务常常时代表进程的,比如windows是典型的多任务操作系统,指系统中可以同时运行多个进程。在CPU手册中,很多时候是使用"任务"来代之线程的,比如著名的多任务状态段(TaskStateSegmentTSS).就是用来记录每个线程的状态。CPU一级的任务很多时候相当于进......
  • 对线程join()方法的理解
    java线程的join()方法的理解thread.join()把指定的线程加入到当前线程,可以将两个交替执行的线程和并为顺序执行的线程。简单说就是同步。例1:比如在线程B中调用了线程A的join方法,直到线程A执行完毕后,才会继续执行线程B。例2:再比如我们做查询操作,总任务需要返回三个查询列......
  • Java多线程-龟兔赛跑
    Java多线程-龟兔赛跑packagecom.alibaba;publicclassTestThread003implementsRunnable{privateStringwinner;@Overridepublicvoidrun(){for(inti=0;i<=100;i++){booleanflag=getWinner(i);if(flag){......
  • 深入理解线程与进程:概念、特点与区别,附带代码演示
    当今计算机系统中,线程(Thread)和进程(Process)是并发编程中的关键概念。它们对于提高程序的效率和性能至关重要。本篇博客将详细介绍线程和进程的概念、特点以及它们之间的区别,同时通过代码演示来加深理解。1.线程1.1概念线程是操作系统能够进行运算调度的最小单位。一个进程可以包含......
  • 多线程访问数据库报错
    在用flask实现http服务器的时候,只需要指定路由和访问方法,前端的访问就可以获取到,然后触发后端的响应函数,如果后端响应函数用公用的sqlconnection的时候,可能会导致数据库连接冲突报错,报错内容如下:AttributeError:'NoneType'objecthasnoattribute'read' 参考这篇博客的解......
  • java多线程并发面试题总结(史上最全40道)
    1、多线程有什么用?一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在我看来,这个回答更扯淡。所谓"知其然知其所以然","会用"只是"知其然","为什么用"才是"知其所以然",只有达到"知其然知其所以然"的程度才可以说是把一个知识点运用自如。OK,下面说说我对这个......
  • java多线程并发面试题总结(史上最全40道)
    1、多线程有什么用?一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在我看来,这个回答更扯淡。所谓"知其然知其所以然","会用"只是"知其然","为什么用"才是"知其所以然",只有达到"知其然知其所以然"的程度才可以说是把一个知识点运用自如。OK,下面说说我对这个问......