首页 > 技术文章 > 线程复习

littlepage 2020-02-15 06:39 原文

本章节根据狂神的多线程视频进行整理,狂神多线程->av54171171

1.线程的概念

概念:线程是操作系统能够进行运算调度的最小单位。

2.Java实现线程的3种方式

继承Thread类、实现Runnable接口、实现Callable接口

Callable不常用,容易忘记。所以重新写一遍

ExecutorService ser = Executors.newFixedThreadPool(3);
Future<Boolean> future = ser.submit(new TestCallable());
Boolean b = future.get();
System.out.println(b);
ser.shutdownNow();

线程安全:

多个线程操作同一个对象,会造成线程不安全

3.线程的5种状态

创建状态、就绪状态、阻塞状态、运行状态、死亡状态

new、runnable、blocked、waiting(timed_waiting)、terminated

Java中方法:

线程休眠 sleep

线程礼让 yield

让运行状态转变为就绪状态,让CPU重新调度,礼让不一定成功

测试:

public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();
        new Thread(myYield, "a").start();
        new Thread(myYield, "b").start();
    }
}

class MyYield implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "开始执行");
        Thread.yield(); // 礼让
        System.out.println(Thread.currentThread().getName() + "结束执行");
    }
}

打印结果:

run() 进行到一半进行了礼让,但是这种情况不会总是发生

a开始执行
b开始执行
a结束执行
b结束执行

线程强制执行 join:

在这个例子中,我们看到在200之前,这两个线程还是接受CPU调度的,但是一旦thread进行了join,那么强制执行thread。

public class TestJoin implements Runnable{
    public static void main(String[] args) throws InterruptedException {
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();
        for(int i = 0; i < 1000; i++) {
            System.out.println(i);
            if(i == 200) {
                thread.join();
            }
        }
    }
    @Override
    public void run() {
        for(int i = 0; i < 100; i++){
            System.out.println("线程VIP"+i);
        }
    }
}

检测线程状态:

public class Observer {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("///////");
        });
        Thread.State state = thread.getState();
        System.out.println(state);
        thread.start();
        while(state != Thread.State.TERMINATED) {
            System.out.println(thread.getState());
            Thread.sleep(100);
            state = thread.getState();
        }
    }
}

线程的优先级:

线程默认优先级为5,我们可以通过setPriority和getPriority进行获取和设置优先级。

守护线程:

所有普通线程结束后,也会结束。

public class DaemonTest {
    public static void main(String[] args) {
        new Thread(new You()).start();
        Thread thread = new Thread(new God());
        thread.setDaemon(true);
        thread.start();
    }
}
class You implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("我还活着");
        }
    }
}
class God implements Runnable {
    @Override
    public void run() {
        while(true) {
            System.out.println("保佑你");
        }
    }
}

4.线程同步机制

三个不安全的案例

  • 买票买负数
public class BuyTicket implements Runnable {
    private int ticketNums = 10;
    private boolean flag = true;
    @Override
    public void run() {
        while (flag) {
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    private void buy() throws InterruptedException {
        if(ticketNums <= 0) {
            flag = false;
            return;
        }
        Thread.sleep(10);
        System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
    }
    public static void main(String[] args) {
        BuyTicket ticket = new BuyTicket();
        new Thread(ticket, "1").start();
        new Thread(ticket, "2").start();
        new Thread(ticket, "3").start();
    }
}
  • 银行取钱
public class UnsafeBank {
    public static void main(String[] args) {
        Account account = new Account(1000,"steveyu");
        Drawing drawing = new Drawing(300, account);
        Drawing drawing2 = new Drawing(800, account);
        drawing.start();
        drawing2.start();
    }
}

class Account {
    public int money;
    public String name;

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

class Drawing extends Thread {
    int drawing;
    Account account;
    public Drawing(int drawing, Account account) {
        this.drawing = drawing;
        this.account = account;
    }
    @Override
    public void run() {
        if (account.money - drawing < 0) {
            System.out.println(Thread.currentThread().getName() + "钱不够");
            return;
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        account.money -= drawing;
        System.out.println(account.name + "余额为" + account.money);
        System.out.println("手里" + drawing);
    }
}
  • 不安全集合
public class UnSafeList {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> list.add(Thread.currentThread().getName())).start();
        }
        System.out.println(list.size());
    }
}

Synchronized同步方法默认锁this,通常用同步方法块

ArrayList在JUC中,是CopyOnWriteArrayList

5.死锁

多个线程互相抱着对象的资源,进行相互对峙

案例:女孩化妆

public class DeadLock {
    public static void main(String[] args) {
        Makeup makeup = new Makeup(0, "beauty1");
        Makeup makeup2 = new Makeup(1, "beauty2");
        makeup.start();
        makeup2.start();
    }
}

class Lipstick { }

class Mirror { }

class Makeup extends Thread {
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
    int choice;//选择
    String girlName;//化妆的人

    Makeup(int choice, String girlName) {
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        if (choice == 0) {
            synchronized (lipstick) {
                System.out.println(this.girlName + "获得口红的锁");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (mirror) {
                    System.out.println(this.girlName + "获得镜子的锁");
                }
            }
        } else {
            synchronized (mirror) {
                System.out.println(this.girlName + "获得镜子的锁");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lipstick) {
                    System.out.println(this.girlName + "获得口红的锁");
                }
            }
        }
    }
}

Lock锁,JDK5.0

Lock是一个借口,ReetrantLock是可重用锁。

  • lock显式,synchronized隐式
  • lock性能更好,有更好的扩展性
  • 使用优先顺序,Lock>同步代码块>同步方法
public class TestLock {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket).start();
        new Thread(buyTicket).start();
        new Thread(buyTicket).start();
    }
}

class BuyTicket implements Runnable {
    int ticketNums= 10;
    private final Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true) {
            lock.lock();
            try {
                if(ticketNums > 0) {
                    Thread.sleep(100);
                    System.out.println("剩余" + ticketNums--);
                }else {
                    break;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            finally {
                lock.unlock();
            }
        }
    }
}

6.线程通信机制(生产者消费者问题)

我们的线程在通讯的时候,存在一个生产者消费者问题

  • 假设仓库中只能存放一件产品,生产者将产品放入仓库,消费者取出。

  • 如果仓库没有产品,生产者放入仓库,否则停止生产并等待,直到产品全部取走为止

  • 如果仓库中存在产品,消费着可以将产品取走并消费,否则停止消费,直到仓库再放入产品

问题:

在生产者消费者问题中,仅仅有synchronized是不够的

  • synchronized可以阻止并发更新同一个共享资源,实现同步
  • synchronized不能用来实现不同线程中的消息传递

为了解决这个问题,Java引入一个wait和notify

wait() 表示现场一直等待,直到其他线程通知

wait(long timeout) 等待毫秒数

notify() 唤醒一个处于等待状态的线程

notifyAll() 唤醒同一个对象所有调用wait() 方法的线程,优先级别高的线程优先调度

解决方式1: 引入数据缓冲区(管程法)

public class TestPC {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();
        Producter producter = new Producter(container);
        Consumer consumer = new Consumer(container);
        producter.start();
        consumer.start();
    }
}
// 生产者
class Producter extends Thread {
    SynContainer container;

    public Producter(SynContainer container) {
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("生产了" + i + "只

推荐阅读