2021-10-20

并发/多线程: 工作基本不用,面试使劲问

进程和线程 Java没有多进程,也不能操纵进程。 Java只有多线程

程序是死的,安装/存放在硬盘上而已。 进程:程序的一次动态执行过程就是进程。

随着程序越来越大,进程也就会占用更多的资源,CPU在对其进行调度时也就会越发吃力。 比如30万人移动基地。

正是由于以上原因,我们发明了更加轻量级别的进程,即线程。 一个进程可以派生出多个线程。

这种拆分后,进程不再被进行调度,而是成为了资源持有的单位。 线程反而成为了资源调度的单位(只带有一点点运行所必须的资源)

线程是轻量级的进程,线程是调度的基本单元,进程是资源持有的基本单元。

写多线程
1. 写一个任务,任务是多线程到底要做什么事情。
任务应该写一个类实现Runnable接口,将任务的具体操作放入这个接口的run方法中。

public class PrintChar implements Runnable {
    private char c;

    public PrintChar(char c) {
        this.c = c;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.printf("%-3c", c);
        }
    }
}
public class PrintNum implements Runnable {
    private int num;

    public PrintNum(int num) {
        this.num = num;
    }

    @Override
    public void run() {
        for (int i = 1; i <= num; i++) {
            System.out.printf("%-3d", i);
        }
    }
}

 start: 开始执行

构造一个Thread对象,将任务放入这个Thread对象

调用Thread对象的start方法

 public static void main(String[] args) {
        PrintChar printChar = new PrintChar('A');
        PrintNum printNum = new PrintNum(5);

//        printChar.run();
//        printNum.run();

        Thread t1 = new Thread(printChar); // 构造了一个线程对象,这个线程的任务是打印A
        Thread t2 = new Thread(printNum); // 构造了一个线程对象,但是这个线程并没有执行

        t1.start(); // start才是让这个线程去执行对应的任务
        // 这句话开始执行时,此时的程序
        t2.start();


    }
public class AccountWithoutSync {
    private static Account account = new Account();

    private static class Account{
        private int balance = 0; // 余额

        public int getBalance() { // 去除余额
            return balance;
        }

        public synchronized void deposit(int amount) { // 存amount金额的钱
            int newBalance = balance + amount;
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            balance = newBalance;
        }
    }

    public static class AddPennyTask implements Runnable {

        @Override
        public void run() {
            account.deposit(1);
        }
    }

    public static void main(String[] args) {
        // newCachedThreadPool根据需求开设线程
        ExecutorService executorService = Executors.newCachedThreadPool();

        for (int i = 0; i < 100; i++)
            executorService.execute(new AddPennyTask());

        executorService.shutdown();

        while (!executorService.isTerminated());
//        Thread.sleep(1000);
        System.out.println(account.getBalance());
    }
}

ExecutorService线程池

newCachedThreadPool:根据需求开设线程 

x.join(); // 插入
Thread.sleep(100); // 睡眠

用Lambda表达式改写上面的代码

public static void main(String[] args) {
        new Thread(() -> {
            Thread x = new Thread(new PrintChar('X'));
            x.start();
            for (int i = 1; i <= 10; i++) {
                if (i == 4){
                    try {
                        x.join(); // 插入
//                        Thread.sleep(100); // 睡眠
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
//                Thread.yield(); // 让出CPU给其他线程优先执行,但是并不绝对
                System.out.printf("%-3c", 'A');
            }
        }).start();
        new Thread(() -> {
            for (int i = 1; i <= 10; i++) {
                System.out.printf("%-3d", i);
            }
        }).start();
    }

Thread类中的常见方法:

  1. sleep :睡眠
  2. yield :让出CPU给其他线程先执行
  3. join ://插入,这个线程无论如何在被加塞的线程前优先做完。

sleep()和对象.join的结果一定是可以确定的

yield()的结果是完全不确定的

 public static void main(String[] args) {
        // ExecutorService 或者其父类接口 Executor 就被认为是线程池
        // Executors 是一个用于创建线程池的工具类
        // newFixedThreadPool 用于创建一个具有固定数量的线程池
        ExecutorService executor = Executors.newFixedThreadPool(3);

        // 一个三线程的线程池只是说可以同时开三个线程,多余的任务可以等等
        // execute方法类似于start方法,用于执行一个线程
        executor.execute(new PrintChar('A'));
        executor.execute(new PrintChar('B'));
        executor.execute(new PrintChar('C'));
        executor.execute(new PrintChar('D'));

        // shutdown用于关闭线程池,这个关闭虽然不是阻塞的,但是它会等待线程池中所有线程全部执行完毕后才关闭线程池
        executor.shutdown();
    }

线程同步: 让多个线程按照我们的要求有序执行

public class AccountWithoutSync {
    private static Account account = new Account();

    private static class AddPennyTask implements Runnable {
        @Override
        public void run() {
            account.deposit(1);
        }
    }
    
    private static class Account{
        private int balance = 0; //余额
        public int getBalance() { //取出余额
            return balance;
        }
        public void deposit(int amount) { //存amount金额的钱
            int newBalance = balance + amount;
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            balance = newBalance;
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        //newCachedThreadPool根据需求开设线程
        for (int i = 0; i < 100; i++)
            executorService.execute(new AddPennyTask());
    
        executorService.shutdown();
    
        while (!executorService.isTerminated());
        //isTerminated用于判断线程池是否已经全部做完
    
        System.out.println(account.getBalance());
    }

}
public class Account {
    private final Object o = new Object();
    private int balance = 0; //余额, 多个线程进行争抢的资源被称作“临界资源”
    public int getBalance() { //取出余额
        return balance;
    }
    public void deposit(int amount) { //存amount金额的钱
        //用于操纵临界资源的代码段被称作“临界区”
        synchronized (o) {  //同步块
            int newBalance = balance + amount;
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            balance = newBalance;
        }
    }
}
public class AddPennyTask implements Runnable{
    private final Account account;

    public AddPennyTask(Account account) {
        this.account = account;
    }
    
    @Override
    public void run() {
        account.deposit(1);
    }

}
public class AccountWithoutSync {

    public static void main(String[] args) throws InterruptedException {
        Account account = new Account();
    
        ExecutorService executorService = Executors.newCachedThreadPool();
        //newCachedThreadPool根据需求开设线程
        for (int i = 0; i < 100; i++)
            executorService.execute(new AddPennyTask(account));
    
        executorService.shutdown();
    
        while (!executorService.isTerminated());
        //isTerminated用于判断线程池是否已经全部做完
    
        System.out.println(account.getBalance());
    }

}

线程间协作:两个线程根据具体情况有条不紊的相互合作。

public class Account {
    private int balance = 0; //余额, 多个线程进行争抢的资源被称作“临界资源”
    private Lock lock = new ReentrantLock(); //获得一把锁
    private Condition condition = lock.newCondition(); //锁的条件,也叫条件锁

    public int getBalance() { //取出余额
        return balance;
    }
    
    public void withdraw(int amount) {
        lock.lock();
        try{
            while(balance < amount) {
                System.out.printf("\t\t\t现在我打算取%d元,但是余额不足,暂时等待\n", amount);
                condition.await(); //await将当前这个线程自我阻塞
            }
            balance -= amount;
            System.out.printf("\t\t\t已经取了%d元钱,账户余额是%d.\n", amount,  getBalance());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    
    public void deposit(int amount) {
        lock.lock();
        try {
            balance += amount;
            System.out.println("已经存入" + amount);
            condition.signalAll(); //signal用于随机释放一个正在阻塞的进程,加all用于释放所有被阻塞的进程
        } finally {
            lock.unlock();
        }
    }

}

public class DepositTask implements Runnable{
    private final Account account;

    public DepositTask(Account account) {
        this.account = account;
    }
    
    @Override
    public void run() {
        while(true) {
            account.deposit((int)(Math.random() * 10));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

public class WithdrawTask implements Runnable{
    private final Account account;

    public WithdrawTask(Account account) {
        this.account = account;
    }
    
    @Override
    public void run() {
        while(true)
            account.withdraw((int)(Math.random() * 10));
    }

}

public class AccountWithoutSync {

    public static void main(String[] args) throws InterruptedException {
        Account account = new Account();
    
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.execute(new DepositTask(account));
        executorService.execute(new WithdrawTask(account));
        executorService.shutdown();
    
    }

}

使用synchronized改写上面的代码

public class Account {
    private int balance = 0; //余额, 多个线程进行争抢的资源被称作“临界资源”

    public int getBalance() { //取出余额
        return balance;
    }
    
    public synchronized void withdraw(int amount) {
        try {
            while (balance < amount) {
                System.out.printf("\t\t\t现在我打算取%d元,但是余额不足,暂时等待\n", amount);
                wait();
            }
            balance -= amount;
            System.out.printf("\t\t\t已经取了%d元钱,账户余额是%d.\n", amount, getBalance());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    public synchronized void deposit(int amount) {
        balance += amount;
        System.out.println("已经存入" + amount);
        notifyAll();
    }

}

改成同步块之后的版本:

public class Account {
    private int balance = 0; //余额, 多个线程进行争抢的资源被称作“临界资源”
    public int getBalance() { //取出余额
        return balance;
    }

    public  void withdraw(int amount) {
        synchronized (this) {
            try {
                while (balance < amount) {
                    System.out.printf("\t\t\t现在我打算取%d元,但是余额不足,暂时等待\n", amount);
                    wait();
                }
                balance -= amount;
                System.out.printf("\t\t\t已经取了%d元钱,账户余额是%d.\n", amount, getBalance());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public synchronized void deposit(int amount) {
        synchronized (this) {
            balance += amount;
            System.out.println("已经存入" + amount);
            notifyAll();
        }
    }

}

生产者和消费者

public class Buffer {
    private static final int CAPACITY = 1;
    private final Queue<Integer> queue = new ArrayDeque<>(CAPACITY);
    private final Lock lock = new ReentrantLock();
    private final Condition write = lock.newCondition();
    private final Condition read = lock.newCondition();
    

    public void write(int value) {
        lock.lock();
        try{
            while(queue.size() == CAPACITY) {
                System.out.println("生产者现在暂停");
                write.await();
            }
            //程序如果能执行到此处,就意味着仓库中是不满的,可以生产
            queue.offer(value);
            read.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    } 
    
    public int read() {
        int value = 0;
        lock.lock();
        try{
            while (queue.isEmpty()) {
                System.out.println("\t\t\t仓库中为空,暂停消费");
                read.await();
            }
            //程序运行到此处,意味着仓库中非空,可以消费,同时也可以生
            value = queue.poll();
            write.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return value;
    }

}
public class ProducerTask implements Runnable { private Buffer buffer;

public ProducerTask(Buffer buffer) {
    this.buffer = buffer;
}
​
@Override
public void run() {
    int i = 1;
    while(true) {
        buffer.write(i);
        System.out.println("生产了对象:" + i++);
        try {
            Thread.sleep((int)(Math.random()*10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

}
public class ConsumerTask implements Runnable{ private Buffer buffer;

public ConsumerTask(Buffer buffer) {
    this.buffer = buffer;
}
​
@Override
public void run() {
    while(true) {
        System.out.println("\t\t\t消费者消费了: " + buffer.read());
        try {
            Thread.sleep((int)(Math.random()*10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

}
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;

public class Test { public static void main(String[] args) { Buffer buffer = new Buffer(); ExecutorService executorService = Executors.newFixedThreadPool(2); executorService.execute(new ProducerTask(buffer)); executorService.execute(new ConsumerTask(buffer)); executorService.shutdown(); } }

阻塞队列 删除Buffer类,变成阻塞队列。 package com.example;

import java.util.concurrent.ArrayBlockingQueue;

public class ProducerTask implements Runnable { private ArrayBlockingQueue<Integer> buffer;

public ProducerTask(ArrayBlockingQueue<Integer>  buffer) {
    this.buffer = buffer;
}
​
@Override
public void run() {
    int i = 1;
    while(true) {
        try {
            buffer.put(i);
            System.out.println("生产者生产了对象:" + i++);
            Thread.sleep((int)(Math.random()*10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

}
import java.util.concurrent.ArrayBlockingQueue;

public class ConsumerTask implements Runnable{ private ArrayBlockingQueue<Integer> buffer;

public ConsumerTask(ArrayBlockingQueue<Integer> buffer) {
    this.buffer = buffer;
}
​
@Override
public void run() {
    while(true) {
        try {
            System.out.println("\t\t\t消费者消费了: " + buffer.take());
            Thread.sleep((int)(Math.random()*10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

}
public class Test { 
    public static void main(String[] args) { 
        ArrayBlockingQueue<Integer> buffer = new ArrayBlockingQueue<>(1);         
        ExecutorService executorService = Executors.newFixedThreadPool(2);             
        executorService.execute(new ProducerTask(buffer)); 
        executorService.execute(new ConsumerTask(buffer)); 
        executorService.shutdown(); } 
    }

阻塞队列

删除Buffer类,变成阻塞队列

public class ProducerTask implements Runnable { private ArrayBlockingQueue<Integer> buffer;

public ProducerTask(ArrayBlockingQueue<Integer>  buffer) {
    this.buffer = buffer;
}
​
@Override
public void run() {
    int i = 1;
    while(true) {
        try {
            buffer.put(i);
            System.out.println("生产者生产了对象:" + i++);
            Thread.sleep((int)(Math.random()*10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 

public class ConsumerTask implements Runnable{ private ArrayBlockingQueue<Integer> buffer;

public ConsumerTask(ArrayBlockingQueue<Integer> buffer) {
    this.buffer = buffer;
}
​
@Override
public void run() {
    while(true) {
        try {
            System.out.println("\t\t\t消费者消费了: " + buffer.take());
            Thread.sleep((int)(Math.random()*10000));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 

 

public class Test { 
    public static void main(String[] args) { 
        ArrayBlockingQueue<Integer> buffer = new ArrayBlockingQueue<>(1);     
        ExecutorService executorService = Executors.newFixedThreadPool(2);         
        executorService.execute(new ProducerTask(buffer)); 
        executorService.execute(new ConsumerTask(buffer)); 
        executorService.shutdown(); 
    } 
}

信号量

信号量就好比是一把锁有1个或多个钥匙。 比如一个被信号量包起来的代码(类似于同步块)可以限制有几个线程能够同时访问。

死锁:多个线程为了争抢有限的资源,从而导致谁都没办法获得全部资源得以运行。 哲学家就餐

线程的几种状态及其之间的转换

  1. 新建(新生)——一个线程被新建,就是new一个Thread
  2. 就绪——一个线程已经拥有了运行所需要的全部资源,唯独只差CPU yield()
  3. 运行——同一时间只能有一个线程是运行的。 run()
  4. 阻塞(等待)——线程因为缺乏某种资源导致无法就绪 4.1 等待目标完成——某个线程在等待某个事件结束,join加塞的必须先做完 4.2 等待超时——某个线程因为时间问题导致无法运行,sleep睡眠 4.3 等待通知——等待其余线程通知其继续运行,wait自我阻塞
  5. 结束(死亡)——线程运行结束了

操作系统中规定,只有就绪态才能转为运行态 在操作系统中,会有很多线程处于就绪状态,这些线程通常都被放在一个就绪队列中

public class AddTask implements Runnable{ private List<Integer> list; AtomicInteger num;

public AddTask(List<Integer> list, AtomicInteger num) {
    this.list = list;
    this.num = num;
}
​
@Override
public void run() {
    System.out.println(num.incrementAndGet());
    for (int i = 0; i < list.size(); i++) {
        list.set(i, list.get(i) + 1); //+1操作不是多线程安全的,此处只有get和set方法才是多线程安全的
    }
}

 

public class Test { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1);list.add(1);list.add(1); AtomicInteger num = new AtomicInteger(0);

    List<Integer> syncList = Collections.synchronizedList(list);
​
    ExecutorService executorService = Executors.newFixedThreadPool(100);
    for (int i = 0; i < 100; i++) {
        executorService.execute(new AddTask(syncList, num));
    }
    executorService.shutdown();
​
    while(!executorService.isShutdown());
​
    System.out.println(num);


}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值