java中的多线程

一.什么是进程?什么是线程?

进程是指一个内存中运行的应用程序,一个进程中有多个线程。 比如在windows系统里,一个运行的xx.exe就是一个进程。
java程序的进程里有几个线程:主线程,垃圾回收线程(后台线程)。
线程是指进程中的一个控制单元(执行任务),一个进程中可以运行多个线程,多个线程可以共享数据。
多进程:操作系统中同时运行的多个程序。
多线程:在一个进程总同时运行的多个任务。

二.进程和线程的比较

1. 进程A和进程B有独立的进程空间,他们的的内存独立不共享,进程中的数据存放空间(堆空间和栈空间)是独立的。
2. 线程A和线程B的堆空间和方法区内存共享,但是栈空间独立,一个线程一个栈

三.创建线程的方式

Java语言提主要供了两种实现线程的方式:

  • 继承Thread类创建线程类
  • 实现Runnable接口创建线程类

1.继承Thread类创建线程类

(现在不推荐这种方法,应该面向接口编程,解耦合)
实现步骤:

  • ①定义类继承Thread类,并重写Thread类的run()方法,
  • ②创建Thread子类的实例,即创建了线程对象。
  • ③调用线程对象的start()方法来启动该线程。

示例代码:
注意:代码中start()方法的作用


/**
 * 2020/12/4,zhanglijun
 */
public class ThreadTest01 {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();

        //start()方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。
        //这段代码的作用只是为了开启一个新的栈空间,,只要新的栈空间开出来,start()方法就结束了。线程就自动启动成功了。
        //启动成功后的线程会自动调用run方法,并且run方法在分支栈的底部(压栈)。
        //run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的
        myThread.start();
        for(int i=0;i<50;i++){
            System.out.println("主线程"+i);
        }
        
    }
}

class MyThread extends  Thread{

    @Override
    public void run() {
        for(int i=0;i<50;i++){
            System.out.println("分支线程启动"+i);
        }
        
    }
}

2.实现Runnable接口创建线程类

实现步骤:

  1. 定义一个类MyRunnable实现Runnable接口
  2. 在MyRunnable类中重写run()方法
  3. 创建MyRunnable类的对象
  4. 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
  5. 启动线程

示例代码:
package com.多线程.Thread;

/**
 * 2020/12/4,zhanglijun
 */
public class ThreadTest03 {
    public static void main(String[] args) {

      //  MyRunnable myRunnable = new MyRunnable();
      // Thread thread = new Thread(myRunnable);
		Thread thread = new Thread(new MyRunnable());//合并代码
        thread.start();
        
        for(int i=0;i<100;i++){
            System.out.println("主线程执行"+i);
        }
    }
}

class MyRunnable implements Runnable{

    @Override
    public void run() {
        for(int i=0;i<100;i++){
            System.out.println("分支线程执行"+i);
        }
    }
}

顺便复习一下匿名内部类,也可以使用匿名内部类的方式来写。

public class ThreadTest04 {
    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("分支线程执行---"+i);
                }
            }
        });
        //启动线程
        thread.start();

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

        }
    }
}

3.两种进程创建方式的比较

A extend Thread:
简单
不能继承其他类了(java单继承)
同份资源不能共享
A implements Runnable:(推荐)
多个线程共享一个资源目标,适合多线程处理同一份资源。
该类还可以继承其他类,也可以实现其他接口。

小问题:为什么要覆盖run方法呢?
Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码。该代码功能就是run方法。也就是说Thread类中的run方法,用于存储线程要运行的代码。

*四.线程的生命周期

1. 新建状态(New):刚new出来的线程对象就处于这种状态。
2. 就绪状态(Runnable):就绪状态的线程又叫做可运行状态,表示当前线程具有抢夺CPU时间片的权利(CPU时间片就是执行权)。当一个线程抢夺到CPU时间片之后,就开始执行run方法,run方法开始执行标志着线程进入运行状态。
3. 运行状态(Running):run方法开始执行标志着线程进入运行状态,当之前占有的CPU时间片用完之后,会重新回到就绪状态,继续抢夺时间片,当再次抢到时间片之后,会重新进入run方法接着上一次的代码继续往下执行。
4. 阻塞状态(Blocked):当一个线程遇到阻塞事件,例如接受用户输入,或者sleep方法等,此时线程会进入阻塞状态,阻塞状态的线程会放弃之前占有的CPU时间片。
5. 死亡状态(Dead):已退出的线程,线程执行完了或者因异常退出了run()方法,处于这种状态。该线程结束生命周期。

在这里插入图片描述
在这里插入图片描述

五.线程控制(线程调度


java中提供了哪些方法是和线程调度有关系的呢?

  • void setPriority(int newPriority) 设置线程的优先级
  • int getPriority() 获取线程优先级
Thread4 t1 = new Thread4("t1");
Thread4 t2 = new Thread4("t2");
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.MIN_PRIORITY);

Java线程有优先级,优先级用整数表示,取值范围是1~10,Thread类有以下三个静态常量:
static int MAX_PRIORITY
线程可以具有的最高优先级,取值为10。
static int MIN_PRIORITY
线程可以具有的最低优先级,取值为1。
static int NORM_PRIORITY
分配给线程的默认优先级,取值为5。

主线程的默认优先级 Thread.NORM_PRIORITY。
线程的优先级有继承关系,比如A线程中创建了B线程,那么B将和A具有相同的优先级。优先级比较高的获取CPU时间片可能会多一些。(但也不完全是,大概率是多的。)

  • static void yield() 让位方法

    暂停当前正在执行的线程对象,并执行其他线程
    yield()方法不是阻塞方法。让当前线程让位,让给其它线程使用。
    yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”。
    注意:在回到就绪之后,有可能还会再次抢到。

  • void join()

    等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态。这里需要理解的就是该线程是指的主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。

class MyThread1 extends Thread {
				public void doSome(){
					MyThread2 t = new MyThread2();
					t.join(); // 当前线程进入阻塞,t线程执行,直到t线程结束。当前线程才可以继续。
				}
			}

	class MyThread2 extends Thread{
				
	}
  • Thread.sleep(long millis)

    线程睡眠方法,使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。sleep()平台移植性好。

  • Object类中的wait()

    线程等待方法,导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是Object类中的方法,行为等价于调用 wait(0) 一样。

  • Object类中的notify()

    线程唤醒方法,唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。 直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。类似的方法还有一个notifyAll(),唤醒在此对象监视器上等待的所有线程。

五.多线程安全

Java中有三大变量?【重要的内容。】

  • 实例变量:在堆中。

  • 静态变量:在方法区。

  • 局部变量:在栈中。

    以上三大变量中:
    局部变量永远都不会存在线程安全问题。
    因为局部变量不共享。(一个线程一个栈。)
    局部变量在栈中。所以局部变量永远都不会共享。

    实例变量在堆中,堆只有1个。
    静态变量在方法区中,方法区只有1个。
    堆和方法区都是多线程共享的,所以可能存在线程安全问题。

    局部变量+常量:不会有线程安全问题。
    成员变量:可能会有线程安全问题。

如果使用局部变量的话:
建议使用:StringBuilder。
因为局部变量不存在线程安全问题。选择StringBuilder。
StringBuffer效率比较低。

ArrayList是非线程安全的。
Vector是线程安全的。
HashMap HashSet是非线程安全的。
Hashtable是线程安全的。

导致安全问题的出现的原因:
多个线程访问出现延迟。
线程随机性。
注:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。
我们可以通过 Thread.sleep(long time)方法来简单模拟延迟情况。

我的总结:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还
没有执行完,另一个线程参与进来执行。导致共享数据的错误。

解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不
可以参与执行。

六.多线程安全解决方法(如何做到线程同步)

三种方法:
同步代码块:
synchronized(obj)
{
//obj 表示同步监视器,是同一个同步对象
/**…
TODO SOMETHING
*/
}
同步方法
格式:
在方法上加上 synchronized 修饰符即可。(一般不直接在 run 方法上加!)

### 回答1: Java 多线程可以通过创建 Thread 类的实例来实现。具体步骤如下: 1. 创建一个实现 Runnable 接口的类,该类实现 run() 方法。 ```java public class MyRunnable implements Runnable { public void run() { // 执行线程的操作 } } ``` 2. 创建一个 Thread 实例,并将实现了 Runnable 接口的类作为构造函数的参数。 ```java MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); ``` 3. 启动线程。 ```java thread.start(); ``` 此外,还可以通过继承 Thread 类来实现多线程。具体步骤如下: 1. 创建一个继承自 Thread 类的类,重写 run() 方法。 ```java public class MyThread extends Thread { public void run() { // 执行线程的操作 } } ``` 2. 创建 MyThread 的实例。 ```java MyThread myThread = new MyThread(); ``` 3. 启动线程。 ```java myThread.start(); ``` 在多线程的使用,需要注意线程安全的问题,比如共享变量的访问、同步操作等。可以使用 synchronized 关键字或者 Lock 接口来保证线程安全。 ### 回答2: 在Java多线程的使用是指程序同时运行多个线程,每个线程执行自己的任务。Java多线程的使用可以带来以下几点好处: 1. 提高程序的效率:多线程可以对多个任务进行并发处理,提高程序的运行效率。例如,可以将网络请求和UI界面分别放在两个不同的线程,这样即使网络请求比较耗时,UI界面也能进行响应,不会出现界面假死的情况。 2. 充分利用系统资源:多线程可以充分利用系统的处理器资源,提高系统的利用率。在多核处理器上运行多个线程,可以让每个核心都得到充分利用,提高系统的整体性能。 3. 实现异步编程:多线程可以实现异步编程,即一个线程执行后续操作,不需要等待另一个线程的完成。这样可以提高程序的响应速度。例如,可以使用多线程来进行文件下载,下载过程可以同时进行其他操作。 4. 处理复杂的并发情况:在一些需要处理多个并发操作的场景多线程可以提供更好的解决方案。例如,在并发访问共享资源的情况下,使用线程锁可以保证对共享资源的安全访问,避免数据冲突和一致性问题。 Java使用多线程可以通过创建Thread类的实例或者实现Runnable接口来实现。通过继承Thread类来创建线程,需要重写run方法,在run方法定义线程要执行的任务。通过实现Runnable接口来创建线程,需要实现run方法,并将实现了Runnable接口的对象作为参数传递给Thread类的构造方法。 总之,Java多线程的使用使得程序可以同时执行多个任务,提高了程序的效率和用户体验,并且能够处理复杂的并发情况。但需要注意多线程的安全性和线程之间的协作,避免出现数据冲突和一致性问题。 ### 回答3: Java多线程的使用是指在一个程序同时执行多个任务或者同时处理多个请求。多线程可以提高程序的并发性和响应性,可以将耗时的操作和任务分配给不同的线程来执行,从而提高程序的运行效率。 在Java多线程的使用主要依靠Thread类或者实现Runnable接口来创建线程。可以通过继承Thread类创建一个线程类,并重写run方法,在run方法定义需要执行的任务;也可以实现Runnable接口,创建一个Runnable对象,然后将该对象作为参数传递给Thread类的构造方法,创建一个线程对象。 使用多线程的好处是可以充分利用处理器的多核特性,同时进行多个任务,提高程序的运行效率。多线程还可以提高程序的响应性,当程序有耗时的操作时,可以将其放在一个独立的线程执行,防止主线程被阻塞,提高用户体验。 然而,多线程的使用也存在一些问题。首先是线程安全问题,多个线程同时访问共享资源可能导致数据不一致或者数据损坏。为解决这个问题,可以使用同步机制,如synchronized关键字或Lock接口,保证在同一时间只有一个线程能够访问共享资源。其次,多线程的创建和销毁会消耗系统资源,如果线程数量过多,可能会影响系统性能。因此,在使用多线程时应该合理控制线程的数量。另外,线程之间的协调和通信也是一个值得关注的问题,可以使用wait、notify、join等方法来实现线程间的协作。 总之,Java多线程的使用可以提高程序的并发性和响应性,但需要注意线程安全、资源消耗以及线程协调和通信等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值