13_并发编程基础

2020-12-11  本文已影响0人  真是个点子王

线程的创建方式

方式一:继承Thread

public class ThreadDemo {
    // 启动后的ThreadDemo当成一个进程。
    // main方法是由主线程执行的,理解成main方法就是一个主线程
    public static void main(String[] args) {
        // 3.创建一个线程对象
        Thread t = new MyThread();
        // 4.调用线程对象的start()方法启动线程,最终还是执行run()方法!
        t.start();

        for(int i = 0 ; i < 100 ; i++ ){
            System.out.println("main线程输出:"+i);
        }
    }
}

// 1.定义一个线程类继承Thread类。
class MyThread extends Thread{
    // 2.重写run()方法
    @Override
    public void run() {
        // 线程的执行方法。
        for(int i = 0 ; i < 100 ; i++ ){
            System.out.println("子线程输出:"+i);
        }
    }
}

Thread类的API

1.public void setName(String name):给当前线程取名字。
2.public void getName():获取当前线程的名字。
     -- 线程存在默认名称,子线程的默认名称是:Thread-索引。
     -- 主线程的默认名称就是:main
3.public static Thread currentThread()
     -- 获取当前线程对象,这个代码在哪个线程中,就得到哪个线程对象。
4.public static void sleep(long time)
     -- 让当前线程休眠多少毫秒再继续执行
5.public Thread(String name)
     -- 创建线程对象并取名字。
public class ThreadDemo {
    // 启动后的ThreadDemo当成一个进程。
    // main方法是由主线程执行的,理解成main方法就是一个主线程
    public static void main(String[] args) {
        // 创建一个线程对象
        Thread t1 = new MyThread();
        t1.setName("1号线程");
        t1.start();
        //System.out.println(t1.getName()); // 获取线程名称

        Thread t2 = new MyThread();
        t2.setName("2号线程");
        t2.start();
        //System.out.println(t2.getName());  // 获取线程名称

        // 主线程的名称如何获取呢?
        // 这个代码在哪个线程中,就得到哪个线程对象。
        Thread m = Thread.currentThread();
        m.setName("最强线程main");
        //System.out.println(m.getName()); // 获取线程名称

        for(int i = 0 ; i < 10 ; i++ ){
            System.out.println(m.getName()+"==>"+i);
        }
    }
}

// 1.定义一个线程类继承Thread类。
class MyThread extends Thread{
    // 2.重写run()方法
    @Override
    public void run() {
        // 线程的执行方法。
        for(int i = 0 ; i < 10 ; i++ ){
            System.out.println(Thread.currentThread().getName()+"==>"+i);
        }
    }
}

方式二:实现Runnable接口

Thread的构造器

-- public Thread(){}
-- public Thread(String name){}
-- public Thread(Runnable target){}
-- public Thread(Runnable target,String name){}
public class ThreadDemo {
    public static void main(String[] args) {
        // 3.创建一个线程任务对象(注意:线程任务对象不是线程对象,只是执行线程的任务的)
        Runnable target = new MyRunnable();
        // 4.把线程任务对象包装成线程对象.且可以指定线程名称
        // Thread t = new Thread(target);
        Thread t = new Thread(target,"1号线程");
        // 5.调用线程对象的start()方法启动线程
        t.start();

        Thread t2 = new Thread(target);
        // 调用线程对象的start()方法启动线程
        t2.start();

        for(int i = 0 ; i < 10 ; i++ ){
            System.out.println(Thread.currentThread().getName()+"==>"+i);
        }
    }
}

// 1.创建一个线程任务类实现Runnable接口。
class MyRunnable implements Runnable{
    // 2.重写run()方法
    @Override
    public void run() {
        for(int i = 0 ; i < 10 ; i++ ){
            System.out.println(Thread.currentThread().getName()+"==>"+i);
        }
    }
}
public class ThreadDemo02 {
    public static void main(String[] args) {
        // 创建一个线程任务对象(注意:线程任务对象不是线程对象,只是执行线程的任务的)
        Runnable target = new Runnable() {
            @Override
            public void run() {
                for(int i = 0 ; i < 10 ; i++ ){
                    System.out.println(Thread.currentThread().getName()+"==>"+i);
                }
            }
        };
        // 把线程任务对象包装成线程对象.且可以指定线程名称
        Thread t = new Thread(target);
        // 调用线程对象的start()方法启动线程
        t.start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0 ; i < 10 ; i++ ){
                    System.out.println(Thread.currentThread().getName()+"==>"+i);
                }
            }
        }).start();

        for(int i = 0 ; i < 10 ; i++ ){
            System.out.println(Thread.currentThread().getName()+"==>"+i);
        }
    }
}

特点

方式三:实现Callable接口

public class ThreadDemo {
    public static void main(String[] args) {
        // 3.创建一个Callable的线程任务对象
        Callable call = new MyCallable();
        // 4.把Callable任务对象包装成一个未来任务对象
        //      -- public FutureTask(Callable<V> callable)
        // 未来任务对象是啥,有啥用?
        //      -- 未来任务对象其实就是一个Runnable对象:这样就可以被包装成线程对象!
        //      -- 未来任务对象可以在线程执行完毕之后去得到线程执行的结果。
        FutureTask<String> task = new FutureTask<>(call);
        // 5.把未来任务对象包装成线程对象
        Thread t = new Thread(task);
        // 6.启动线程对象
        t.start();

        for(int i = 1 ; i <= 10 ; i++ ){
            System.out.println(Thread.currentThread().getName()+" => " + i);
        }

        // 在最后去获取线程执行的结果,如果线程没有结果,让出CPU等线程执行完再来取结果
        try {
            String rs = task.get(); // 获取call方法返回的结果(正常/异常结果)
            System.out.println(rs);
        }  catch (Exception e) {
            e.printStackTrace();
        }

    }
}

// 1.创建一个线程任务类实现Callable接口,申明线程返回的结果类型
class MyCallable implements Callable<String>{
    // 2.重写线程任务类的call方法!
    @Override
    public String call() throws Exception {
        // 需求:计算1-10的和返回
        int sum = 0 ;
        for(int i = 1 ; i <= 10 ; i++ ){
            System.out.println(Thread.currentThread().getName()+" => " + i);
            sum+=i;
        }
        return Thread.currentThread().getName()+"执行的结果是:"+sum;
    }
}

特点

线程安全问题

//Account.java
public class Account {
    private String cardId;
    private double money;

    public void draw(double money) {
        System.out.println("现在取钱的用户:"+Thread.currentThread().getName());
        //显示谁来取钱

        if( this.money >= money){
            System.out.println("余额充足,"+Thread.currentThread().getName() + "取走:" + money);
            this.money -= money;
            System.out.println(Thread.currentThread().getName()+"取款后的余额为:"+this.money);
        }else{
            System.out.println("余额不足:"+Thread.currentThread().getName());
        }
    }

    public Account() {
    }

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

    @Override
    public String toString() {
        return "Account{" +
                "cardId='" + cardId + '\'' +
                ", money=" + money +
                '}';
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
}


//DrawThread.java
public class DrawThread extends Thread{
    private Account account;


    public DrawThread(Account account,String name){
        super(name);
        this.account = account;
    }

    @Override
    public void run(){
        account.draw(1000000);
    }
}


//ThreadSafe.java
public class ThreadSafe {
    public static void main(String[] args) {

        Account account = new Account("ICBC-110",1000000);
        //创建一个账户

        Thread t1 = new DrawThread(account,"小红");
        // 小红进程

        Thread t2 = new DrawThread(account,"小明");
        // 小明进程
        t1.start();
        t2.start();
    }
}

线程同步


方法一:同步代码块

synchronized(锁对象){
 // 访问共享资源的核心代码
}
锁对象:理论上可以是任意的“唯一”对象即可。
原则上:锁对象建议使用共享资源。
    -- 在实例方法中建议用this作为锁对象。此时this正好是共享资源!必须代码高度面向对象
    -- 在静态方法中建议用类名.class字节码作为锁对象。
public void drawMoney(double money) {
        String name = Thread.currentThread().getName();
        synchronized (this){
            if(this.money >= money){           
                System.out.println(name+"来取钱,吐出:"+money);                
                this.money -= money;
                System.out.println(name+"来取钱"+money+"成功,取钱后剩余:"+this.money);
            }else{
                System.out.println(name+"来取钱,余额不足,剩余"+this.money);
            }
        }
    }

方法二:同步方法

    public synchronized void drawMoney(double money) {      
        String name = Thread.currentThread().getName();
        if(this.money >= money){
            System.out.println(name+"来取钱,吐出:"+money);
            this.money -= money;
            System.out.println(name+"来取钱"+money+"成功,取钱后剩余:"+this.money);
        }else{
            System.out.println(name+"来取钱,余额不足,剩余"+this.money);
        }
    }

方法三:Lock显式锁

public class Account {
    private double money ; // 余额
    private String cardId ;
    // 创建一把锁对象:因为账户对象对于小明小红是唯一的,所以这里的锁对象对于小明小红也是唯一的
    private final Lock lock = new ReentrantLock();

    public void drawMoney(double money) {
        // 1.先拿到是谁来取钱:取当前线程对象的名称
        String name = Thread.currentThread().getName();
        lock.lock(); // 上锁~!
        try{
            if(this.money >= money){
                // 3.余额足够
                System.out.println(name+"来取钱,吐出:"+money);
                // 4.更新余额
                this.money -= money;
                // 5.输出结果
                System.out.println(name+"来取钱"+money+"成功,取钱后剩余:"+this.money);
            }else{
                // 6.余额不足
                System.out.println(name+"来取钱,余额不足,剩余"+this.money);
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock(); // 解锁~!
        }

    }

线程通信

    public void wait(): 让当前线程进入到等待状态 此方法必须锁对象调用.
    public void notify() : 唤醒当前锁对象上等待状态的某个线程  此方法必须锁对象调用
    public void notifyAll() : 唤醒当前锁对象上等待状态的全部线程  此方法必须锁对象调用

模拟案例

// ThreadCommunication.java
public class ThreadCommunication {
    public static void main(String[] args) {
        Account account = new Account(0, "ICBC-111");
        Thread xiaohong = new DrawThread(account, "小红");
        Thread xiaoming = new DrawThread(account, "小明");
        Thread qindie = new SaveThread(account, "亲爹");
        Thread gandie = new SaveThread(account, "干爹");
        Thread yuefu = new SaveThread(account, "岳父");
        xiaohong.start();
        xiaoming.start();
        qindie.start();
        gandie.start();
        yuefu.start();
    }
}

//Account.java
public class Account {
    private int money;
    private String cardID;
    public synchronized void drawMoney(int money) {

        String name = Thread.currentThread().getName();
        try {
            if(this.money >= 10000){
                this.money -= money;
                System.out.println(name +":取钱成功,余额:"+this.money);
                this.notifyAll();
                this.wait();
            }else{
                System.out.println(name +":余额不足");
                this.notifyAll();
                this.wait();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public synchronized void saveMoney(int money) {
        String name = Thread.currentThread().getName();
        try {
            if(this.money >= 10000){
                System.out.println(name +":余额充足,余额:"+ this.money);
                this.notifyAll();
                this.wait();
            }else{
                this.money += money;
                System.out.println(name +":存钱成功,余额:"+ this.money);
                this.notifyAll();
                this.wait();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Account() {
    }

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public String getCardID() {
        return cardID;
    }

    public void setCardID(String cardID) {
        this.cardID = cardID;
    }

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

//DrawThread.java
public class DrawThread extends Thread {

    Account account;
    DrawThread(Account account,String name){
        super(name);
        this.account = account;
    }

    @Override
    public void run(){
        while (true){
            try {
                Thread.sleep(3000);
                account.drawMoney(10000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

//SaveThread.java
public class SaveThread extends Thread {
    private Account account;

    SaveThread(Account account,String name){
        super(name);
        this.account = account;
    }
    @Override
    public void run(){
        while (true){
            try {
                Thread.sleep(3000);
                account.saveMoney(10000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

线程状态

线程状态 导致状态发生条件
NEW(新建) 线程刚被创建,但是并未启动。还没调用start方法。MyThread t = new MyThread只有线程对象,没有线程特征。
Runnable(可运行) 线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。调用了t.start()方法 :就绪(经典教法)
Blocked(锁阻塞) 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
Waiting(无限等待) 一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
Timed Waiting(计时等待) 同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、Object.wait。
Teminated(被终止) 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。

线程池


创建线程池示例

public class ThreadPoolsDemo02 {
    public static void main(String[] args) {
        // a.创建一个线程池,指定线程的固定数量是3.
        // new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
        ExecutorService pools = Executors.newFixedThreadPool(3);
        // b.创建线程的任务对象。
        Runnable target = new MyRunnable();
        // c.把线程任务放入到线程池中去执行。
        pools.submit(target); // 提交任务,此时会创建一个新线程,自动启动线程执行!
        pools.submit(target); // 提交任务,此时会创建一个新线程,自动启动线程执行!
        pools.submit(target); // 提交任务,此时会创建一个新线程,自动启动线程执行!
        pools.submit(target); // 不会再创建新线程,会复用之前的线程来处理这个任务

        pools.shutdown(); // 等待任务执行完毕以后才会关闭线程池
        //pools.shutdownNow(); // 立即关闭线程池的代码,无论任务是否执行完毕!
    }
}

class MyRunnable implements Runnable{
    @Override
    public void run() {
        for(int i  = 0 ; i < 5 ; i++ ){
            System.out.println(Thread.currentThread().getName()+" => "+i);
        }
    }
}
public class TestCallableThreadPool {

    public static void main(String[] args) {
        // 创建一个线程池,固定线程数为3
        ExecutorService pools = Executors.newFixedThreadPool(3);

        Future<String> t1 = pools.submit(new MyCallable(50));
        Future<String> t2 = pools.submit(new MyCallable(100));
        Future<String> t3 = pools.submit(new MyCallable(200));
        Future<String> t4 = pools.submit(new MyCallable(300));

        try {
            System.out.println(t1.get());
            System.out.println(t2.get());
            System.out.println(t3.get());
            System.out.println(t4.get());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            pools.shutdown();
        }

    }

}

class MyCallable implements Callable<String>{

    private int n;

    MyCallable(int n){
        this.n = n;
    }

    @Override
    public String call() throws Exception {
        int sum = 0;
        for(int i = 1 ; i <= n ;i++){
            sum += i;
        }
        return Thread.currentThread().getName() + " - Sum = " + sum;
    }
}
上一篇 下一篇

猜你喜欢

热点阅读