一、线程安全
1.什么是线程安全
2.用程序模拟线程安全问题
public class Account {
private double money;//余额
private String cardId;//卡号
public Account() {
}
public Account(double money, String cardId) {
this.money = money;
this.cardId = cardId;
}
public void drawMoney(double money){
//先搞清楚是谁来取钱
String name = Thread.currentThread ().getName ();
//1.判断账户金额是否足够
if (this.money>=money){
System.out.println (name+"开始取钱");
this.money-=money;
System.out.println (name+"取钱成功后余额为"+this.money);
}else {
System.out.println ("未取钱余额不足");
}
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public String getCardId() {
return cardId;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
}
public class MyThread extends Thread{
private Account acc;
public MyThread(Account acc,String name){
super ( name );
this.acc=acc;
}
@Override
public void run() {
// if (acc.getMoney ()>=100000.0){
// System.out.println (Thread.currentThread ().getName ()+"开始取钱");
// acc.setMoney (acc.getMoney ()-100000.0);
// System.out.println (Thread.currentThread ().getName ()+"取钱成功");
// System.out.println ( Thread.currentThread ().getName ()+"取钱后账户余额"+acc.getMoney () );
// }else {
// System.out.println ("账户余额不足");
// }
acc.drawMoney ( 100000.0 );
}
}
public class ThreadTest {
public static void main(String[] args) {
//1.创建一个账户类对象代表两个人的共享账户
Account acc = new Account ( 100000.0, "787878787877" );
//2.创建两个线程,分别表示小明,小红,两人同时去一个账户中取钱10w
Thread m1 = new MyThread ( acc,"小明" );
Thread m2 = new MyThread ( acc,"小红" );
m1.start ();
m2.start ();
}
}
3.如何解决线程安全问题?
1.线程同步
解决方案:加锁
2.如何实现线程同步(加锁)
1.同步代码块
public class Account {
private double money;//余额
private String cardId;//卡号
public Account() {
}
public Account(double money, String cardId) {
this.money = money;
this.cardId = cardId;
}
public void drawMoney(double money){
//先搞清楚是谁来取钱
String name = Thread.currentThread ().getName ();
//1.判断账户金额是否足够
synchronized (this) {//同步代码块上锁 推荐用共享资源作为锁(this)
if (this.money>=money){
System.out.println (name+"开始取钱");
this.money-=money;
System.out.println (name+"取钱成功后余额为"+this.money);
}else {
System.out.println (name+"未取钱余额不足");
}
}
}
public static void test(){ //静态方法推荐用类名上锁
synchronized (Account.class){
}
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public String getCardId() {
return cardId;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
}
2.同步方法
public synchronized void drawMoney(double money){
//先搞清楚是谁来取钱
String name = Thread.currentThread ().getName ();
//1.判断账户金额是否足够
if (this.money>=money){
System.out.println (name+"开始取钱");
this.money-=money;
System.out.println (name+"取钱成功后余额为"+this.money);
}else {
System.out.println (name+"未取钱余额不足");
}
}
3.Lock锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Account {
private double money;//余额
private String cardId;//卡号
//创建一个锁对象
private final Lock lk=new ReentrantLock ();//final 保护锁对象
public Account() {
}
public Account(double money, String cardId) {
this.money = money;
this.cardId = cardId;
}
public void drawMoney(double money){
//先搞清楚是谁来取钱
String name = Thread.currentThread ().getName ();
//1.判断账户金额是否足够
lk.lock ();//加锁
try {
if (this.money>=money){
System.out.println (name+"开始取钱");
this.money-=money;
System.out.println (name+"取钱成功后余额为"+this.money);
}else {
System.out.println (name+"未取钱余额不足");
}
} catch (Exception e) {
e.printStackTrace ();
} finally {
lk.unlock ();//解锁
}
}
public static void test(){ //静态方法推荐用类名上锁
synchronized (Account.class){
}
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public String getCardId() {
return cardId;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
}
二、线程通信(前提保证线程安全)
1.什么是线程通信
2.代码实例
public class ThreadTest {
public static void main(String[] args) {
Desk desk=new Desk ();
//创建3个生产者线程
new Thread ( ()-> {
while (true) {
desk.put();
}
},"厨师1" ).start ();
new Thread ( ()-> {
while (true) {
desk.put();
}
},"厨师2" ).start ();
new Thread ( ()-> {
while (true) {
desk.put();
}
},"厨师3" ).start ();
//创建两个消费者线程
new Thread(()->{
while (true) {
desk.get();
}
},"吃货1").start ();
new Thread(()->{
while (true) {
desk.get();
}
},"吃货2").start ();
}
}
import java.util.ArrayList;
import java.util.List;
public class Desk {
private List<String> list =new ArrayList<> ();
//放包子
//厨师1,厨师2,厨师3
public synchronized void put() {
try {
String name = Thread.currentThread ().getName ();
//判断是否由包子
if(list.size ()==0){
list.add ( name+"做的肉包子" );
System.out.println (name+"做了个肉包子");
Thread.sleep ( 2000 );
//唤醒别人,等待自己,(用锁对象调用)
this.notifyAll ();//先唤醒 后等待
this.wait ();
}else {
//唤醒别人,等待自己,(用锁对象调用)
this.notifyAll ();//先唤醒 后等待
this.wait ();
}
} catch (Exception e) {
e.printStackTrace ();
}
}
//取包子
//吃货1,吃货2
public synchronized void get() {
try {
String name = Thread.currentThread ().getName ();
if (list.size ()==1){
//有包子,吃掉
System.out.println (name+"吃了"+list.get ( 0 ));
list.clear ();
Thread.sleep ( 1000 );
//唤醒别人,等待自己,(用锁对象调用)
this.notifyAll ();//先唤醒 后等待
this.wait ();
}else {
//没有包子
//唤醒别人,等待自己,(用锁对象调用)
this.notifyAll ();//先唤醒 后等待
this.wait ();
}
} catch (Exception e) {
e.printStackTrace ();
}
}
}
三、线程池
1.什么是线程池
2.线程池的工作原理
控制线程的数量,也控制任务的数量,避免系统创建太多线程,耗费大量的系统资源。挺高系统性能。
3.如何创建线程池
1.方式1(ThreadPoolExecutor对象创建)
import java.util.concurrent.*;
public class ThreadPoolTest {
public static void main(String[] args) {
// public ThreadPoolExecutor(int corePoolSize,
// int maximumPoolSize,
// long keepAliveTime,
// TimeUnit unit,
// BlockingQueue<Runnable> workQueue) {
// this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
// Executors.defaultThreadFactory(), defaultHandler);
//1.通过ThreadPoolExecutor 创建一个线程池对象
ExecutorService pool= new ThreadPoolExecutor ( 3,5,8,TimeUnit.SECONDS,
new ArrayBlockingQueue<> ( 4 ),Executors.defaultThreadFactory (),new ThreadPoolExecutor.AbortPolicy ());
}
}
2.方式2(Executors创建)
import java.util.concurrent.*;
public class Test {
public static void main(String[] args) throws Exception {
// //1.通过ThreadPoolExecutor 创建一个线程池对象
// ExecutorService pool= new ThreadPoolExecutor ( 3,5,8, TimeUnit.SECONDS,
// new ArrayBlockingQueue<> ( 4 ), Executors.defaultThreadFactory (),new ThreadPoolExecutor.AbortPolicy ());
//1-2 通过Executors创建一个线程对象。
ExecutorService pool = Executors.newFixedThreadPool ( 3 );//新建固定数量线程的线程池
//计算密集型任务:核心线程数量=CPU核数+1;
//IO密集型任务:核心线程数量=CPU核数*2;
Executors.newSingleThreadExecutor ();//新建单个线程数量的线程池
//2.使用线程处理Callable 任务类
Future<String> f1 = pool.submit ( new MyCallable ( 100 ) );//返回未来任务对象(结果)
Future<String> f2 = pool.submit ( new MyCallable ( 200 ) );
Future<String> f3 = pool.submit ( new MyCallable ( 300 ) );
Future<String> f4 = pool.submit ( new MyCallable ( 400 ) );
System.out.println ( f1.get () );
System.out.println ( f2.get () );
System.out.println ( f3.get () );
System.out.println ( f4.get () );
}
}
3.注意事项
4.线程池处理Runnable任务
1.常用方法
2.新任务的拒绝策略
3.代码
public class MyRunnable implements Runnable{
@Override
public void run() {
//描述任务
System.out.println (Thread.currentThread ().getName ()+"====>666");
try {
Thread.sleep ( Integer.MAX_VALUE );
} catch (InterruptedException e) {
e.printStackTrace ();
}
}
}
import java.util.concurrent.*;
public class ThreadPoolTest {
public static void main(String[] args) {
// public ThreadPoolExecutor(int corePoolSize,
// int maximumPoolSize,
// long keepAliveTime,
// TimeUnit unit,
// BlockingQueue<Runnable> workQueue) {
// this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
// Executors.defaultThreadFactory(), defaultHandler);
//1.通过ThreadPoolExecutor 创建一个线程池对象
ExecutorService pool= new ThreadPoolExecutor ( 3,5,8,TimeUnit.SECONDS,
new ArrayBlockingQueue<> ( 4 ),Executors.defaultThreadFactory (),new ThreadPoolExecutor.AbortPolicy ());
Runnable target = new MyRunnable ();
pool.execute ( target );//把任务交给线程池,线程池会自动创建一个新的线程,自动处理这个任务,自动执行。
pool.execute ( target );
pool.execute ( target );
//临时线程的创建时机
pool.execute ( target );//复用前面的核心线程执行任务
pool.execute ( target );
//开始进入任务队列
pool.execute ( target );
pool.execute ( target );
pool.execute ( target );
pool.execute ( target );
//开始拒绝策略(任务队列占满)
pool.execute ( target );//拒绝直接抛出错误
// pool.shutdown ();//等线程池的任务全部执行完毕后,才会关闭线程池
// pool.shutdownNow ();//立刻关闭线程池,不管任务是否执行完毕
}
}
5.线程池处理Callable任务
1.常用方法
2.代码
import java.util.concurrent.Callable;
public class MyCallable implements Callable<String> {
private int n;
public MyCallable() {
}
public MyCallable(int n) {
this.n = n;
}
@Override
public String call() throws Exception {
int sum=0;
for (int i = 0; i <=n; i++) {
sum+=i;
}
return Thread.currentThread ().getName ()+"求出了1-"+n+"的和是:"+sum;
}
}
import java.util.concurrent.*;
public class Test {
public static void main(String[] args) throws Exception {
//1.通过ThreadPoolExecutor 创建一个线程池对象
ExecutorService pool= new ThreadPoolExecutor ( 3,5,8, TimeUnit.SECONDS,
new ArrayBlockingQueue<> ( 4 ), Executors.defaultThreadFactory (),new ThreadPoolExecutor.AbortPolicy ());
//2.使用线程处理Callable 任务类
Future<String> f1 = pool.submit ( new MyCallable ( 100 ) );//返回未来任务对象(结果)
Future<String> f2 = pool.submit ( new MyCallable ( 200 ) );
Future<String> f3 = pool.submit ( new MyCallable ( 300 ) );
Future<String> f4 = pool.submit ( new MyCallable ( 400 ) );
System.out.println ( f1.get () );
System.out.println ( f2.get () );
System.out.println ( f3.get () );
System.out.println ( f4.get () );
}
}
四、并发、并行
1.进程和线程的关系
2.什么是并发?
3.什么是并行?
4.多线程是怎么执行的?
五、线程的生命周期
人的生命周期
sleep(不会释放锁) wait(会释放锁)
六、悲观锁和乐观锁
1.悲观锁:
一上来就加锁,没有安全感,每次只能一个线程进入访问完毕后,再关锁。线程安全,性能较差!
public class MyRunnable implements Runnable{
private int count;
@Override
public void run() {
for (int i = 0; i <100; i++) {
//悲观锁
synchronized (this) {
System.out.println ("count=====>"+ (++count));
}
}
}
}
public class Test1 {
public static int Number;
public static void main(String[] args) {
//悲观锁,乐观锁的原理
//悲观锁:一上来就加锁,没有安全感,每次只能一个线程进入访问完毕后,再关锁。线程安全,性能较差!
//乐观锁:一开始不上锁,认为是没有问题的,大家一起跑,等要出现线程安全问题时才开始控制。线程安全,性能较好!
//需求:1个变量,100个线程,每个线程对其加100次
Runnable target =new MyRunnable ();
for (int i = 1; i <=100; i++) {
new Thread (target).start ();
}
}
}
2.乐观锁
利用原子类实现
乐观锁:一开始不上锁,认为是没有问题的,大家一起跑,等要出现线程安全问题时才开始控制。线程安全,性能较好!
public class MyRunnable2 implements Runnable{
//乐观锁 CAS算法 比较(Compare)A(and)S(修改Set) 算法
//整数修改的乐观锁:原子类实现的
private AtomicInteger count=new AtomicInteger ();
@Override
public void run() {
for (int i = 0; i <100; i++) {
System.out.println ("count=====>"+ count.incrementAndGet ());//先加1然后再返回值
}
public class Test2 {
public static void main(String[] args) {
//悲观锁,乐观锁的原理
//悲观锁:一上来就加锁,没有安全感,每次只能一个线程进入访问完毕后,再关锁。线程安全,性能较差!
//乐观锁:一开始不上锁,认为是没有问题的,大家一起跑,等要出现线程安全问题时才开始控制。线程安全,性能较好!
//需求:1个变量,100个线程,每个线程对其加100次
Runnable target =new MyRunnable2 ();
for (int i = 1; i <=100; i++) {
new Thread (target).start ();
}
}
}