欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 产业 > Java线程

Java线程

2025/2/25 15:08:11 来源:https://blog.csdn.net/m0_53499553/article/details/142728083  浏览:    关键词:Java线程

目录

一、线程入门

二、线程同步

三、死锁

四、线程的方法

五、线程的流程图

六、线程池


一、线程入门

1.进程:每一个软件都是一个进程。

2.线程:进程是由多个线程组成的。

3.进程和线程的关系:一个进程是对应一个或者是多个线程的。

4.我们所有的操作都是由CPU来执行的,但是CPU一次只能处理一个操作,所以我们在打开了多个程序后,只是看似是一起运行的,实际上还是在单个运行:

(1)那么多个程序为什么可以看似是一起运行的,这是因为CUP把执行的时间分成了时间片段;

(2)比如:把1秒分成了多个时间片段,其中一部分时间片段是执行一个程序,而另一部分时间片段又去执行另一个程序,因为时间片段很快,我们是很难用肉眼去分辨的,所以在1秒内,多个程序是可以看作是一起运行的;

  • 秒后面的单位:微秒 -> 纳秒

5.线程类:Thread类

(1)创建线程:new一个线程类,但是一般情况下,我们是不会这么写的,我们会通过匿名内部类的方式来写;

Thread t = new Thread(); // 创建线程  -- 初始化

(2)使用匿名内部类的方法来创建线程;

// 创建线程
Thread t = new Thread(){//运行状态  -->>  抢到了CPU资源// 重写run方法public void run(){}
};

(3)子线程;

  • 在一个线程中创建了另一个线程,那么被创建出来的那个线程就是那个已有线程的子线程;

(4)线程类中的常用方法;

  • 获取当前线程的名字:Thread.currentThread().getName();
  • System.out.println(Thread.currentThread().getName());
  • 启动线程:线程类的对象名.start();
    • 注意线程只有在启动后才会执行里面的代码;
    • // 就绪状态 -->>  可以开始去抢占CPU的资源了
      t.start(); // 启动线程

(5)设置线程的优先级:线程类的对象名.setPriority(Thread的静态常量);

  •    t.setPriority(Thread.MAX_PRIORITY);
  • Thread.MAX_PRIORITY --> 10;
  • Thread.MIN_PRIORITY --> 1;
  • Thread.NORM_PRIORITY --> 5;
  • 设置线程为精灵线程,也可以称为守护线程或者后台线程:线程类的对象名.setDaemon(true);
    • 作用:如果将一个子线程设置为精灵线程,那么这个子线程会在主线程停止执行后一起停止,就算是子线程还没有执行完也会被强制停止;
    • t.setDaemon(true);

6.线程的状态:

(1)初始化状态:创建线程对象;

// 创建一个线程类  --  初始化
Thread t = new Thread();

(2)就绪状态:调用start()方法;

  • 就绪状态代表线程可以开始去抢占CPU资源了;
  • // 启动线程  --  就绪状态
    t.start();

(3)运行状态:执行run()方法;

  • 代表是抢到了CPU资源,开始执行run()方法;
  • // 创建线程
    Thread t = new Thread(){ // 运行状态  -->>  抢到了CPU资源// 执行run方法public void run(){}
    };

(4)死亡状态:run()方法运行结束;

(5)阻塞状态:调用sleep()方法的时候;

  • sleep()方法的括号中的参数是以毫秒为单位的;
  • Thread.sleep(1);

7.实现线程有三种方法:

(1)实现接口:要将一个普通类变成线程类需要实现Runnable接口;

  • 使用实现接口的方法,那么这个普通类就变成了一个可以被线程类操作的类,然后使用线程类去完成一些操作;
  • 在实现接口后,这个普通类只是可以被线程类操作了,并没有变成一个线程类;
  • public class Thread2 implements Runnable {@Overridepublic void run() {}
    }

(2)继承类:要将一个普通类变成线程类需要继承Thread类并重写run()方法;

  • 使用继承类的方法,那么将一个普通类变成线程类之后,这个普通类就是一个线程类,它和线程类的用法没有区别;
  • public class Thread1 extends Thread {@Overridepublic void run() {}
    }

(3)线程池:线程池用来管理线程,下面会介绍;

8.实现线程的方法中,实现接口和继承类这个两个方法推荐使用实现接口的方法,因为java只能单继承,但是可以多实现。

二、线程同步

1.同步代码块:

(1)格式:synchronized(锁旗标){};

(2)含义:在同步块中的代码,同时只能有一个线程去执行;

public class Ticket implements Runnable {int number = 100;@Overridepublic void run() {while (true) {// 同步代码块synchronized ("") {if (number > 0) {// 还有票try {Thread.sleep(50);// 模拟卖票时间} catch (InterruptedException e) {e.printStackTrace();}System.out.println("同步代码块:" + Thread.currentThread().getName() + "卖了" + number-- + "张票");}}}}
}

(3)注意:synchronized括号中可以放字符串或对象,只有括号中是同一个字符串或同一个对象时才可以达到同步的效果,一般括号中放的是空字符串或者this对象;

2.同步方法:

(1)同步方法锁住的是当前对象;

public class Ticket implements Runnable {int number = 100;@Overridepublic void run() {while(true){sale();}}// 卖票的方法// 同步方法 -->>  锁住的是当前对象public synchronized void sale(){//thisif (number > 0) {// 还有票try {Thread.sleep(50);// 模拟卖票时间} catch (InterruptedException e) {e.printStackTrace();}System.out.println("同步方法:" + Thread.currentThread().getName() + "卖了" + number-- + "张票");}}
}

3.总结:

(1)每个对象身上都有一把锁,它的状态不是1就是0;

(2)我们锁住的东西称为锁旗标,如:this,空字符串都可以称为锁旗标;

(3)想要线程同步,只需要让多个线程拥有同一个锁旗标;

三、死锁

1.互相拿着对方的锁,但是需要对方来解锁;

public class Ticket implements Runnable {int number = 100;String s = "a";@Overridepublic void run() {while (true) {if ("a".equals(s)) {while (true) {sale();}} else {while (true) {synchronized ("") {if (number > 0) {// 还有票try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("同步代码块:" + Thread.currentThread().getName() + "卖了" + number-- + "张票");// 会造成死锁synchronized (this){}}}}}}}//卖票的方法//同步方法 -->>  锁住的是当前对象public synchronized void sale(){//thisif (number > 0) {try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("同步方法:" + Thread.currentThread().getName() + "卖了" + number-- + "张票");//会造成死锁synchronized (""){}}}
}public class Demo {public static void main(String[] args) throws InterruptedException {Ticket ticket = new Ticket();Thread t1 = new Thread(ticket);Thread t2 = new Thread(ticket);Thread t3 = new Thread(ticket);Thread t4 = new Thread(ticket);t1.start();t2.start();Thread.sleep(1);t.s = "b";t3.start();t4.start();}
}

四、线程的方法

1.挂起:线程对象名.suspend();

(1)此方法已过时,作为了解。

Thread t1 = new Thread();
t1.suspend();

2.取消挂起:线程对象名.resume();

(1)此方法已过时,作为了解。

Thread t1 = new Thread();
t1.resume();

3.礼让:线程对象名.yield();

(1)线程在抢到CPU的资源后,会让出来,但是在让出来后,还会和其他线程去抢CPU的资源(只让一次)。

Thread t1 = new Thread();
t1.yield();

4.加入:线程对象名.join();

(1)在一个线程执行的途中,可以加入另一个正在执行的线程,然后正在执行的线程就会停下来让新加入的线程先执行完,等到新加入的线程执行完毕后,原来的线程才会继续执行。

public class ThreadB extends Thread {@Overridepublic void run() {for (int i = 0; i < 100; i++) {try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "===" + i);}}public static void main(String[] args) {ThreadB b = new ThreadB();ThreadA a = new ThreadA(b);a.start();b.start();}
}public class ThreadA extends Thread {private ThreadB b;public ThreadA(ThreadB b){this.b = b;}@Overridepublic void run() {for (int i = 0; i < 100; i++) {try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}if(i == 10){try {//当A线程执行到10的时候,将B线程加入进来b.join();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() + "===" + i);}}
}

5.休息/等待:线程对象名.wait()或者this.wait();

(1)让一个正在执行的线程停止执行,进入休息状态。

6.唤醒当前线程:线程对象名.notify()或者this.notify();

(1)如果当前线程是休息状态,则唤醒当前线程。

7.唤醒所有线程:线程对象名.notifyAll()或者this.notifyAll();

(1)唤醒全部已经进入休息状态的线程。

8.使用生产者和消费者案例来解释wait和notifyAll方法,notify方法和notifyAll方法使用方法差不多,一个是唤醒当前线程,一个是唤醒全部线程;

/**
* 产品
*/
public class Product {int number = 0;int max = 100;public synchronized void add(){if(number < max){number++;System.out.println("生产了一件产品,现在的数量是:" + number);//当生产了一件产品的时候,唤醒全部线程开始消费this.notifyAll();}else {try {//当现有的产品数量大于等于最大的库存容量时,休息一下this.wait();} catch (InterruptedException e) {e.printStackTrace();}}}public synchronized void remove(){if(number > 0){number--;System.out.println("消费了一件产品,现在的数量是:" + number);//当消费了一件产品的时候,唤醒全部线程开始生产this.notifyAll();}else {try {//当没有产品时,先休息一下this.wait();} catch (InterruptedException e) {e.printStackTrace();}}}
}/*** 生产者*/
public class Produce extends Thread {Product pro = null;public Produce(Product pro){this.pro = pro;}@Overridepublic void run() {while (true) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}pro.add();}}
}/*** 消费者*/
public class Saler extends Thread {Product pro = null;public Saler(Product pro){this.pro = pro;}@Overridepublic void run() {while (true) {try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}pro.remove();}}
}public class Test {public static void main(String[] args) {Product pro = new Product();new Produce(pro).start();new Produce(pro).start();new Saler(pro).start();}
}

五、线程的流程图

六、线程池

1.线程池的概念:

(1)将多个线程放入到一个类似池子的空间中,然后如果要使用某个线程,就从线程池中取出使用,使用完成后再将线程放入到线程池中。

2.Java通过Executors提供了四种线程池:

(1)newCacheThreadPool:创建一个可缓存的线程池,如果线程池长度超过处理需要,可以灵活的回收空闲线程,若没有可回收的线程,则会新创建一个;

  • 可缓存的线程池为无限大,当执行第二个任务时第一个任务已经完成,第二个任务会复用第一个任务的线程,而不用每次都新创建一个线程;
  • 使用execute方法来执行线程;
  • // 可缓存的线程池,线程池为无限大,当执行第二个任务时如果第一个任务已经完成,会复用第一个任务的线程,而不用每次新建线程
    ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    for (int i = 0; i < 10; i++) {final int index = i;try {Thread.sleep(1000 * index);} catch (InterruptedException e) {e.printStackTrace();}cachedThreadPool.execute(new Runnable() {@Overridepublic void run() {System.out.println(index);}});
    }

(2)newFixedThreadPool:创建一个固定长度的线程池,可控制线程的最大并发数,如果超出,线程会在队列中等待;

  • 创建一个固定长度的线程池,这个线程池的长度是固定的,每次执行线程的时候,会按最大长度去限制执要行线程的个数;
    • 如果最大长度为3,则一次性最多只能执行3个线程,如果为5,则一次性最多只能执行5个线程;
  • 使用execute方法来执行线程;
  • // 创建固定长度的线程池,因为线程池的大小为3,每个任务休息3秒之后,会打印三个数字,所以是每三秒打印三个数字
    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);for (int i = 0; i < 10; i++) {final int index = i;fixedThreadPool.execute(new Runnable() {@Overridepublic void run() {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(index);}});}

(3)newScheduledThreadPool:创建一个定长的线程池,支持定时及周期任务;

  • 使用schedule方法来定时执行线程,就是让一个线程在多长时间后执行;
  • // 创建一个定长的线程池,支持定时及周期任务执行
    // 定时:表示延迟3秒后执行一次
    ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
    scheduledThreadPool.schedule(new Runnable() {@Overridepublic void run() {System.out.println("delay 3 seconds");}
    },3, TimeUnit.SECONDS);
  • 使用scheduleAtFixedRate方法来定时及定周期执行线程,就是让一个线程,在多长时间后执行,每多长时间执行一次;
  • // 创建一个定长的线程池,支持定时及周期任务执行
    // 延迟1秒之后,每隔3秒执行一次
    ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
    scheduledThreadPool.scheduleAtFixedRate(new Runnable(){@Overridepublic void run() {System.out.println("delay 1 seconds, and execute every 3 seconds");}
    },1,3,TimeUnit.SECONDS);

(4)newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,可以保证所有的任务按照指定的顺序来执行;

  • 创建一个单线程化的线程池,也就是说这个线程池只会创建一个线程,然后使用这个线程去执行各个任务,相当于顺序执行各个任务;
  • 使用execute方法来执行线程;
  • // 创建一个单一的线程池,结果按照执行顺序依次输出,相当于顺序执行各个任务
    ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
    for (int i = 0; i < 10; i++) {final int index = i;singleThreadExecutor.execute(new Runnable() {@Overridepublic void run() {System.out.println(index);try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}});
    }

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词