关于Java多线程相关知识总结

目录

一、概念

1.区分程序、进程、线程:

2.区分并行与并发:

二、线程的基本操作:

1、线程的创建

2、线程类的常用方法

3、线程调度与线程通信

4、线程的生命周期

5、线程安全与线程同步

6、锁

三、其它重点:

(1)sleep 和 yield 的区别

(2)sleep 和 wait 的区别

(3)synchronized 和 Lock 有什么区别


一、概念

1.区分程序、进程、线程:

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

       进程:程序的一次执行过程。当一个程序被运行,即开启一个进程。

       线程:即细化后的进程,可以理解为程序完成任务的一个步骤,是程序内部执行的一条路径。同时线程也是CPU调度的最小单位。一个进程可以有多个线程,若该进程同时执行了多个线程,即为多线程

2.区分并行并发

        并行多个CPU同时执行多个任务。

        并发一个CPU同时执行多个任务。


二、线程的基本操作:

1、线程的创建

        Java 对于多线程的创建提供了 Thread 类和 Runable Callable 接口,我们要去创建一个线程,那么我们可以选择去继承Thread 类或者去实现 Runable Callable 接口。
        继承Thread类:如下为定义一个MyThread1类去继承Thread类,然后在main方法中创建MyThread1类的对象,从而实现线程的创建:
public class Threadcreat {
    public static void main(String[] args) {
        MyThread1 myThread1=new MyThread1();
        //myThread1.start(); //用于开启一个一个线程,并且start方法只能调用一次
        myThread1.run();
        /*
            面试题:run方法和start方法的区别
         */
        for (int i=0;i<10;i++){
            System.out.println(Thread.currentThread().getName() + "打印---->" + i);
        }
    }
}
class MyThread1 extends Thread{
    //run方法里面存放的是线程任务
    @Override
    public void run(){
        for (int i=0;i<10;i++){
            //Thread类提供了一个静态方法名字叫做getName()用于获取线程名称
            System.out.println(Thread.currentThread().getName() + "打印----》" + i);
        }
    }
}

        实现Runnable接口:

public class RunnableTest {
    public static void main(String[] args) {
        Mythread01 mythread01=new Mythread01();
        Thread thread=new Thread(mythread01,"线程1");
        Thread thread1=new Thread(mythread01,"线程2");
        thread.start();
        thread1.start();
    }
}

class Mythread01 implements Runnable{
    @Override
    public void run() {
        for (int i=0;i<10;i++){
            /*
                thread类的currentthread()方法可以用于返回一个当前线程类的对象
            */
            System.out.println(Thread.currentThread().getName() + "----->" + i);
        }
    }
}

2、线程类的常用方法

常用方法以及其对应的功能:
        start():启动当前线程 / 调用线程中的 run 方法;
        run(): 通常需要重写 Thread 类中的此方法,将创建的线程要执行的操作声明在此方法中;
        currentThread(): 静态方法,返回执行当前代码的线程;
        getName(): 获取当前线程的名字;
        setName(): 设置当前线程的名字;
        yield(): 主动释放当前线程的执行权;
        join(): 在线程中插入执行另一个线程,该线程被阻塞,直到插入执行的线程完全执行完毕以后,该线程才继续执行下去;
         stop(): 过时方法。当执行此方法时,强制结束当前线程;
        sleep long millitime ):线程休眠一段时间;
        isAlive ():判断当前线程是否存活;

3、线程调度与线程通信

        线程调度:由于计算机通常只有一个 cpu,在任意时刻只能执行一条机器指令。因此,每个线程只有获得 cpu 的使用权才能执行指令。所谓线程并发运行,其实是各个线程轮流获取cpu的使用权,分别执行各自的任务。在线程池中会有多个线程处于就绪状态等待cpu。JAVA虚拟机的任务之一就是负责线程调度-------->按照特定机制为多个等待中的线程分配cpu

        线程通信:在需要让多个线程按照规则去协同执行任务时,需要用到线程通信。如定义两个线程交替打印数字。

        线程通信通常可以利用 wait()notify()、notifyAll()等方法来实现。其中wait()方法:让当前进程释放对象锁并进入阻塞状态;notify()方法:用于唤醒一个正在等待相应对象锁的线程,使其进入就绪队列,以便在当前线程释放锁后竞争锁, 进而得到 CPU 的执行;notifyAll()方法:用于唤醒所有正在等待相应对象锁的线程。

:wait、notifynotifyall 这三个方法都不是 Thread 类中所声明的方法,而是 Object 类中声明的方法。因此在使用的时需要用对应同步块或者方法的锁对象去调用。

class T1 extends Thread{
    private static int num = 1;
    private static Object object = new Object();
    T1(String name){
        super(name);
    }
    @Override
    public void run() {
        while (true){
            synchronized (object){
                object.notify();
                if (num <= 20){
                    System.out.println(getName()+"--->"+num);
                    num++;
                    try {
                        object.wait();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }else {
                    break;
                }
            }
        }
    }
}

4、线程的生命周期

        与对象相同,线程也有生命周期,一个线程的生命周期包含:新建、就绪、运行、阻塞、死亡五个状态。

        新建:当创建声明一个Thread类或其子类后,新生的线程对象就处于新建状态;

        就绪当处于新建状态的线程对象调用 start 方法以后,它就进入线程队列等待cpu时间片,此时线程对象已经具备了运行条件,只是没有得到 cpu 资源;

        运行处于就绪状态的线程对象获得 cpu 资源以后就可以进入运行状态,run 方法定义了线程的任务;
        阻塞:在某些特殊情况下,被人为挂起或执行输入输出操作的时候,线程对象让出自己的 cpu 并终止任务进入阻塞状态;
        死亡:线程对象完成了它的线程任务或者被强制退出、发生异常等,线程对象就进入死亡状态。

5、线程安全与线程同步

        线程安全:指多个线程去操作共享数据的时候出现了共享数据的冲突,我们称此时线程是不安全的。如:创建三个线程售卖火车票问题。在线程一对共享数据进行操作时,线程二拿到的并不是线程一操作后的数据,因此该数据前后会发生冲突。这一问题通常通过线程同步或线程通信去解决。

        线程同步:指当一个线程在操作数据的时候,其它线程不能参与进来,只能当前线程某个操作完成以后才可以让其它线程参与进来。java中提供了一些关键字用于实现线程同步。如Synchronized、lock等。

        用Synchronized去同步代码块或方法:通过 Synchronized 关键字将需要待同步的代码给放到指定代码块里面,并使用同步锁去控制同步。同步代码块中的同步监视器是用于控制线程同步,同步监视器也就是锁,任何一个类的对象都是锁,需要注意的是多个线程要使用同一把锁否则将不能达到同步的效果。

        此方法主要用到了线程的三大特性:原子性、有序性、可见性。

Synchronized(同步监视器){
        代码
}
public class Model18 {
    public static void main(String[] args) {
        WindowSal sal = new WindowSal();
        Thread thread1 = new Thread(sal,"窗口一");
        Thread thread2 = new Thread(sal,"窗口二");
        Thread thread3 = new Thread(sal,"窗口三");
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
class WindowSal implements Runnable{
    private int ticket = 1000;
    @Override
    public void run() {
        while (true){
            synchronized (this){
                if (ticket > 0){

                    System.out.println(Thread.currentThread().getName()+"正在售卖第
                            "+(100-ticket+1)+"张票"+"还剩下:"+(ticket-1)+"张票。");
                            ticket--;
                }else {
                    break;
                }
            }
        }
    }
}

6、锁

        :上述关键字Synchronized中的参数,也就是Synchronized(同步监视器)中的“同步监视器”就是锁。它可以是一个类的对象,也可以是this指针。用于控制线程同步,即在一个对象执行完这部分代码之前,其它线程对线不能访问这部分代码,即形成了锁。

        死锁多个线程互相等待对方持有的锁,而在得到对方的锁之前都不会释放自己的锁,即为死锁。

        产生条件:

(1)互斥条件(Mutual exclusion):资源不能被共享,只能由一个进程使用。

(2)请求与保持条件(Hold and wait):已经得到资源的进程可以再次申请新的资源。

(3)非剥夺条件(No pre-emption):已经分配的资源不能从相应的进程中被强制地剥夺。

(4) 循环等待条件( Circular wait ):系统中若干进程组成环路,改环路中每个进程都在等待相邻进程正占用的资源。
        死锁预防尽量使用 tryLock(long timeout, TimeUnit unit) 的方法 (ReentrantLock 、
ReentrantReadWriteLock) ,设置超时时间,超时可以退出防止死锁。尽量使用 Java. util. concurrent 并发类代替自己手写锁。尽量降低锁的使用粒度,尽量不要几个功能用同一把锁。
尽量减少同步的代码块。
死锁案例:

class T1 extends Thread{
    private static Object obj1 = new Object();
    private static Object obj2 = new Object();
    private int flag;
    T1(String name,int flag){
        super(name);
        this.flag = flag;
    }
    @Override
    public void run() {
        if (flag == 1){
            synchronized (obj1) {
                System.out.println(getName()+"已持锁一");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(getName()+"正在尝试获取锁二");
                synchronized (obj2) {
                    System.out.println(getName()+"已持锁二");
                }
            }
        }else {
            synchronized (obj2) {
                System.out.println(getName()+"已持锁二");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(getName()+"正在尝试获取锁一");
                synchronized (obj1) {
                    System.out.println(getName()+"已持锁一");
                }
            }
        }
    }
}
public class Model2 {
    public static void main(String[] args) {
        T1 t1 = new T1("线程一",1);
        T1 t2 = new T1("线程二",2);
        t2.start();
        t1.start();
    }
}

        死锁解决:资源回收、改变加锁顺序、开放调用、定时锁等。

        Lock锁:Java提供的一个接口,使用它的实现类可以达到与Synchronized相同的同步代码块的效果。

        Lock接口中用于加锁、释放锁等操作的方法

(1)void lock():获得锁。如果锁不可用,则当前线程将被禁用以进行线程调度,并处于休眠状态,直到获取锁。

(2)void lockInterruptibly() :获取锁,如果可用并立即返回。如果锁不可用,那么当前线程将被禁用以进行线程调度,并且处于休眠状态,和 lock() 方法不同的是在锁的获取中可以中断当前线程。
(3) boolean tryLock() :只有在调用时才可以获得锁。如果可用,则获取锁,并立即返回值为 true ;如果锁不可用,则此方法将立即返回值为 false
(4) boolean tryLock(long time, TimeUnit unit) :超时获取锁,当前线程在一下三种情况下会返回:
        a. 当前线程在超时时间内获得了锁;
        b.当前线程在超时时间内被中断;
        c.超时时间结束,返回 false
(5) void unlock() :释放锁。

public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long var1, TimeUnit var3) throws
            InterruptedException;
    void unlock();
    Condition newCondition();
}

在操作 Lock 锁需要注意的是 Lock 锁需要手动释放,即使发生了异常,锁也不会被释放,需要我们手动释放,对此我们在使用 Lock 锁时就需要将代码放在放在 try-finally 异常处理机制中。


三、其它重点:

(1)sleep 和 yield 的区别

        1)状态区别
        调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞);
        调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程。
        2)调度区别
        调用 sleep 会让当前线程进入阻塞状态分不到 cpu 时间片;
        调用 yield 方法只是会让当前线程让出 cpu 的使用权,但是任务调度器仍然可能分配给该线程时间片,从宏观上只是该线程被分配 cpu 的概率变低了。

(2)sleep 和 wait 的区别

sleep() Thread 类中的方法,而 wait() 则是 Object 类中的方法。
最主要是 sleep 方法没有释放锁,而 wait 方法释放了锁,使得其他线程可以使用同步控制块或者方法。
使用范围: wait notify notifyAll 只能在同步控制方法或者同步控制块里面使用,一旦一个对象调用了 wait 方法,必须要采用 notify() notifyAll() 方法唤醒该进程;而 sleep 可以在任何地方使用。
相同点: sleep 必须捕获异常, wait notify notifyAll 同样需要捕获异常。
sleep() 方法导致了程序暂停(线程进入睡眠状态),但是他的监控状态依然保持着,当指定的时间到了又会自动恢复到 可运行状态。在调用 sleep() 方法的过程中,线程不会释放对象锁。
wait() 方法会导致线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用 notify() 方法后本线程才进入对象锁定池准备获取对象锁进入可运行状态。即 wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的notify() 或者 notifyAll() 方法。

(3)synchronized 和 Lock 有什么区别

  • synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。
  • synchronized 不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁;而 lock 需要自己加锁和释放锁,如果使用不当没有 unLock()去释放锁就会造成死锁。
  • 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,我可以为您提供一些关于Java多线程的基础知识和编写代码的总结Java多线程基础知识: 1. 线程的创建和启动:Java可以通过实现Runnable接口或继承Thread类来创建和启动线程。 2. 线程的状态:Java线程可以有6种状态,包括新建、就绪、运行、阻塞、等待和终止状态。 3. 线程的控制:Java提供了一些方法来控制线程,比如sleep()、yield()、join()等。 4. 多线程同步:Java提供了synchronized关键字和Lock接口来实现线程同步,避免多个线程同时访问共享资源。 5. 线程间通信:Java提供了wait()、notify()和notifyAll()方法来实现线程间的通信。 Java多线程编写代码总结: 1. 创建线程: ``` Thread thread = new Thread(new Runnable(){ public void run() { // 线程执行的代码 } }); ``` 2. 启动线程: ``` thread.start(); ``` 3. 线程控制: ``` // 线程休眠 Thread.sleep(1000); // 线程礼让 Thread.yield(); // 等待线程执行完毕 thread.join(); ``` 4. 多线程同步: ``` // 方法一:使用synchronized关键字同步代码块 synchronized (obj) { // 需要同步的代码块 } // 方法二:使用synchronized关键字同步方法 public synchronized void methodName() { // 需要同步的代码块 } // 方法三:使用Lock接口同步 Lock lock = new ReentrantLock(); lock.lock(); try { // 需要同步的代码块 } finally { lock.unlock(); } ``` 5. 线程间通信: ``` // 线程等待 synchronized (obj) { while (condition) { obj.wait(); } } // 线程唤醒 synchronized (obj) { obj.notify(); // obj.notifyAll(); } ``` 希望以上内容对您有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值