简述
java的重要功能之一就是内部支持多线程——在一个程序中允许同时允许多个任务。多线程能使程序反应更快,交互性更强,执行效率更高。
创建任务
在java中,每个任务都是Runnable接口的一个实例,也称为可运行对象,线程本质上讲的就是便于任务执行的对象。
一个任务类除了实现Runnable接口,还需要有run方法,这个方法告诉系统线程将如何运行。
一旦定义了一个TaskClass,你就可以用它的构造方法创建一个任务,例如TaskClass task = new TaskClass(...); 任务必须在线程中执行,Thread类包括创建线程的构造方法以及控制线程的很多有用的方法。使用Thread thread = new Thread(task);创建任务的线程,然后调用start方法告诉java虚拟机该线程准备运行,JVM通过调用任务的run方法执行任务。
我们定义打印100个数字的任务类和打印100个字母的任务类,然后在test类里创建这两个线程并启动。
运行一下我们可以看到执行的过程并不是先执行完一个再执行另一个的,而是共享cpu资源,随时可能中断。
Thread类
Thread类包含为任务而创建的线程构造方法,以及控制线程的方法。
线程池
如果我们为每一个任务创建一个线程,这样就对解决大量的任务而言是不够高效的。
Java提供Executor接口来执行线程池中的任务,提供ExecutorService接口来管理和控制任务,后者是前者的子接口。
为了创建Excutor对象,可以使用Excutors类中的静态方法——newFixedThreadPool(int)方法在池中创建固定数目的线程,当线程完成了任务的执行,它可以被重新使用以执行另一个任务。
线程安全
如果一个共享资源被多个线程同时进行读写操作,就可能发生线程安全问题。
如果有个任务是往账户里添加一块钱,如果我们执行100个该线程
执行后账户总额应该为100,但无论运行多少次主线程,余额都不对
那是因为多个线程同时对balance变量读写,发生中断会产生线程安全问题。
利用synchronized锁确保线程安全
为了确保线程安全,就要防止多个线程进入对共享资源的操作区,我们称为临界区。
像上面存钱的例子的临界区就是deposit方法,我们可以通过为deposit方法添加关键字synchronized,让这个方法变成同步的,同时这个类也变成线程安全的了。
通过为这个方法加锁,当有一个线程进入deposit方法时,会锁住这个资源,不让其他线程进入,其他线程将被堵塞,直到解锁。
synchronized在返回值前加
每个锁都是Lock的一个具体实现,除了synchronized锁还有ReentrantLock,该锁可以创建具有特定的公平策略的锁,公平策略为真,则谁等得久谁获得锁,否则将锁给任意一个在等待的线程。
我们在改改刚刚的账户代码,在account类中定义一个静态reentrantlock变量,用于despoit方法的控制。
然后在临界区前加锁,临界区结束时释放锁。
线程间协作
通过锁可以实现线程之间的同步,线程之间的协作则需要通过条件实现。
条件是通过Lock对象newCondition方法创建的对象,一旦创建了条件,就可以使用await,signal,signall方法来实现线程之间的相互通信。
我们用一个例子来演示线程之间的协作,我们创建两个任务一个用来向账户中存款,一个从同一个账户提款。当提款的数额大于当前余额时,提款线程需等待。只要存款都要通知提款线程。
为了同步这些操作,我们使用一个具有条件的锁newDeposit,如果余额小于取款数额,提款任务将等待newDeposit条件。当存款任务给账户增加资金时,存款任务将唤醒等待中的提款任务再次尝试。
下面是运行代码,通过条件newDeposit,实现了取款和存款的协作
package ThreadCollaboration; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @author Layton * @version 1.0 * @date 2020/10/1 21:54 */ public class ThreadCollaboration { private static Account account = new Account(); public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(2); executor.execute(new DepositTask()); executor.execute(new WithdrawTask()); executor.shutdown(); System.out.println("sos"); } public static class DepositTask implements Runnable{ @Override public void run() { try{ while(true){ account.deposit(5); Thread.sleep(1000); } }catch (InterruptedException e){ e.printStackTrace(); } } } public static class WithdrawTask implements Runnable{ @Override public void run() { while(true){ account.withdraw(10); } } } private static class Account{ private static Lock lock = new ReentrantLock(); private static Condition newDeposit = lock.newCondition(); private int balance = 0; public int getBalance() { return balance; } private void withdraw(int amout){ lock.lock(); try{ while(balance < amout){ System.out.println("wait"); newDeposit.await(); } balance -= amout; System.out.println("withdraw" + amout +" "+ getBalance()); }catch(InterruptedException e){ e.printStackTrace(); } finally { lock.unlock(); } } private void deposit(int amount){ lock.lock(); try{ balance += amount; System.out.println("Deposit" + amount +" "+ getBalance()); newDeposit.signalAll(); }finally { lock.unlock(); } } } }
断断续续写了一周的多线程,其实也没多少内容,这么就写了那么久了呢,大概是床天天在诱惑我躺上去。