多线程
2021-03-18
10 min read
基本概念
- 进程(Process)、线程(Thread)、多线程
- 程序是指令和数据的集合,是一个静态的概念
- 进程是程序的一次的执行过程,是一个动态的概念。是系统资源分配的单位
- 通常在一个进程包含若干个线程,但至少有一个线程,如main。是CPU调度和执行的单位
- 很多多线程是模拟出来的,即在一个CPU的情况下,在同一个时间点,CPU只能执行一个代码,因为切换的很快,所以就有同时执行的错觉。
- 在一个进程中,如果开辟了多个线程,线程的运行由CPU安排调度,先后顺序是不能人为干预的
- 对同一份资源进行操作时,会存在资源抢夺问题,需要加入并发控制
- 线程会带来额外的开销,如CPU调度时间,并发控制开销
- 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致
创建线程三种方式
- 继承Thread类
//创建线程方式一:继承Threa类
public class TestThread1 extends Thread {
@Override
public void run() {
//重写run()方法
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码---" + i);
}
}
public static void main(String[] args) {
//main方法主线程体
TestThread1 testThread1 = new TestThread1();
//调用start()方法开启线程
testThread1.start();
for (int i = 0; i < 1000; i++) {
System.out.println("我在学多线程---" + i);
}
}
}
注意:线程开启不一定立即执行,由CPU调度执行
- 实现Runnable()接口,
//创建线程方式二:实现Runnable()接口
public class TestThread2 implements Runnable{
@Override
public void run() {
//重写run()方法
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码---" + i);
}
}
public static void main(String[] args) {
//创建Runnable()接口实现类
TestThread2 testThread2 = new TestThread2();
//创建线程对象,通过线程对象开启我们的线程,代理
new Thread(testThread2).start();
for (int i = 0; i < 1000; i++) {
System.out.println("我在学多线程---" + i);
}
}
}
- 实现callable()接口
静态代理模式
- 真实对象和代理对象都要实现同一个接口
- 代理对象要代理真实角色
- 好处
- 代理对象可以做很多真实对象做不了的事
- 真实对象专注做自己事
Lambda表达式
- 函数式接口
- 任何接口,如果只包含唯一一个抽象方法,那么他就是函数式接口
- 对于函数式接口,我们可以通过lambda表达式来创建该接口的对象
- Runnable接口就是一个函数式接口
- Lambda表达式实现
public class LambdaTest {
public static void main(String[] args) {
//实现Lambda表达式
ILove i = (x) -> {
System.out.println("I love you!" + x);
};
i.print(20);
}
}
//定义一个函数式接口
interface ILove {
void print(int x);
}
线程状态
- 线程停止
- 建议线程正常停止-->利用次数,不建议死循环
- 建议使用标志位-->设置一个标志位
- 不要使用stop或destroy等过时或者JDK不建议使用的方法
- 线程间共享变量需要使用volatile关键字标记,确保每个线程都能读取到更新后的变量值
public class TestStop implements Runnable {
//1、设置一个标识位
private volatile boolean flag = true;
@Override
public void run() {
int i = 0;
//2、线程中使用该标识符
while (flag) {
System.out.println("run...Thread" + i++);
}
}
//3、设置一个公开的方法停止线程,转换标识位
public void stop() {
this.flag = false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 200; i++) {
System.out.println("main" + i);
if (i == 150) {
//调用stop()方法停止线程
testStop.stop();
System.out.println("线程停止了");
}
}
}
}
-
线程休眠
- Thread.sleep(时间) 指定当前线程阻塞的毫秒数
- sleep可以模拟网络延时、倒计时等
- 每个对象都有一个锁,sleep不会释放锁
-
线程礼让
- Thread.yield()
- 将线程从运行状态转为就绪状态
- CPU重新重新调度,但不一定成功
-
线程强制执行
- join合并线程,将此线程执行完毕后,在执行其他线程,其他线程阻塞
- 可以想象成插队
public class TestJoin implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("线程VIP来了" + i);
}
}
public static void main(String[] args) throws InterruptedException {
//启动我们的线程
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
//主线程
for (int i = 0; i < 500; i++) {
if (i == 200)
//插队
thread.join();
System.out.println("main" + i);
}
}
}
- 守护线程
- 线程分为用户线程和守护线程
- 虚拟机必须确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
public class TestDaemon {
public static void main(String[] args) {
You you = new You();
God god = new God();
Thread thread = new Thread(god);
//默认false是用户线程,设置true为守护线程
thread.setDaemon(true);
thread.start();
new Thread(you).start();
}
}
class You implements Runnable{
@Override
public void run() {
for (int i = 0; i < 3000; i++) {
System.out.println("开心活着");
}
System.out.println("Game over!");
}
}
class God implements Runnable{
@Override
public void run() {
while (true) {
System.out.println("上帝保佑你!");
}
}
}
同步线程
- 并发:同一个对象被多个线程同时操作,可能会引起资源冲突
- 线程同步是一种等待机制,多个需要同时访问一个对象的线程进入这个对象的等待池,形成队列,等待前面线程访问完毕,下一个线程再使用
- 锁:synchronized关键字对一个对象进行加锁,当一个线程获得对象的锁,独占资源,其他线程需等待
- 使用锁会使性能降低,所以方法里需要修改的内容才需要锁
- 使用锁的方法
- 同步方法
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket station = new BuyTicket();
new Thread(station, "邓憨憨").start();
new Thread(station, "夜十七").start();
new Thread(station, "黄卡丽").start();
}
}
class BuyTicket implements Runnable {
private int tickets = 10;
boolean flag = true;//外部停止线程方法
@Override
public void run() {
//买票
while (flag) {
try {
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//synchronized 同步方法 锁的是this.tickets
private synchronized void buy() throws InterruptedException {
if (tickets <= 0) {
flag = false;
return;
}
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "拿到了票" + tickets--);
}
}
2. 同步块
public class UnsafeBank {
public static void main(String[] args) {
Account account = new Account(100,"结婚基金");
Drawing you = new Drawing(account,50,"you");
Drawing me = new Drawing(account,100,"me");
you.start();
me.start();
}
}
//账户类
class Account {
int money;
String name;
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
//取钱类
class Drawing extends Thread {
Account account;
int drawingMoney;//取出多少钱
int nowMoney; //手中的钱
public Drawing(Account account, int drawingMoney, String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
//同步方法块,默认锁this
synchronized (account) {
if (account.money - drawingMoney < 0) {
System.out.println("账户余额不足");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money -= drawingMoney;
nowMoney += drawingMoney;
System.out.println(this.getName() + "手里的钱" + nowMoney);
System.out.println(account.name + "账户余额:" + account.money);
}
}
}
- 死锁
多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或多个线程都在等待对方资源,都停止执行的情形。即某一个同步块同时拥有“两个以上对象的锁”时。
public class DeadLock {
public static void main(String[] args) {
MakeUp m1 = new MakeUp(0, "邓憨憨");
MakeUp m2 = new MakeUp(1, "黄卡丽");
m1.start();
m2.start();
}
}
//口红
class Lipstick {
}
//镜子
class Mirror {
}
class MakeUp extends Thread {
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;
String name;
MakeUp(int choice, String name) {
this.choice = choice;
this.name = name;
}
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void makeup() throws InterruptedException {
if (choice == 0) {
synchronized (lipstick) {
System.out.println(name + "获得口红的锁");
Thread.sleep(1000);
synchronized (mirror) {
System.out.println(name + "获得镜子的锁");
}
}
} else {
synchronized (mirror) {
System.out.println(name + "获得镜子的锁");
Thread.sleep(2000);
synchronized (lipstick) {
System.out.println(name + "获得口红的锁");
}
}
}
}
}
- lock锁:是显式锁
public class TestLock {
public static void main(String[] args) {
BuyTickets station = new BuyTickets();
new Thread(station, "邓憨憨").start();
new Thread(station, "夜十七").start();
new Thread(station, "黄卡丽").start();
}
}
class BuyTickets implements Runnable {
private int tickets = 10;
boolean flag = true;//外部停止线程方法
//定义锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
//买票
while (flag) {
try {
//加锁
lock.lock();
buy();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//解锁
lock.unlock();
}
}
}
private void buy() throws InterruptedException {
if (tickets <= 0) {
flag = false;
return;
}
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "拿到了票" + tickets--);
}
}
线程池
- 线程池内部维护一组线程,可以高效执行大量小任务
- Executors提供了静态方法创建不同类型的ExecutorService
- 必须调用shutdown()关闭ExecutorService
import java.util.concurrent.*;
public class TestPool {
public static void main(String[] args) {
//创建服务,创建线程池
ExecutorService service = Executors.newFixedThreadPool(4);
//执行
for (int i = 0; i < 6; i++) {
service.submit(new MyThreads());
}
//关闭链接
service.shutdown();
}
}
class MyThreads implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}