Java---多线程入门

线程创建

Thread

package thread;

class MyThread extends Thread {
    @Override
    public void run() {
        for(int i = 0; i < 10; i++) {
            System.out.println("MyThread is running...");
        }
    }
}

public class StartThread01 {
    public static void main(String[] args) {

        new MyThread().start(); // 建立线程,什么时候被 CPU 调度到,什么时候就开始执行
//        new MyThread().run(); // 调用 run 方法,是普通方法调用,不会出现和主线程交替执行的情况
        for(int i = 0; i < 10; i++) {
            System.out.println("Main thread is running...");
        }
//        如在这里建立线程,由于后面没有主线程的代码了,一定是前面的主线程代码执行完再一直执行 MyThread 线程的代码
//        new MyThread().start();
    }
}

Runnable

package thread;

class MyThread2 implements Runnable {
    @Override
    public void run() {
        for(int i = 0; i < 10; i++) {
            System.out.println("MyThread is running...");
        }
    }
}

public class StartThread02 {
    public static void main(String[] args) {
        new Thread(new MyThread2()).start();
        for(int i = 0; i < 10; i++) {
            System.out.println("Main thread is running...");
        }
    }
}

龟兔赛跑

package thread;

public class Race implements Runnable{

    public static String winner = null;

    @Override
    public void run() {
        for(int step = 1; step <= 100; step++) {
            // 模拟兔子休息
            if(Thread.currentThread().getName().equals("rabbit") && (step % 10 == 0)) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            boolean flag = gameOver(step);
            if(flag) {
                break;
            } else {
                System.out.println(Thread.currentThread().getName() + "-->" + step);
            }
        }
    }
    private boolean gameOver(int step) {
        if(winner != null) {
            return true;
        } else {
            if(step == 100) {
                winner = Thread.currentThread().getName();
                System.out.println("Winner is " + winner + ".");
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Race race = new Race(); // 下面两个线程共享同一个 Race,即龟兔参加的是同一场比赛。。
        new Thread(race, "rabbit").start();
        new Thread(race,"tortoise").start();
    }
}

龟兔赛跑(Callable)

package thread;

import java.util.concurrent.*;

public class CRace implements Callable<Integer>{

    public static String winner = null;

    @Override
    public Integer call() {
        for(int step = 1; step <= 100; step++) {

            String currentThreadName = Thread.currentThread().getName();
            currentThreadName = currentThreadName.equals("pool-1-thread-1") ? "rabbit" : "tortoise";

            // 模拟兔子休息
            if(currentThreadName.equals("rabbit") && (step % 5 == 0)) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            boolean flag = gameOver(step);
            if(flag) {
                return step;
            } else {
                System.out.println(currentThreadName + "-->" + step);
            }
        }
        return null;
    }
    private boolean gameOver(int step) {
        if(winner != null) {
            return true;
        } else {
            if(step == 100) {
                winner = Thread.currentThread().getName().equals("pool-1-thread-1") ? "rabbit" : "tortoise";
                System.out.println("Winner is " + winner + ".");
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CRace race = new CRace(); // 下面两个线程共享同一个 Race,即龟兔参加的是同一场比赛。。

        ExecutorService service = Executors.newFixedThreadPool(2);

        Future<Integer> rabbit = service.submit(race);
        Future<Integer> tortoise = service.submit(race);

        Integer rabbitResult = rabbit.get();
        Integer tortoiseResult = tortoise.get();

        System.out.println("rabbit " + rabbitResult + ", tortoise " + tortoiseResult);

        service.shutdown();
    }
}

线程状态

新建(NEW)
就绪(RUNNABLE)
运行(RUNNABLE)
等待(BLOCKED/WAITING/TIMED_WAITING)
停止(TERMINATED)

Java Thread States and Life Cycle
Java---多线程入门_第1张图片
Thread.sleep,不释放锁进入等待状态,等待时间结束后进入就绪状态。

package state;

public class BlockedSleep01 {
    public static void main(String[] args) throws InterruptedException {
        int n = 3; // 倒计时 3 秒
        while (true){
            System.out.println(n--);
            if(n < 1) {
                break;
            }
            Thread.sleep(1000);
        }
    }
}

Thread.yield,运行状态变成就绪状态,让出 CPU 的调度。

package state;

class MyYield implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "-->start" );
        Thread.yield();
        System.out.println(Thread.currentThread().getName() + "-->end");
    }
}

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

在这里插入图片描述
join 方法。下面代码展示了爸爸让儿子买烟的故事,只有儿子买烟回来后,爸爸才能拿到烟,爸爸给儿子发号施令——给我买烟去之后,就要等待儿子买烟这个事搞完,才能拿到烟,也就是说在爸爸线程中,儿子线程调用 join 方法把爸爸线程阻塞掉,执行完儿子线程后,爸爸线程往后执行拿到烟。。

package state;

public class BlockedJoin01 {
    public static void main(String[] args) {
        System.out.println("爸爸让儿子买烟的故事");
        new Thread(new Father()).start();
    }
}

class Father extends Thread {
    @Override
    public void run() {
        System.out.println("爸爸:儿子买烟去");
        Thread son = new Son();
        son.start();

        try {
            son.join(); // 爸爸只有在儿子买烟回来才能拿到烟,阻塞爸爸线程,等待儿子线程执行完
            System.out.println("爸爸:谢谢儿子买的烟");
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("儿子丢了。。。");
        }
    }
}
class Son extends Thread {
    @Override
    public void run() {
        System.out.println("儿子:我去买烟了");
        System.out.println("儿子:路上走进游戏厅");
        for(int i = 0; i < 6; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println((i+1) + " 秒过去了...");
        }
        System.out.println("儿子:想起买烟的事,赶快买烟回家了");
    }
}

setPriority 方法设置线程优先级,要在执行之前设置,1-10。优先级越大,被优先调度的概率大,但不代表绝对的执行顺序。

setDaemon 方法设置是否是守护线程,JVM 会等待所有用户线程执行完,但不必等守护线程执行完。

并发

synchronized

多人同时购票问题

package concurrency;

class SafeWeb12306 implements Runnable {
    private int ticketNum = 1000;
    private boolean flag = true;    // 是否有余票标志
    @Override
    public void run() {
        while (flag) {
            test();
        }
    }
    // 1. 不用 synchronized 同步(特别是网络延迟导致的线程安全问题,可以模拟延迟),
    // (1)有可能多人拿到同一号票,各个线程从主存读取数据到其私有工作内存,修改后写回主内存,整个过程不是原子性的。
    // (2)也可能拿到 0 号票,比如两个线程同时看到 ticketNum = 1,其中一个线程拿到 1 号票,另一个线程会拿到 0 号票。
    // 2. synchronized 普通方法锁的是当前对象(和 ticketNum、flag 相关联的当前对象),重点分析多线程共享的是谁,这里
    //  多线程共享的是类 SafeWeb12306 的对象 web,所以对 SafeWeb12306 的对象加锁是没问题的
    public synchronized void test() {
        if(ticketNum <= 0) {
            flag = false;
            return;
        }
        try {
            Thread.sleep(10);  // 模拟网络延迟
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "-->" + ticketNum--);
    }
}

public class SynTest01 {
    public static void main(String[] args) {
        SafeWeb12306 web = new SafeWeb12306();
        new Thread(web, "buyer1").start();
        new Thread(web, "buyer2").start();
        new Thread(web, "buyer3").start();
    }
}

多人从同一账号取钱问题

package concurrency;

class Account {
    private String name;
    private double money;

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

    public String getName() {
        return name;
    }
    public double getMoney() {
        return money;
    }
    public void setMoney(double money) {
        this.money = money;
    }
}

class SafeDrawing implements Runnable {

    private Account account;    // 从哪个账号取钱
    private String name; // 取钱人
    private double drawingMoney;    // 取钱数

    public SafeDrawing(Account account, String name, double drawingMoney) {
        this.account = account;
        this.name = name;
        this.drawingMoney = drawingMoney;
    }

    @Override
    public void run() {
        test();
    }
    // 关键还是关注共享的资源是谁,这里多线程共享的是 Account 类的对象 account,应该锁 account,如果对下面 test 方法
    // 加上 synchronized,上锁的对象却是 SafeDrawing 类的对象,而每个线程分别持有不同的 SafeDrawing 类的对象,根本就
    // 没有共享同一个 SafeDrawing,锁 SafeDrawing 的对象没有用。要真想通过锁 SafeDrawing 来解决线程安全,那就锁 SafeDrawing
    // 的类对象 SafeDrawing.class
    //
    // 其实,这里只需要给 account 上锁就 OK 了
    private synchronized void test() {
        synchronized (account) {
            if (account.getMoney() < drawingMoney) {
                System.out.println("余额不足..." + "(" + account.getMoney() + " --> " + drawingMoney + ")");
                return;
            }
            // 模拟延迟
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            account.setMoney(account.getMoney() - drawingMoney);
            System.out.println("account.money = " + account.getMoney());
        }
    }
}

public class SynTest02 {
    public static void main(String[] args) {
        Account account = new Account("account", 100.0);
        new Thread(new SafeDrawing(account, "person1", 90.0)).start();
        new Thread(new SafeDrawing(account, "person2", 80.0)).start();
    }
}

死锁

package concurrency;

// 模拟两个玩家玩玩具,玩家1持有玩具A的情况下想玩玩具B,玩家2持有玩具B的情况下想玩玩具A

class ToyA {}
class ToyB {}

class Player implements Runnable {
    public static ToyA toyA = new ToyA();
    public static ToyB toyB = new ToyB();
    private int choice;

    public Player(int choice) {
        this.choice = choice;
    }

    @Override
    public void run() {
        if(choice == 1) {
            synchronized (toyA) {
                System.out.println(Thread.currentThread().getName() + " is playing with toyA...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (toyB) {
                    System.out.println(Thread.currentThread().getName() + " want to play with toyB...");
                }
            }
        } else {
            synchronized (toyB) {
                System.out.println(Thread.currentThread().getName() + " is playing with toyB...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (toyA) {
                    System.out.println(Thread.currentThread().getName() + " want to play with toyA...");
                }
            }
        }
    }
}

public class DeadLock {
    public static void main(String[] args) {
        new Thread(new Player(1), "player1").start();
        new Thread(new Player(2), "player2").start();
    }
}

如下,稍微改下就不死锁了。。。

package concurrency;

// 模拟两个玩家玩玩具,玩家1持有玩具A的情况下想玩玩具B,玩家2持有玩具B的情况下想玩玩具A

class ToyA {}
class ToyB {}

class Player implements Runnable {
    public static ToyA toyA = new ToyA();
    public static ToyB toyB = new ToyB();
    private int choice;

    public Player(int choice) {
        this.choice = choice;
    }

    @Override
    public void run() {
        if(choice == 1) {
            synchronized (toyA) {
                System.out.println(Thread.currentThread().getName() + " is playing with toyA...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }   // 把这个或 else 对应的那个拿出来就不会死锁了
            synchronized (toyB) {
                System.out.println(Thread.currentThread().getName() + " want to play with toyB...");
            }
        } else {
            synchronized (toyB) {
                System.out.println(Thread.currentThread().getName() + " is playing with toyB...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (toyA) {
                    System.out.println(Thread.currentThread().getName() + " want to play with toyA...");
                }
            }
        }
    }
}

public class DeadLock {
    public static void main(String[] args) {
        new Thread(new Player(1), "player1").start();
        new Thread(new Player(2), "player2").start();
    }
}

生产者消费者模式

生产者和消费者共享缓冲区,生产者线程往共享缓冲区存数据,消费者从共享缓冲区取数据。多线程共享资源就需要同步(synchronized),而缓冲区不满才能继续生产,缓冲区非空才能消费,需要协作好,一旦消费了一个产品就可通知生产者继续生产,一旦生产了一个产品就可通知消费者继续消费(wait,notify)。

package concurrency;

public class CoTest01 {
    public static void main(String[] args) {
        Buffer buffer = new Buffer();
        new Producer(buffer).start();
        new Consumer(buffer).start();
    }
}

// 生产者
class Producer extends Thread {
    private Buffer buffer;
    public Producer(Buffer buffer) {
        this.buffer = buffer;
    }
    public void run() {
        for(int i = 1; i <= 100; i++) {
            System.out.println("To produce Product " + i);
            buffer.push(new Product(i));
        }
    }
}
// 消费者
class Consumer extends Thread {
    private Buffer buffer;
    public Consumer(Buffer buffer) {
        this.buffer = buffer;
    }
    public void run() {
        for(int i = 1; i <= 100; i++) {
            System.out.println("To consume Product " + buffer.pop().getId());
        }
    }
}
// 缓冲区
class Buffer {
    private Product[] products = new Product[100];
    private int count = 0;

    public synchronized void push(Product product) {
        if(count == products.length) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        products[count] = product;
        count++;
        notifyAll();
    }

    public synchronized Product pop() {
        if(count == 0) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        count--;
        Product product = products[count];
        notifyAll();
        return product;
    }
}
// 产品
class Product {
    private int id;
    public Product(int id) {
        this.id = id;
    }
    public int getId() {
        return  id;
    }
}

可重入锁

可重入锁,同时可以持有某个锁多次。内置锁是可重入的,有个计数器,持有一次,计数器加1,释放一次,计数器减1。

参考资料

[1] https://www.bilibili.com/video/BV1ct411n7oG?p=208
[2] https://blog.csdn.net/ccnuacmhdu/article/details/98957882

你可能感兴趣的:(Java基础知识记)