JavaSE-09 Thread 多线程(完整版)
Posted :Concerto
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaSE-09 Thread 多线程(完整版)相关的知识,希望对你有一定的参考价值。
JavaSE-09 Thread 多线程
1. 线程简介
1.1 普通方法调用和多线程
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 Marry
private 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 基本内容
- Lambda表达式属于函数式编程
- 例如:a->System.out.println("我在学习多线程->"+i);
-
函数式接口的定义:任何接口只包含了一个抽象方法,那就是函数式接口,就可以通过lambda表达式来创建该接口的对象
2.8.2 简略代码的方法
- 静态内部类
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();
- 局部内部类
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();
- 匿名内部类:没有类的名称
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();
- 用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();
再写一个
- 正常接口代码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);
- 匿名内部类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 );
- 用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线程五个状态
3.2 线程方法
有优先级,休眠,加入,暂停,停止,是否存活
3.2.1 线程停止stop:com.fenfen.Thread.ThreadStop
JDK一般不建议使用它自己本身的方法停止线程,一般会建立一个标志位进行终止变量
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
- sleep作用:模拟网络超时:没有sleep会出现只有一个线程拿了票,有了延迟是为了方法问题的发生性:发生线程安全,即多个线程操作了通过同一个对象
- 用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();
- 用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
- 概念:让当前执行的线程暂停,但不阻塞,将线程从运行状态转为就绪状态,然后cpu会重新调度,礼让不一定成功,可能调度的还是它
- 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
-
概念:join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
- 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
- 具体实现如下:
- 使用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方法执行后,子线程就停止了,主线程跳出循环
- 再回顾下线程的五个状态
3.2.6 线程优先级getPriority().setPriority(int x)
- 原理
在Thread类中有几个常量,设置了最小优先值为1,最大优先级是10
顺便看下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;
- 获取与设置线程的优先级
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基础概念
- 并发:同一个对象被多个线程同时操作
- 线程同步:就是一种等待机制,多个需要同时访问此对象的线程进行这个对象的等待池形成对列,等待前面线程使用完毕,下一个线程再使用,一般会通过对列加锁的形式保证线程同步,解决安全性
- 锁:为了保证线程安全,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,其他线程必须等待,使用后释放锁即可
- 用锁后仍然存在以下问题:
- 一个线程有锁导致其他线程需要此锁的所有线程会被挂起
- 多线程竞争下,加锁,释放锁会导致比较多的上下文切换以及调度延迟,引起性能问题
- 优先级高的线程等待一个优先级低的线程释放锁,会导致优先级导致,引发性能倒置问题
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 Thread
Account 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 同步块
-
基本概念:同步块synchronized(Obj),Obj就是称之为同步监视器,但是推荐使用共享资源作为同步监视器 ,例子中就是锁的是account这个共享资源
- 银行取钱修改:
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 Thread
Account1 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,因此哪个类的属性会发生变化,就锁哪个类的哪个类的对象
- 线程不安全的集合的修改
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 死锁
-
概念:多个线程都在等待对方释放资源,都停止执行,某一个同步块同时拥有”两个以上对象的锁“,就可能发生”死锁“
- 代码实现
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+"获得口红的锁");
- 化解死锁
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+"获得镜子的锁");
else
synchronized (mirror1)//把镜子锁住
System.out.println(this.girlName+"获得镜子的锁");
Thread.sleep(2000);
synchronized (lipStick1)//放到外面来就可以了
System.out.println(this.girlName+"获得口红的锁");
4.3.4 Lock锁
-
概念:通过显示定义同步锁对象来实现同步,同步锁使用Lock对象,只有一个线程对Lock对象加锁,类似synchronized并发性和内存语义,常用ReentrantLock可重入锁
- 看下源码
/** The lock protecting all mutators */
final transient ReentrantLock lock = new ReentrantLock();
- 具体运用
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 Runnable
int 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();//解锁
以上是关于JavaSE-09 Thread 多线程(完整版)的主要内容,如果未能解决你的问题,请参考以下文章