JavaSE-09 Thread 多线程(完整版)

男儿欲遂平生志,五经勤向窗前读。这篇文章主要讲述JavaSE-09 Thread 多线程(完整版)相关的知识,希望能为你提供帮助。
javaSE-09 Thread 多线程 1. 线程简介 1.1 普通方法调用和多线程

JavaSE-09 Thread 多线程(完整版)

文章图片

1.2 程序、进程、线程
  • 程序跑起来编程进程,进程里面分为若干个线程 :例如main函数就是主线程(是系统入口,用于执行多个程序),gc垃圾回收机制也是一个线程
  • 多线程是模拟出来的,真正的多线程是指很多cpu,即多核,但是因为cpu执行代码切换的很快,所以有同时执行的感觉
  • 多个线程是由调度器安排调度与操作系统相关的,控制到cpu先后顺序
  • 对同一个资源操作时,会发生资源抢夺的问题,需要加入并发控制
2. 线程创建:com.fenfen.Thread.Demo1 2.1继承Thread类
三步走:
1.自定义线程类继承Thread类
2.重写run()方法,编写线程执行力
3.创建线程对象,调用start()方法启动线程
public class TestTread1 extends Thread@Override public void run() //run 方法线程体 for (int i = 0; i < 20; i++) System.out.println("我在通宵肝代码---"+i); public static void main(String[] args) //main方法,主线程//创建一个线程对象,并调用start方法 TestTread1 testTread1 = new TestTread1(); testTread1.start(); //如果是run方法就是正常的先跑run方法上面的 testTread1.run(); for (int i = 0; i < 200; i++) System.out.println("我在学习多线程---"+i); /* 输出结果是交替执行的,由cpu调度执行 */

2.2 用继承thread实现网图下载的多线程
学完io流后记得补代码

2.3 实现runnable接口
三步走:
1.定义MyRunnable类实现Runnable接口
2.实现run()方法,编写线程执行体
3.创建线程对象,传入目标对象+调用start()方法启动线程
public class TestThread3 implements Runnable @Override public void run() //run 方法线程体 for (int i = 0; i < 20; i++) System.out.println("我在通宵肝代码---"+i); public static void main(String[] args) //main方法,主线程//创建Runnable接口实现类对象,并调用start方法 TestThread3 testTread3 = new TestThread3(); //创建线程对象,通过线程对象来开启我们的线程,代理 Thread thread = new Thread(testTread3); thread.start(); //new Thread(testTread3).start(); 或者直接一句这个for (int i = 0; i < 200; i++) System.out.println("我在学习多线程---"+i); /* 1、去看源码发现,本质是因为:Thread也实现了Runnable接口,Runnable就一个run方法在里面 2、继承是单继承,推荐使用Runnable方法 */

2.4 初始并发问题
多个线程操作同一个资源的情况下,并发出现问题,线程不安全了,数据紊乱
public class TestThread4 implements Runnable//票数 private int ticketnums = 10; @Override public void run() while (true) if (ticketnums< =0) break; //模拟延迟sleep try Thread.sleep(200); catch (InterruptedException e) e.printStackTrace(); System.out.println(Thread.currentThread().getName()+"--> 拿到了第"+ticketnums--+"票"); public static void main(String[] args) TestThread4 ticket = new TestThread4(); new Thread(ticket,"小明").start(); new Thread(ticket,"小芬").start(); new Thread(ticket,"黄牛党").start();

2.5 利用多线程实现龟兔赛跑
思路:
1.fori循环
2.方法用boolean写一个判断是否完成比赛,传递i过去
3.比赛结束跳出循环
4.新建两个线程调用
5.让兔子线程休息,记得try和catch一下
public class Race implements Runnable//胜利者 private static String winner; @Override public void run() for (int i = 0; i < = 100; i++) //模拟兔子休息sleep if(Thread.currentThread().getName().equals("兔子")& & i%10==0)try Thread.sleep(1); catch (InterruptedException e) e.printStackTrace(); //判断比赛是否结束 boolean flag = gameover(i); //如果比赛结束了,就停止 if(flag) break; System.out.println(Thread.currentThread().getName()+"跑了"+i+"步"); //判断完成比赛 private boolean gameover(int steps) //判断是否有胜利者 if (winner!=null) return true; if (steps > =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(); new Thread(race,"兔子").start(); new Thread(race,"乌龟").start();

2.6 实现Callable接口
好几步走:
1.实现Callable接口,需要返回值类型
2.重写call方法,需要抛出异常
3.创建目标对象
4.创建执行服务
5.提交执行
6.获取结果
7.关闭服务
了解就好,如果以后用到再学,再来补,先鸽一下(狗头)
2.7 静态代理模式:com.fenfen.Thread.Demo2
思路:
1.两个类都改写接口的方法
2.将真实对象通过参数传进去(构造器),代理对象从而代理真实角色
好处:
代理对象可以做很多真实对象做不了的事情,真实对象专注做自己的事情
就是线程的底部原理
public class StaticProxy public static void main(String[] args) You you = new You(); //这边用lambda表达式表示:Thread代理一个真实的Runnable接口,并且调用了start方法 new Thread(()-> System.out.println("我爱你")).start(); //或者精简成new WeddingCompany(new You()).HappyMarry(); WeddingCompany weddingCompany = new WeddingCompany(new You()); weddingCompany.HappyMarry(); interface Marry void HappyMarry(); //真实角色 class You implements Marry @Override public void HappyMarry() System.out.println("要结婚啦"); //代理角色,帮助 class WeddingCompany implements Marryprivate Marry target; public WeddingCompany(Marry target) this.target = target; @Override public void HappyMarry() before(); this.target.HappyMarry(); //这就是真实对象 after(); private void after() System.out.println("结婚之后,收尾款"); private void before() System.out.println("结婚之前,布置现场");

2.8 Lambda表达式:com.fenfen.Thread.lamdba
2.8.1 基本内容
  1. Lambda表达式属于函数式编程
  2. 例如:a-> System.out.println(" 我在学习多线程-> " +i);
  3. 函数式接口的定义:任何接口只包含了一个抽象方法,那就是函数式接口,就可以通过lambda表达式来创建该接口的对象
    2.8.2 简略代码的方法
  4. 静态内部类
public class TestLambda2 //利用静态内部类的方式:加上static static class Like11 implements ILike1 @Override public void lambda() System.out.println("i like lambda1"); public static void main(String[] args) Like11 like11 = new Like11(); like11.lambda(); //1、定义一个函数式接口 interface ILike1 void lambda();

  1. 局部内部类
public class TestLambda3 public static void main(String[] args) class Like3 implements ILike3 @Override public void lambda() System.out.println("i like lambda3"); Like3 like3 = new Like3(); like3.lambda(); //1、定义一个函数式接口 interface ILike3 void lambda();

  1. 匿名内部类:没有类的名称
public class TestLambda4 public static void main(String[] args) ILike4 like4 = new ILike4() @Override public void lambda() System.out.println("i like lambda4"); ; like4.lambda(); //1、定义一个函数式接口 interface ILike4 void lambda();

  1. 用lambda简化
public class TestLambda5 public static void main(String[] args) ILike5 like5= ()-> System.out.println("i like lambda5"); ; like5.lambda(); //1、定义一个函数式接口 interface ILike5 void lambda();

再写一个
  1. 正常接口代码2
public class TestLambda6 public static void main(String[] args) Love love = new Love(); love.love(666); interface Ilove void love(int a ); class Love implements Ilove @Override public void love(int a) System.out.println("i love life--> "+a);

  1. 匿名内部类2
public class TestLambda7 public static void main(String[] args) Ilove1 ilove1 = new Ilove1()//记得改成接口的类 @Override public void love(int a) System.out.println("i love life--> "+a); ; ilove1.love(888); interface Ilove1 void love(int a );

7.用lambda简化2
public static void main(String[] args) Ilove2 ilove2 = (int a)-> System.out.println("i love life--> "+a); ; //再简化:①去掉参数类型 ilove2 = (a)-> System.out.println("i love life--> "+a); ; //再简化:把括号都简化没了 ilove2 = a-> System.out.println("i love life--> "+a); ilove2.love(888); interface Ilove2 void love(int a );

  1. 用lambda简化多个参数
public class TestLambda9 public static void main(String[] args) Ilove3 ilove3 = null; ilove3 = (a,b)-> System.out.println("i love you--> "+a+" "+b); ; ilove3.love(520,1314); /* 多个参数也可以去掉参数类型,要去掉就全部去掉,并且带上括号 */interface Ilove3 void love(int a,int b );

3. 线程状态 3.1线程五个状态
JavaSE-09 Thread 多线程(完整版)

文章图片

3.2 线程方法
有优先级,休眠,加入,暂停,停止,是否存活
JavaSE-09 Thread 多线程(完整版)

文章图片

3.2.1 线程停止stop:com.fenfen.Thread.ThreadStopJDK一般不建议使用它自己本身的方法停止线程,一般会建立一个标志位进行终止变量
public class TestStop implements Runnable //1、设置一个标志位 private boolean flag = true; @Override public void run() int i = 0; while (flag) System.out. println("run....Thread"+i++); //2、设置一个公开的方法停止线程,转换标志位,方便调用的public void stop() this.flag = false; public static void main(String[] args) //子线程 TestStop testStop = new TestStop(); new Thread(testStop).start(); for (int i = 0; i < 1000; i++) System.out.println("main"+i); if (i ==900) //调用stop方法切换标志位,让子线程停止 testStop.stop(); System.out.println("线程该停止了");

3.2.2 线程休眠sleep:com.fenfen.Thread.ThreadSleep
  1. sleep作用:模拟网络超时:没有sleep会出现只有一个线程拿了票,有了延迟是为了方法问题的发生性:发生线程安全,即多个线程操作了通过同一个对象
  2. 用sleep模拟当前倒计时
public class TestSleep2 public static void tenDown() throws InterruptedException int num = 10; while (true) Thread.sleep(1000); System.out.println(num--); if (num< =0) break; public static void main(String[] args) try tenDown(); catch (Exception e) e.printStackTrace();

  1. 用sleep打印系统当前时间
public class TestSleep3 public static void main(String[] args) Date startTime = new Date(System.currentTimeMillis()); //获取系统当前时间while (true)try System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime)); //时间格式化工厂 startTime = new Date(System.currentTimeMillis()); //更新当前时间 Thread.sleep(1000); catch (Exception e) e.printStackTrace();

3.2.3 线程礼让yield
  1. 概念:让当前执行的线程暂停,但不阻塞,将线程从运行状态转为就绪状态,然后cpu会重新调度,礼让不一定成功,可能调度的还是它
  2. com.fenfen.Thread.ThreadYield
public class TestYield1 public static void main(String[] args) MyYield myYield = new MyYield(); new Thread(myYield,"a").start(); new Thread(myYield,"b").start(); class MyYield implements Runnable//alt+回车导入run方法 @Override public void run() System.out.println(Thread.currentThread().getName()+ "线程开始执行"); Thread.yield(); //礼让 System.out.println(Thread.currentThread().getName()+ "线程停止执行");

3.2.4 线程强制执行join
  1. 概念:join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
  2. com.fenfen.Thread.ThreadJoin
public class TestJoin1 implements Runnable //main到199的时候,让给thread执行了 @Override public void run() for (int i = 0; i < 1000; i++) System.out.println("线程vip已经出现!" + i); public static void main(String[] args) throws InterruptedException TestJoin1 testJoin1 = new TestJoin1(); Thread thread = new Thread(testJoin1); thread.start(); //主线程方法for (int i = 0; i < 500; i++) if (i == 200) thread.join(); //插队System.out.println("main" + i);

3.2.5 线程状态观测getState
  1. 具体实现如下:
  • 使用thread.getState()观察测试线程的状态:主线程观测子线程状态,当子线程中重写的run方法执行后,子线程就停止了,主线程跳出循环并停止
public class TestState1 public static void main(String[] args) throws InterruptedException //用lambda重写run方法 Thread thread = new Thread(()-> for (int i = 0; i < 5; i++) try Thread.sleep(1000); catch (InterruptedException e) e.printStackTrace(); System.out.println(i); System.out.println("========"); ); //观察状态 Thread.State state = thread.getState(); System.out.println(state); //观察启动后并再次观察状态 thread.start(); state = thread.getState(); //可以不用每次都创建一个对象了,节约空间来的 System.out.println(state); // while (state != Thread.State.TERMINATED)//只要线程不终止,就一直输出状态 Thread.sleep(100); state = thread.getState(); //再次更新线程状态 System.out.println(state);

观察测试线程的状态:主线程观测子线程状态,当子线程中重写的run方法执行后,子线程就停止了,主线程跳出循环
  1. 再回顾下线程的五个状态
JavaSE-09 Thread 多线程(完整版)

文章图片

3.2.6 线程优先级getPriority().setPriority(int x)
  1. 原理
在Thread类中有几个常量,设置了最小优先值为1,最大优先级是10
JavaSE-09 Thread 多线程(完整版)

文章图片

顺便看下setPriority方法
public final void setPriority(int newPriority) //此方法需要一个int值 ThreadGroup g; //线程组 checkAccess(); if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) throw new IllegalArgumentException(); //如果线程的优先级超出上面的常量最大值,小于最小值抛一个异常出来if((g = getThreadGroup()) != null) //为空的话,直接默认或者最大都ok if (newPriority > g.getMaxPriority()) newPriority = g.getMaxPriority(); setPriority0(priority = newPriority);

那就继续顺便咯,在下看getPriority方法,就比较简单们直接返回priority就可以
public final int getPriority() return priority;

  1. 获取与设置线程的优先级
public class TestPriority public static void main(String[] args) //获取主线程名字以及优先级:默认是5 System.out.println(Thread.currentThread().getName()+"--> "+Thread.currentThread().getPriority()); MyPriority myPriority = new MyPriority(); Thread t1 = new Thread(myPriority); Thread t2 = new Thread(myPriority); Thread t3 = new Thread(myPriority); Thread t4 = new Thread(myPriority); Thread t5 = new Thread(myPriority); Thread t6 = new Thread(myPriority); //先设置优先级,再启动,不然启动了再设置没用的 t1.start(); t2.setPriority(1); t2.start(); t3.setPriority(4); t3.start(); t4.setPriority(Thread.MAX_PRIORITY); //10 t4.start(); //t5.setPriority(-1); //t5.start(); 会报错抛出异常//t6.setPriority(11); //t6.start(); 会报错抛出异常class MyPriority implements Runnable @Override public void run() //获取子线程名字以及优先级 System.out.println(Thread.currentThread().getName()+"--> "+Thread.currentThread().getPriority());

只是意味着获取调度的概率低,并不是优先级高的就一定被最先调用,全看cpu的调度
4 线程同步 4.1基础概念
  1. 并发:同一个对象被多个线程同时操作
  2. 线程同步:就是一种等待机制,多个需要同时访问此对象的线程进行这个对象的等待池形成对列,等待前面线程使用完毕,下一个线程再使用,一般会通过对列加锁的形式保证线程同步,解决安全性
  3. 锁:为了保证线程安全,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,其他线程必须等待,使用后释放锁即可
  4. 用锁后仍然存在以下问题:
  • 一个线程有锁导致其他线程需要此锁的所有线程会被挂起
  • 多线程竞争下,加锁,释放锁会导致比较多的上下文切换以及调度延迟,引起性能问题
  • 优先级高的线程等待一个优先级低的线程释放锁,会导致优先级导致,引发性能倒置问题
4.2 线程不安全的举例:com.fenfen.Thread.Synchronized
4.2.1 火车票超卖的例子:不安全的买票:票会出现-1的情况
public class UnsafeBuyTicket public static void main(String[] args) BuyTicket station = new BuyTicket(); new Thread(station,"你").start(); new Thread(station,"我").start(); new Thread(station,"他").start(); class BuyTicket implements Runnable//票 private int ticketNums = 10; //标志位 boolean flag = true; //外部停止方式 @Override public void run() //买票 while(flag) try buy(); catch (InterruptedException e) e.printStackTrace(); private void buy() throws InterruptedException //判断是否邮票 if(ticketNums< =0) flag = false; return; //延迟一下 Thread.sleep(100); System.out.println(Thread.currentThread().getName()+"拿到-----> "+ticketNums--);

4.2.2 银行取钱的例子多人去银行取钱可取到超出余额的钱
public class UnsafeBank public static void main(String[] args) Account account = new Account(100,"积蓄"); Drawing you = new Drawing(account,50,"你" ); Drawing boyfriend = new Drawing(account,100,"boyfriend" ); you.start(); boyfriend.start(); //账户 class Account int money; //余额 String name; //卡名public Account(int money, String name) this.money = money; this.name = name; //银行:模拟取款 class Drawing extends ThreadAccount account; //账户 //要去多少钱 int drawingMoney; //现在手里有多少钱 int nowMoney; //构造器 public Drawing(Account account,int drawingMoney,String name) super(name); //调用父类的有参构造,是线程的名字 this.account = account; this.drawingMoney = drawingMoney; //重写方法 @Override public void run() //判断下有没有钱呢 if(account.money-drawingMoney< 0) System.out.println(Thread.currentThread().getName()+"钱不够你取了"); return; //延迟下呀,放大线程不安全的发生性 try Thread.sleep(100); catch (InterruptedException e) e.printStackTrace(); //余额=上次余额-取的钱 account.money = account.money-drawingMoney; //你手里的钱 nowMoney = nowMoney +drawingMoney; System.out.println(account.name+"余额为:"+account.money); //Thread.currentThread().getName() = this.getName()原因是:继承了Thread的全部方法,因此可以调用this System.out.println(this.getName()+"手里钱为:"+nowMoney);

4.2.3 线程不安全的集合
public class UnsafeList public static void main(String[] args) throws InterruptedException List< String> list = new ArrayList< > (); for (int i = 0; i < 18000; i++) //实际输出就17000多 new Thread(()-> list.add(Thread.currentThread().getName()); //把线程名字添加到集合里面了 ).start(); Thread.sleep(3000); System.out.println(list.size());

可能出现两个线程同一瞬间操作了同一个位置,把两个数组添加了同一个位置,覆盖的就是少的元素
4.3 同步方法及同步块
4.3.1 同步方法火车票超卖修改:将synchronized放在方法前:默认锁的是this类
public class SafeBuyTicket public static void main(String[] args) BuyTicket1 station = new BuyTicket1(); new Thread(station,"你").start(); new Thread(station,"我").start(); new Thread(station,"他").start(); class BuyTicket1 implements Runnable//票 private int ticketNums = 10; //标志位 boolean flag = true; //外部停止方式 @Override public void run() //买票 while(flag)try Thread.sleep(100); catch (InterruptedException e) e.printStackTrace(); buy(); // private synchronized void buy() //判断是否邮票 if(ticketNums< =0) flag = false; return; //延迟一下 //Thread.sleep(1000); //注意这边,sleep不会释放锁,一个线程拿到锁就会一直执行,于是把sleep放在run方法里System.out.println(Thread.currentThread().getName()+"拿到-----> "+ticketNums--);

4.3.2 同步块
  1. 基本概念:同步块synchronized(Obj),Obj就是称之为同步监视器,但是推荐使用共享资源作为同步监视器,例子中就是锁的是account这个共享资源
  2. 银行取钱修改:
public class SafeBank public static void main(String[] args) Account1 account1 = new Account1(500,"积蓄"); Drawing1 you = new Drawing1(account1,50,"你" ); Drawing1 boyfriend = new Drawing1(account1,100,"boyfriend" ); you.start(); boyfriend.start(); //账户 class Account1 int money; //余额 String name; //卡名public Account1(int money, String name) this.money = money; this.name = name; //银行:模拟取款 class Drawing1 extends ThreadAccount1 account1; //账户 //要去多少钱 int drawingMoney; //现在手里有多少钱 int nowMoney; //构造器 public Drawing1(Account1 account1,int drawingMoney,String name) super(name); //调用父类的有参构造,是线程的名字 this.account1 = account1; this.drawingMoney = drawingMoney; //重写方法 @Override public void run() synchronized (account1) //锁这个对象,是根据代码中针对哪一些进行了增删改查 //判断下有没有钱呢 if (account1.money - drawingMoney < 0) System.out.println(Thread.currentThread().getName() + "钱不够你取了"); return; //延迟下呀,放大线程不安全的发生性 try Thread.sleep(100); catch (InterruptedException e) e.printStackTrace(); //余额=上次余额-取的钱 account1.money = account1.money - drawingMoney; //你手里的钱 nowMoney = nowMoney + drawingMoney; System.out.println(account1.name + "余额为:" + account1.money); //Thread.currentThread().getName() = this.getName()原因是:继承了Thread的全部方法,因此可以调用this System.out.println(this.getName() + "手里钱为:" + nowMoney);

分析:
  • 只锁run方法的时候:只会锁住所在的Drawing类,不会锁住Account类线程
  • 线程“you”执行run方法时发现加锁了,便找到加锁对象(you),发现没有其他线程执行run方法,就持锁执行run方法
  • 线程“boyfriend”执行run方法时发现加锁了,便找到加锁对象,也发现没有其他线程执行run方法,就只锁执行run方法,导致实际上两个线程还是同步运行
  • 真正被两个线程并发访问引起冲突的是账户,因为两个Drawing都是使用同一个account来new的,所以应该锁Account而不是Bank,因此哪个类的属性会发生变化,就锁哪个类的哪个类的对象
  1. 线程不安全的集合的修改
public class SafeList public static void main(String[] args) throws InterruptedException List< String> list = new ArrayList< > (); for (int i = 0; i < 18000; i++) //实际输出就17000多 new Thread(()-> synchronized (list) list.add(Thread.currentThread().getName()); //把线程名字添加到集合里面了).start(); Thread.sleep(3000); System.out.println(list.size());

把lambda中的list用同步块锁住就可以了
4.3.3 JUC包初始JUC安全类型的集合:人家写好的类本来就是安全的,我们不需要再去同步了
public class TestJUC public static void main(String[] args) throws InterruptedException CopyOnWriteArrayList< String > list = new CopyOnWriteArrayList< String> (); for (int i = 0; i < 10000; i++) new Thread(()-> list.add(Thread.currentThread().getName()); ).start(); Thread.sleep(3000); System.out.println(list.size());

去看源码:
public CopyOnWriteArrayList() setArray(new Object[0]);

final void setArray(Object[] a) array = a;

private transient volatile Object[] array; //array用两个关键词修饰了, volatile 是唯一的意思,transient保证是序列化的 //序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程,Java只是以某种形式实现了序列化

4.3.4 死锁
  1. 概念:多个线程都在等待对方释放资源,都停止执行,某一个同步块同时拥有”两个以上对象的锁“,就可能发生”死锁“
  2. 代码实现
public class DeadLock public static void main(String[] args) Makeup g1 = new Makeup(0,"芬芬"); Makeup g2 = new Makeup(1,"静静"); g1.start(); g2.start(); //口红 class LipStick //镜子 class Mirror class Makeup extends Thread//static是为了限制只有一份资源 static LipStick lipStick =new LipStick(); static Mirror mirror =new Mirror(); //定义选择以及人名 int choice ; String girlName; //整一个构造器 Makeup(int choice,String girlName) this.choice = choice; this.girlName = girlName; @Override public void run() try makeup(); catch (InterruptedException e) e.printStackTrace(); //写一个化妆的方法:互相持有对方的锁,即拿到对方的资源 private void makeup() throws InterruptedException if(choice==0) synchronized (lipStick)//把口红锁住 System.out.println(this.girlName+"获得口红的锁"); Thread.sleep(1000); synchronized (mirror)////锁中锁,自己没释放,还想去锁拿另一个的资源 System.out.println(this.girlName+"获得镜子的锁"); else synchronized (mirror)//把镜子锁住 System.out.println(this.girlName+"获得镜子的锁"); Thread.sleep(2000); synchronized (lipStick)////锁中锁,自己没释放,还想去锁拿另一个的资源 System.out.println(this.girlName+"获得口红的锁");

  1. 化解死锁
public class DeadLock1 public static void main(String[] args) Makeup1 g1 = new Makeup1(0,"芬芬"); Makeup1 g2 = new Makeup1(1,"静静"); g1.start(); g2.start(); //口红 class LipStick1 //镜子 class Mirror1 class Makeup1 extends Thread//static是为了限制只有一份资源 static LipStick1 lipStick1 =new LipStick1(); static Mirror1 mirror1 =new Mirror1(); //定义选择以及人名 int choice ; String girlName; //整一个构造器 Makeup1(int choice,String girlName) this.choice = choice; this.girlName = girlName; @Override public void run() try makeup(); catch (InterruptedException e) e.printStackTrace(); //写一个化妆的方法:互相持有对方的锁,即拿到对方的资源 private void makeup() throws InterruptedException if(choice==0)synchronized (lipStick1)//把口红锁住 System.out.println(this.girlName+"获得口红的锁"); Thread.sleep(1000); synchronized (mirror1)//放到外面来就可以了 System.out.println(this.girlName+"获得镜子的锁"); elsesynchronized (mirror1)//把镜子锁住 System.out.println(this.girlName+"获得镜子的锁"); Thread.sleep(2000); synchronized (lipStick1)//放到外面来就可以了 System.out.println(this.girlName+"获得口红的锁");

4.3.4 Lock锁
  1. 概念:通过显示定义同步锁对象来实现同步,同步锁使用Lock对象,只有一个线程对Lock对象加锁,类似synchronized并发性和内存语义,常用ReentrantLock可重入锁
  2. 看下源码
/** The lock protecting all mutators */ final transient ReentrantLock lock = new ReentrantLock();

  1. 具体运用
【JavaSE-09 Thread 多线程(完整版)】
JavaSE-09 Thread 多线程(完整版)

文章图片

public class TestLock public static void main(String[] args) TestLock2 testLock2 = new TestLock2(); new Thread(testLock2).start(); new Thread(testLock2).start(); new Thread(testLock2).start(); class TestLock2 implements Runnableint ticketNums = 10; //定义lock锁 private final ReentrantLock lock = new ReentrantLock(); @Override public void run() while (true) //加锁解锁,在try和finally中 try lock.lock(); if(ticketNums> 0) try Thread.sleep(1000); catch (InterruptedException e) e.printStackTrace(); System.out.println(ticketNums--); else break; finally lock.unlock(); //解锁


    推荐阅读