多线程

多线程

概念

  • 同步

    先做1,1做完了再做2

  • 异步

    两件事可以一起做

    • 并行

      一个cpu核心上只运行一件事

    • 并发

      一个cpu核心上运行多件事,利用cpu时间切片技术

      • 多进程

        进程与进程之间,内存不共享 运行一个应用程序,就是启动了一个进程,这个刚刚启动的进程,叫做主进程 由主进程创建的进程,叫做子进程

      • 多线程

        线程与线程之间,内存共享(方法区和堆区共享) 只要有进程,每个进程里面,都认有一个线程,此线程叫做主线程 由主线程创建的线程,叫做子线程

      • 多协程

快速入门

  • Thread.sleep()

    在当前线程睡眠,单位为毫秒

  • Thread::start

    启动线程

  • Thread::run

    线程在执行时,要运行的代码

  • SecondThread.java

package com.futureweaver.multithread;

public class SecondThread extends Thread {
@Override
public void run() {
try {
while (true) {
System.out.println("第二个线程的代码");
Thread.sleep(1500);
}
} catch (InterruptedException e) {
System.out.println("第二个线程退出了...");
}
}
}

  • ThreadPractice.java
package com.futureweaver.multithread;

public class ThreadPractice {
public static void main(String[] args) throws InterruptedException {
Thread thread = new SecondThread();
thread.start();

    while (true) {
        System.out.println("主线程里面的代码");

        // InterruptedException异常为别人想要关闭此线程
        // 在运行此代码时,会收到一个异常
        // 抓此异常,目的是为了释放掉自己所占用的资源
        // 抛此异常,目的是不做任何处理,直接退出程序

        // 当前线程睡眠多少毫秒
        Thread.sleep(2000);
    }
}

}

使用时的注意事项

每创建一个线程,都要有名字

Thread结合Runnable的用法

在使用线程时,应注意关注点分离,Thread只用于处理创建线程、在线程里执行过程等等的功能 Runnable只关注要干什么 关注点分离的好处在于,无需重度重写父类方法,就可以直接用

  • SecondRunnable.java
package com.futureweaver.multithread;

public class SecondRunnable implements Runnable {
@Override
public void run() {
try {
while (true) {
System.out.println("这是一个Thread结合Runnable要执行的代码");
Thread.sleep(1500);
}
} catch (InterruptedException e) {
System.out.println("线程退出...");
}
}
}

  • Thread2Practice.java
package com.futureweaver.multithread;

// 多线程的第二种用法
// Thread结合Runnable使用

public class Thread2Practice {
public static void main(String[] args) throws InterruptedException {
SecondRunnable secondRunnable = new SecondRunnable();
Thread thread = new Thread(secondRunnable, "thread and runnable ...");
thread.start();

    while (true) {
        System.out.println("主线程代码");

        Thread.sleep(2000);
    }
}

}

Runnable的匿名内部类的用法

  • Thread2Practice.java
package com.futureweaver.multithread;

// 多线程的第二种用法
// Thread结合Runnable使用

public class Thread2Practice {
public static void main(String[] args) throws InterruptedException {
Runnable secondRunnable = new Runnable() {
@Override
public void run() {
try {
while (true) {
System.out.println("second runnable里的过程");
Thread.sleep(1500);
}
} catch (InterruptedException e) {
System.out.println("线程退出了");
}
}
};
Thread thread = new Thread(secondRunnable, "thread and runnable ...");
thread.start();

    while (true) {
        System.out.println("主线程代码");

        Thread.sleep(2000);
    }
}

}

Runnable的lambda表达式写法【重点】

  • Thread2Practice.java
package com.futureweaver.multithread;

// 多线程的第二种用法
// Thread结合Runnable使用

public class Thread2Practice {
public static void main(String[] args) throws InterruptedException {
// 匿名内部类,缺什么方法,就重写什么方法即可
// lambda表达式必须是函数式接口

    // public void run() {...}
    // public是能够智能识别的
    // void是能够智能识别
    // ()说明的是函数式接口当中的函数没有任何参数,假设有参数,参数类型都不用写,因为可以智能识别
    // ->用于分隔参数和函数体/方法体
    // {}方法体
    // 方法体内,如果函数式接口当中的函数需要返回值,那就写return语句
    // 如果返回值类型为void,那就不写return语写
    Thread thread = new Thread(() -> {
        try {
            while (true) {
                System.out.println("second runnable里的过程");
                Thread.sleep(1500);
            }
        } catch (InterruptedException e) {
            System.out.println("线程退出了");
        }
    }, "thread and runnable ...");
    thread.start();

    while (true) {
        System.out.println("主线程代码");

        Thread.sleep(2000);
    }
}

}

注意

  1. 主线程退出,其他线程不管有没有执行结束,都会结束
  2. 当多个线程同时使用一个资源时,极有可能会出现线程同步问题: 当多个线程同时争抢一个资源时,操作此资源的代码如果无法保证原子性(类似于数据库中的事务),就会产生数据错乱问题 为了保证数据是对的,或保证数据的一致性,需要在操作此资源时添加线程同步的机制 推荐使用synchronized关键字来保证此机制

线程同步操作演示

  • SecondRunnable.java
package com.futureweaver.threadsync;

import java.util.ArrayList;
import java.util.List;

public class SecondRunnable implements Runnable {
private List<String> list;

public SecondRunnable(List&lt;String&gt; list) {
    this.list = list;
}

@Override
public void run() {
    try {
        for (int i = 20000; i &lt; 21000; i++) {
            Thread.sleep(1);
            add(list, Integer.toString(i));
        }
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
}

// syncrhonized关键字能够保证在操作此list时,不会发生资源争抢问题
public static synchronized void add(List&lt;String&gt; list, String str) {
    // 开始
    list.add(str);
    // 结束
}

}

  • ThreadPractice.java
package com.futureweaver.threadsync;

import java.util.ArrayList;
import java.util.List;

public class ThreadPractice {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<>();
SecondRunnable secondRunnable = new SecondRunnable(list);
Thread thread = new Thread(secondRunnable);
thread.start();

    for (int i = 0; i &lt; 10000; i++) {
        Thread.sleep(1);
        String s = Integer.toString(i);
        SecondRunnable.add(list, s);
    }

    Thread.sleep(5000);
    System.out.println(list);
}

}

死锁问题

所谓死锁问题,就是在等待一把永远也解不开的锁

死锁案例1

加锁后出现异常,导致锁无法解开

  • SecondRunnable.java
package com.futureweaver.threadsync;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.reentrantlock;

public class SecondRunnable implements Runnable {
private List<String> list;

// 注意,如果多个线程同时操作某个资源
// 那么想要用Lock加线程同步,必须要保证这几个线程用的是同一把锁
public static Lock lock = new reentrantlock();

public SecondRunnable(List&lt;String&gt; list) {
    this.list = list;
}

@Override
public void run() {
    try {
        for (int i = 20000; i &lt; 21000; i++) {
            Thread.sleep(1);
            add(list, Integer.toString(i));
        }
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
}

public static void add(List&lt;String&gt; list, String str) {
    // 将这一把锁上锁
    lock.lock();

    // 开始
    list.add(str);
    // 结束

    list.get(30000);

    // 将这一把锁解锁
    lock.unlock();
}

}

  • ThreadPractice.java
package com.futureweaver.threadsync;

import java.util.ArrayList;
import java.util.List;

public class ThreadPractice {
public static void main(String[] args) throws InterruptedException {
List<String> list = new ArrayList<>();
SecondRunnable secondRunnable = new SecondRunnable(list);
Thread thread = new Thread(secondRunnable);
thread.start();

    for (int i = 0; i &lt; 10000; i++) {
        Thread.sleep(1);
        String s = Integer.toString(i);
        SecondRunnable.add(list, s);
    }

    Thread.sleep(5000);
    System.out.println(list);
}

}

死锁案例二

小明锁了A,等待B。同时B被小红锁上,等待A

  • XiaoHong.java
package com.futureweaver.threadsync;

import java.util.concurrent.locks.Lock;

public class XiaoHong implements Runnable {
private Lock left;
private Lock right;

public XiaoHong(Lock left, Lock right) {
    this.left = left;
    this.right = right;
}

@Override
public void run() {
    right.lock();
    System.out.println("小红拿到第二根筷子");

    left.lock();
    System.out.println("小红拿到第一根筷子");

    System.out.println("吃饭");

    left.unlock();
    right.unlock();
}

}

  • XiaoMing.java
package com.futureweaver.threadsync;

import java.util.concurrent.locks.Lock;

public class XiaoMing implements Runnable {
private Lock left;
private Lock right;

public XiaoMing(Lock left, Lock right) {
    this.left = left;
    this.right = right;
}

@Override
public void run() {
    left.lock();
    System.out.println("小明拿到了第一根筷子");

    right.lock();
    System.out.println("小明拿到了第二根筷子");

    System.out.println("吃饭");

    right.unlock();
    left.unlock();
}

}

  • DoubleLockDeadPractice.java
package com.futureweaver.threadsync;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.reentrantlock;

public class DoubleLockDeadPractice {
public static void main(String[] args) {
Lock left = new reentrantlock();
Lock right = new reentrantlock();

    Thread xiaohong = new Thread(new XiaoHong(left, right));
    Thread xiaoming = new Thread(new XiaoMing(left, right));

    xiaohong.start();
    xiaoming.start();
}

}

相关文章

Java中的String是不可变对象 在面向对象及函数编程语言中,不...
String, StringBuffer 和 StringBuilder 可变性 String不可变...
序列化:把对象转换为字节序列的过程称为对象的序列化. 反序...
先说结论,是对象!可以继续往下看 数组是不是对象 什么是对...
为什么浮点数 float 或 double 运算的时候会有精度丢失的风险...
面试题引入 这里引申出一个经典问题,看下面代码 Integer a ...