线程复习

Posted littlepage

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程复习相关的知识,希望对你有一定的参考价值。

本章节根据狂神的多线程视频进行整理,狂神多线程->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 + "只??");
            container.push(new Chicken(i));
        }
    }
}
// 消费者
class Consumer extends Thread {
    SynContainer container;
    public Consumer(SynContainer container) {
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消费了" + container.pop().id + "只??");
        }
    }
}
// 商品
class Chicken {
    int id;
    public Chicken(int id) {
        this.id = id;
    }
}
// 缓冲区
class SynContainer {
    Chicken[] chickens = new Chicken[10];
    int index;
    public synchronized void push(Chicken chicken) {
        if(index == chickens.length) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        chickens[index++] = chicken;
        this.notifyAll();
    }

    public synchronized Chicken pop() {
        if(index == 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.notifyAll();
        return chickens[--index];
    }
}

解决方式2: 使用一个标志位置(信号灯法)

public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();
        Player player = new Player(tv);
        Watcher watcher = new Watcher(tv);
        player.start();
        watcher.start();
    }
}

class Watcher extends Thread {
    TV tv;

    public Watcher(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}

class Player extends Thread {
    TV tv;

    public Player(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i % 2 == 0) {
                this.tv.play("快乐大本营");
            }else {
                this.tv.play("抖音");
            }
        }
    }
}

class TV {
    String voice;
    boolean flag = true;

    public synchronized void play(String voice) {
        if (!flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("演员表演了" + voice);
        this.voice = voice;
        this.flag = !this.flag;
        this.notifyAll();
    }

    public synchronized void watch() {
        if (flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观众观看了" + voice);
        this.flag = !this.flag;
        this.notifyAll();
    }
}

7.线程池

经常创建和销毁一些资源,在并发的时候会影响很大,所以使用线程池能解决这个问题

  • ExecutorService 线程池接口
    • void execute(Runnable command) 执行runnable
    • <T> Future <T> submit(Callable<T> task) 执行任务,有返回值,一般使用Callable
    • void shutdown() 关闭线程池
  • Executors 工具类
public class TestPool {
    public static void main(String[] args) {
        ExecutorService service = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 10; i++) {
            service.execute(new MyThread());
        }
        service.shutdownNow();
    }
}
class MyThread implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "  " + i);
        }
    }
}
ExecutorService ser = Executors.newFixedThreadPool(3);
Future<Boolean> future = ser.submit(new TestCallable());
Boolean b = future.get();
System.out.println(b);
ser.shutdownNow();

以上是关于线程复习的主要内容,如果未能解决你的问题,请参考以下文章

newCacheThreadPool()newFixedThreadPool()newScheduledThreadPool()newSingleThreadExecutor()自定义线程池(代码片段

安卓复习8

安卓复习8

安卓复习8

复习打卡--0821多线程并发

JavaSE复习4