多线程学习

    <link rel="stylesheet" href="https://csdnimg.cn/release/blogv2/dist/mdeditor/css/editerView/ck_htmledit_views-b3c43d3711.css">
            <div id="content_views" class="htmledit_views">
                <p id="main-toc"><strong>目录</strong></p> 

 简单计数器

Runnable接口

狂神的并发问题代码——以抢票为例

龟兔赛跑-Race

Lamda表达式

线程状态

线程停止Stop()

线程休眠Sleep() 

观察线程状态

 线程的优先级

守护(daemon)线程

线程同步

三大不安全案例

死锁

Lock(锁)

管程法

线程池

 总结


Java体系的一个重要部分就是多线程。在JAVA程序中,线程的最普遍用途是允许Applet在接受用户输入的同时,在屏幕的另一部分显示动画。使用多线程可以编写出CPU最大利用率的高效程序,因为空闲时间保持最低。这对Java运行的交互式的网络互连环境是至关重要的。

 简单计数器


 
  1. package Thread;
  2. import java.awt.*;
  3. import java.awt.event.*;
  4. import java.applet.*;
  5. public class Counter1 extends Applet {
  6. private int count = 0;
  7. private Button onOff = new Button( "Toggle"); // 初始化2个按钮
  8. private Button start = new Button( "Start");
  9. private TextField t = new TextField( 10); // 初始化文本字段对象
  10. private boolean runFlag = true;
  11. private long i = 0; // 循环变量
  12. public void init () {
  13. add(t);
  14. start.addActionListener( new StartL()); // 注册按钮
  15. add(start); // 放置按钮
  16. onOff.addActionListener( new OnOffL());
  17. add(onOff);
  18. }
  19. public void go () {
  20. while ( true) {
  21. i = 0;
  22. while (i <= 100000000) {
  23. i++;
  24. } // 花费近千毫秒的时间,密集计算
  25. if (runFlag) // 一旦开始,CPU没机会执行其他代码,耗费时间
  26. t.setText(Integer.toString(count++));
  27. }
  28. }
  29. class StartL implements ActionListener {
  30. public void actionPerformed (ActionEvent e) // 内部类,实现接口,响应按钮单击事件,其实没执行的机会
  31. {
  32. go();
  33. }
  34. }
  35. class OnOffL implements ActionListener {
  36. public void actionPerformed (ActionEvent e) // 内部类,实现接口,响应按钮单击事件,其实没执行的机会
  37. {
  38. runFlag = !runFlag; // 一个标志变量
  39. }
  40. }
  41. public static void main (String[] args) // 应用程序入口,用浏览器时不执行该部分代码
  42. {
  43. Counter1 applet = new Counter1();
  44. Frame aFrame = new Frame( "Counter1");
  45. aFrame.addWindowListener( new WindowAdapter() { // 关闭框架按钮事件,没机会执行
  46. public void windowClosing (WindowEvent e) {
  47. System.exit( 0);
  48. }
  49. });
  50. aFrame.add(applet, BorderLayout.CENTER);
  51. aFrame.setSize( 300, 200);
  52. applet.init();
  53. applet.start();
  54. aFrame.setVisible( true);
  55. }
  56. }

一旦按下start按钮,就会调用go()方法,go()方法是永远不会返回的,因为它被设计成一个无限循环。即在第一个按键以后便陷入go()中,go()根本不会返回,程序不能再对其他任何事件进行控制,这时的Toggle按钮和窗口的关闭按钮不再有反应。

Runnable接口

使用接口的原因在于:Applet程序是由于JApplet类扩展而来,因为Java不支持多重继承,所以我们不能同时从JApplet类和Thread类派生类,所以用Runnable接口。 

用线程改写上面的代码


 
  1. package Thread;
  2. import java.awt.*;
  3. import java.awt.event.*;
  4. import java.applet.*;
  5. public class Counter2 extends Applet implements Runnable{ //线程接口
  6. private int count = 0;
  7. private Button onOff = new Button( "Toggle"); // 初始化2个按钮
  8. private Button start = new Button( "Start");
  9. private TextField t = new TextField( 10); // 初始化文本字段对象
  10. private boolean runFlag = true;
  11. private long i = 0; // 循环变量
  12. Thread athread; //线程对象声明
  13. public void init (){
  14. start.addActionListener( new StartL());
  15. add(start);
  16. start.addActionListener( new OnOffl());
  17. add(onOff);
  18. }
  19. public void run (){
  20. System.out.println( "已经调用run()");
  21. while( true){
  22. try{
  23. athread.sleep( 3000); //休眠3秒,CPU执行其他代码
  24. } catch(InterruptedException e){
  25. }
  26. if(runFlag){
  27. count++;
  28. System.out.println( "count="+count);
  29. }
  30. }
  31. }
  32. class StartL implements ActionListener{
  33. public void actionPerformed (ActionEvent e) //CPU有机会响应按钮的单击事件
  34. {
  35. Counter2 aCounter2= new Counter2(); //
  36. athread = new Thread(aCounter2); //线程创立
  37. athread.start(); //线程启动
  38. System.out.println( "已经按下start按钮");
  39. }
  40. }
  41. class OnOffl implements ActionListener{
  42. public void actionPerformed (ActionEvent e){
  43. System.out.println( "已经按下onOff按钮");
  44. }
  45. }
  46. public static void main (String[] args){
  47. Counter2 applet= new Counter2();
  48. Frame aFrame = new Frame( "Counter2");
  49. aFrame.addWindowListener( new WindowAdapter() { // 关闭框架按钮事件,没机会执行
  50. public void windowClosing (WindowEvent e) {
  51. System.exit( 0);
  52. }
  53. });
  54. aFrame.add(applet, BorderLayout.CENTER);
  55. aFrame.setSize( 300, 200);
  56. applet.init();
  57. applet.start();
  58. aFrame.setVisible( true);
  59. }
  60. }

程序运行后,单击start按钮开始计数,并显示相应的信息,计数过程中单击Toggle按钮也能做出相应的反应,单击退出可退出。

狂神的多线程代码


 
  1. package Thread;
  2. //创建线程方式,实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法
  3. public class TestThread3 implements Runnable{
  4. public void run (){
  5. //run方法线程体
  6. for( int i = 0;i< 100;i++){
  7. System.out.println( "在练花式单杠动作马凯、"+i);
  8. }
  9. }
  10. public static void main (String[] args){
  11. //创建runnable接口的实现类对象
  12. TestThread3 testThread3 = new TestThread3();
  13. //创建线程对象,通过线程对象来开启我们的线程,代理
  14. // Thread thread = new Thread(testThread3);
  15. // thread.start();
  16. new Thread(testThread3).start(); //上面两行代码可以写成这一句
  17. for( int i= 0;i< 1000;i++){
  18. System.out.println( "我在练单杠360--"+i);
  19. }
  20. }
  21. }

可以从运行结果看粗是同时运行的,不过在把i调整为100以下的就会出现线程堆块。

 实现Runnable接口的好处

1.实现接口Runnable具有多线程能力

2.启动线程:传入目标对象+Thread对象.start()

3.避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

狂神的并发问题代码——以抢票为例


 
  1. package Thread;
  2. //多个线程同时操作一个对象
  3. //买火车票
  4. public class TestThread4 implements Runnable{
  5. //票数
  6. private int ticketNums = 10; //10张票
  7. //重写run方法
  8. public void run (){
  9. while( true){
  10. if(ticketNums<= 0){
  11. break; //如果没票了就break跳出
  12. }
  13. System.out.println(Thread.currentThread().getName()+ "-->拿到了第"+ticketNums--+ "票");
  14. }
  15. }
  16. public static void main (String[] args){
  17. TestThread4 ticket= new TestThread4();
  18. new Thread(ticket, "俊俊").start();
  19. new Thread(ticket, "俊爷").start();
  20. new Thread(ticket, "虾翻").start();
  21. }
  22. }

这里我们运行可以看到俊爷一个人把所有的票抢光了

然后设置休眠以防止一个人拿走全部的票

在16行代码加入


 
  1. try{
  2. Thread.sleep( 100); //设置休眠200毫秒
  3. } catch(InterruptedException e){
  4. e.printStackTrace();
  5. }

 发现拿了超过10张的票,说明多个线程操作同一个资源的情况下,线程不安全,数据出现紊乱。

龟兔赛跑-Race


 
  1. package Thread;
  2. //模拟龟兔赛跑
  3. public class Race implements Runnable {
  4. //静态的胜利者
  5. private static String winner;
  6. public void run (){
  7. for( int i= 0;i<= 100;i++){
  8. //判断比赛是否结束
  9. boolean flag = gameOver(i);
  10. if(flag){ //如果flag等于真
  11. break; //如果比赛结束就停止
  12. }
  13. System.out.println(Thread.currentThread().getName()+ "跑了————"+i+ "步长");
  14. }
  15. }
  16. //判断是否完成比赛
  17. private boolean gameOver (int steep){
  18. //判断是否有胜利者
  19. if(winner != null){
  20. return true;
  21. }{
  22. if(steep>= 100){
  23. winner = Thread.currentThread().getName();
  24. System.out.println( "winner is "+winner);
  25. return true;
  26. }
  27. }
  28. return false;
  29. }
  30. public static void main (String[] args){
  31. Race race = new Race();
  32. new Thread(race, "乌龟").start();
  33. new Thread(race, "兔子").start();
  34. }
  35. }

 兔子赢了

然后我们让兔子睡觉在第八行加入


 
  1. //这里让兔子睡觉
  2. if(Thread.currentThread().getName().equals( "兔子")&&i% 10== 0){
  3. try{
  4. Thread.sleep( 1);
  5. } catch (InterruptedException e) {
  6. e.printStackTrace();
  7. }
  8. }

 实现Callable接口(了解即可)

Lamda表达式

--希腊字母表中排序第十一位的字母,英文名称为Lambda

--避免匿名内部类定义过多

--其实质函数式编程的概念

为什么要使用lambda表达式

--去掉一堆没有意义的代码,只留下核心的逻辑


 
  1. package Thread;
  2. /*
  3. 推导lamda表达式
  4. */
  5. public class TestLamda {
  6. public static void main (String[] args){
  7. Ilike l = new Like();
  8. l.lamda();
  9. }
  10. }
  11. //1.定义一个函数式的接口
  12. interface Ilike{
  13. void lamda ();
  14. }
  15. //2.实现类
  16. class Like implements Ilike{
  17. public void lamda (){
  18. System.out.println( "牛马努力给老板奋斗法拉利");
  19. }
  20. }

 优化。。


 
  1. package Thread;
  2. /*
  3. 推导lamda表达式
  4. */
  5. public class TestLamda {
  6. //静态内部类
  7. static class Like2 implements Ilike{
  8. public void lamda (){
  9. System.out.println( "久旱逢甘露");
  10. }
  11. }
  12. public static void main (String[] args){
  13. Ilike l = new Like();
  14. l.lamda();
  15. l = new Like2();
  16. l.lamda();
  17. //4.局部内部类
  18. class Like3 implements Ilike{
  19. public void lamda (){
  20. System.out.println( "他乡遇故知");
  21. }
  22. }
  23. l = new Like3();
  24. l.lamda();
  25. //5.匿名内部类,没有类的名称,必须借助接口或者父类(没有名字,借助了接口)
  26. l= new Ilike(){
  27. public void lamda (){
  28. System.out.println( "洞房花烛夜");
  29. }
  30. };
  31. l.lamda();
  32. //6.有lamda简化
  33. l = ()->{
  34. System.out.println( "金榜题名时");
  35. };
  36. l.lamda();
  37. }
  38. }
  39. //1.定义一个函数式的接口
  40. interface Ilike{
  41. void lamda ();
  42. }
  43. //2.实现类
  44. class Like implements Ilike{
  45. public void lamda (){
  46. System.out.println( "牛马努力给老板奋斗法拉利");
  47. }
  48. }

 lamda表达式的一些总结

//lamda表达式只能有一行代码的情况下才能简化为一行;如果有多行,那么就用代码块包裹。(即花括号)

//前提是接口为函数式接口

//多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号

线程状态

五大状态:创建状态、就绪状态、阻塞状态、运行状态、死亡状态。

线程停止Stop()


 
  1. package Thread;
  2. //测试stop
  3. //1.建议线程正常停止-->利用次数,不建议死循环
  4. //2.建议使用标志位-->设置一个标志位
  5. //3.不要使用stop或者destroy等过时或者JDK不建议使用的方法
  6. public class StopTest implements Runnable{
  7. //1.设置一个标识位
  8. private boolean flag = true;
  9. public void run (){
  10. int i= 0;
  11. while(flag){
  12. System.out.println( "runThread"+i++);
  13. }
  14. }
  15. //2.设置一个公开的方法停止线程,转换标志位
  16. public void Stop (){
  17. this.flag = false;
  18. }
  19. public static void main (String[] args){
  20. StopTest ST = new StopTest();
  21. new Thread(ST).start();
  22. for( int i= 0; i< 100;i++){
  23. System.out.println( "main"+i);
  24. if (i == 90) {
  25. //调用stop方法切换标志位,让线程停止
  26. ST.Stop();
  27. System.out.println( "stopThread");
  28. }
  29. }
  30. }
  31. }

在main执行到90的时候停止线程

线程休眠Sleep() 

>sleep(时间)指定当前线程阻塞的毫秒数

>sleep存在异常InterruptedException

>sleep时间达到后线程进入就绪状态

>sleep可以模拟网络延时,倒计时等

>每一个对象都有一个锁,sleep不会释放锁


 
  1. package Thread;
  2. //模拟倒计时
  3. public class Sleep {
  4. public static void main (String[] args){
  5. try{
  6. tenDown();
  7. } catch(InterruptedException e){
  8. e.printStackTrace();
  9. }
  10. }
  11. public static void tenDown () throws InterruptedException{
  12. int num = 10;
  13. while( true){
  14. Thread.sleep( 1000);
  15. System.out.println(num--);
  16. if(num== 0){
  17. break;
  18. }
  19. }
  20. }
  21. }

每隔一秒打印一下


 
  1. package Thread;
  2. import java.util.Date;
  3. import java.text.SimpleDateFormat;
  4. //模拟倒计时
  5. public class Sleep {
  6. public static void main (String[] args){
  7. //打印当前系统时间
  8. Date startTime = new Date(System.currentTimeMillis()); //获取当前系统时间
  9. while( true){
  10. try{
  11. Thread.sleep( 1000);
  12. System.out.println( new SimpleDateFormat( "HH:mm:ss").format(startTime)); //
  13. startTime = new Date(System.currentTimeMillis()); //更新当前时间
  14. } catch(InterruptedException e){
  15. e.printStackTrace();
  16. }
  17. }
  18. }
  19. public static void tenDown () throws InterruptedException{
  20. int num = 10;
  21. while( true){
  22. Thread.sleep( 1000);
  23. System.out.println(num--);
  24. if(num== 0){
  25. break;
  26. }
  27. }
  28. }
  29. }

 第一次运行发现没有类 SimpleDateFormat,于是调用java.text.SimpleDateFormat

观察线程状态


 
  1. package Thread;
  2. //观察测试线程的状态
  3. public class TestState{
  4. public static void main (String[] args) throws InterruptedException {
  5. Thread thread = new Thread(() -> {
  6. for ( int i = 0; i < 5; i++) {
  7. try {
  8. Thread.sleep( 100);
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. }
  13. System.out.println( "");
  14. });
  15. //观察状态
  16. Thread. State state = thread.getState();
  17. System.out.println(state);
  18. //观察启动后
  19. thread.start(); //启动线程
  20. state = thread.getState();
  21. System.out.println(state);
  22. while (state != Thread.State.TERMINATED) { //只要线程不终止,就一直输出状态
  23. Thread.sleep( 100); //这里抛出一个异常
  24. state = thread.getState(); //更新线程状态
  25. System.out.println(state); //输出状态
  26. }
  27. }
  28. }

这里我们试一下线程停止后还能否正常启动,在while循环后添加thread.start()

 运行发现没办法再次启动线程。

 线程的优先级

Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。


 
  1. package Thread;
  2. //测试线程的优先级
  3. public class Testpriority {
  4. public static void main (String[] args){
  5. //主线程默认优先级
  6. System.out.println(Thread.currentThread().getName()+ "-->"+Thread.currentThread());
  7. Mypriority myPriority = new Mypriority();
  8. Thread t1 = new Thread(myPriority);
  9. Thread t2 = new Thread(myPriority);
  10. Thread t3 = new Thread(myPriority);
  11. Thread t4 = new Thread(myPriority);
  12. Thread t5 = new Thread(myPriority);
  13. //先不设置优先级,启动
  14. t1.start();
  15. t2.setPriority( 1);
  16. t2.start();
  17. t3.setPriority( 4);
  18. t3.start();
  19. t4.setPriority(Thread.MAX_PRIORITY); //MAX_PRIPRITY
  20. t4.start();
  21. }
  22. }
  23. class Mypriority implements Runnable{
  24. public void run (){
  25. System.out.println(Thread.currentThread().getName()+ "-->"+Thread.currentThread().getPriority());
  26. }
  27. }

可以看到优先级高的先跑,但是你得设置优先级(优先级数为1~10)

 要注意的是这里可能会出现性能倒置:优先级低只是意味着获得调度的概率低,并不是优先级低的就不会被调度了,这都是看CPU的调度。

守护(daemon)线程

(1)线程分为用户线程和守护线程

(2)虚拟机必须确保用户线程执行完毕

(3)虚拟机不用等待守护线程执行完毕


 
  1. package Thread;
  2. //测试守护线程
  3. //上帝守护你,人生不过三万天
  4. public class TestDaemon {
  5. public static void main (String[] args){
  6. God god = new God();
  7. You you = new You();
  8. Thread thread = new Thread(god);
  9. thread.setDaemon( true); //默认false表示用户线程
  10. thread.start(); //上帝线程启动
  11. new Thread(you).start(); //你 用户线程启动
  12. }
  13. }
  14. //上帝
  15. class God implements Runnable{
  16. public void run (){
  17. while ( true) {
  18. System.out.println( "你被上帝保护着");
  19. }
  20. }
  21. }
  22. //你
  23. class You implements Runnable {
  24. public void run () {
  25. for ( int i = 1; i < 36500; i++) {
  26. System.out.println( "你活到了"+i+ "天");
  27. }
  28. System.out.println( "差不多活够了,该狗带了");
  29. }
  30. }

虚拟机关闭需要一定的时间,所以用户线程结束后,守护线程还在运行。

线程同步

由于同一个进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放即可。

三大不安全案例

1.不安全的买票


 
  1. package Thread;
  2. //不安全的买票
  3. //线程不安全,会有负数
  4. public class UnsafeBuyTicket {
  5. public static void main (String[] args){
  6. BuyTicket station = new BuyTicket();
  7. new Thread(station, "niuam").start();
  8. new Thread(station, "niuam1").start();
  9. new Thread(station, "niuam2").start();
  10. }
  11. }
  12. class BuyTicket implements Runnable{
  13. //票
  14. private int ticketNums = 10;
  15. boolean flag = true; //外部停止方式
  16. public void run (){
  17. //买票
  18. while (flag) {
  19. try{
  20. buy(); //调用方法buy买票
  21. } catch (InterruptedException e) {
  22. e.printStackTrace();
  23. }
  24. }
  25. }
  26. private void buy () throws InterruptedException{
  27. //判断是否有票
  28. if (ticketNums <= 0) {
  29. flag = false;
  30. return;
  31. }
  32. //模拟延时
  33. Thread.sleep( 100);
  34. //买票
  35. System.out.println(Thread.currentThread().getName()+ "拿到"+ticketNums--);
  36. }
  37. }

2.银行取钱


 
  1. package Thread;
  2. //不安全的取钱
  3. //两个人同时取钱
  4. public class UnsafeBank {
  5. public static void main (String[] args){
  6. Account account = new Account( 100, "结婚基金");
  7. Drawing you = new Drawing(account, 50, "牛马");
  8. Drawing girlfriend = new Drawing(account, 100, "牛马de女票");
  9. you.start();
  10. girlfriend.start();
  11. }
  12. }
  13. //账户
  14. class Account{
  15. int money; //余额
  16. String name; //卡名
  17. //Alt+insert构造函数
  18. //按住Ctrl选中两个
  19. public Account (int money, String name) {
  20. this.money = money;
  21. this.name = name;
  22. }
  23. }
  24. //银行:模拟取款
  25. class Drawing extends Thread{
  26. Account account; //账户
  27. //取了多少钱
  28. int drawingMoney;
  29. //现在手里有多少钱
  30. int nowMoney;
  31. public Drawing (Account account,int drawingMoney,String name){
  32. super(name);
  33. this.account = account;
  34. this.drawingMoney = drawingMoney;
  35. }
  36. //取钱
  37. public void run (){
  38. //判断有没有钱
  39. if(account.money-drawingMoney< 0){
  40. //钱不够,要取的钱比账户余额多
  41. System.out.println(Thread.currentThread().getName()+ "钱不够,不能操作");
  42. return;
  43. }
  44. try{
  45. Thread.sleep( 1000);
  46. } catch (InterruptedException e) {
  47. e.printStackTrace();
  48. }
  49. //卡内余额 = 余额 - 你取走的钱
  50. account.money = account.money - drawingMoney;
  51. //你现在手里的
  52. nowMoney = nowMoney + drawingMoney;
  53. System.out.println(account.name+ "余额为:"+account.money);
  54. //Thread.currountThread().getName() = this.getName()
  55. System.out.println( this.getName()+ "手里的钱"+nowMoney);
  56. }
  57. }

 

 这里相当于负债了,两人操作一个账户,但是在不同的内存地址中。

3.线程不安全的情况

死锁

两个线程在一瞬间同时加载到了同一个位置中,把数据加载在同一位置,然后覆盖掉了。就会缺失掉一部分线程。

死锁:多个线程相互抱着对方需要的资源,然后形成僵持
package com.wang.thread;


public class DeadLock {
    public static void main(String[] args) {
        Makeup girl1 = new Makeup(0,"灰姑娘");
        Makeup girl2 = new Makeup(1,"白雪公主");
        girl1.start();
        girl2.start();
    }
}
//口红
class Lipstick {

}
//镜子
class Mirror {

}
class Makeup extends Thread {

    //需要的资源只有一份,用static修饰来保证只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice;//选择
    String girlName;//使用化妆品的人

    public Makeup(int choice, String girlName) {
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        //化妆
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //super.run();
    //化妆,互相持有对方的锁,需要拿到对方的资源
       /*
        第一个人进来想拿口红,1秒之后拿镜子,然后离开
        第二个人进来拿镜子,2秒之后拿口红,然后离开
        于是形成了互相僵持,想拿到对方的东西
        **/
    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+"获得口红的锁");
                }
            }
        }
    }
}

在这里插入图片描述

解决方法:把同步块拿出来,不让一个对象同时获得两把锁

package com.wang.thread;


public class DeadLock {
    public static void main(String[] args) {
        Makeup girl1 = new Makeup(0,"灰姑娘");
        Makeup girl2 = new Makeup(1,"白雪公主");
        girl1.start();
        girl2.start();
    }
}
//口红
class Lipstick {

}
//镜子
class Mirror {

}
class Makeup extends Thread {

    //需要的资源只有一份,用static修饰来保证只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice;//选择
    String girlName;//使用化妆品的人

    public Makeup(int choice, String girlName) {
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        //化妆
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //super.run();
    //化妆,互相持有对方的锁,需要拿到对方的资源
       /*
        第一个人进来想拿口红,1秒之后拿镜子,然后离开
        第二个人进来拿镜子,2秒之后拿口红,然后离开
        于是形成了互相僵持,想拿到对方的东西
        **/
    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)互斥条件:一个资源每次只能被一个进程使用。

(2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

(3)不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺。

(4)循环等待条件:若进程之间形成一种头尾相接的循环等待资源关系。

Lock(锁)

从JDK5.0开始,Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步。同步锁用Lock对象充当 java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象 ReentrantLock 类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。

 
  1. package Thread;
  2. import java.util.concurrent.locks.ReentrantLock;
  3. //测试Lock锁
  4. public class TestLock {
  5. public static void main (String[] args){
  6. TestLock2 testLock2 = new TestLock2();
  7. new Thread(testLock2).start();
  8. new Thread(testLock2).start();
  9. new Thread(testLock2).start();
  10. new Thread(testLock2).start();
  11. }
  12. }
  13. class TestLock2 implements Runnable{
  14. //定义票数
  15. int ticketNums = 10;
  16. //定义lock锁
  17. private final ReentrantLock lock = new ReentrantLock();
  18. public void run (){
  19. while( true){
  20. // lock.lock();
  21. try {
  22. lock.lock();
  23. if(ticketNums> 0){
  24. if(ticketNums> 0){
  25. try{
  26. lock.lock(); //加锁
  27. Thread.sleep( 1000);
  28. } catch (InterruptedException e) {
  29. e.printStackTrace();
  30. }
  31. System.out.println(ticketNums--);
  32. } else{
  33. break;
  34. }
  35. }
  36. } finally {
  37. //解锁
  38. lock.unlock();
  39. }
  40. }
  41. }
  42. }

排队一个一个来拿票。

多多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。某一个同步块同时拥有**”两个以上对象的锁“**时,就可能发生”死锁“的问题个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。某一个同步块同时拥有**”两个以上对象的锁“**时,就可能发生”死锁“的问题

管程法


 
  1. package newbegin;
  2. //测试:生产者消费者模型-->利用缓冲区解决;
  3. //生产者,消费者,产品,缓冲区
  4. public class TestPC {
  5. public static void main (String[] args) {
  6. SynContainer container = new SynContainer();
  7. new Productor(container).start();
  8. new Cousumer(container).start();
  9. }
  10. }
  11. //生产者
  12. class Productor extends Thread{
  13. SynContainer container;
  14. public Productor (SynContainer container){
  15. this.container=container;
  16. }
  17. //生产工作
  18. public void run (){
  19. for ( int i = 0; i < 100 ; i++) {
  20. container.push( new Chicken(i)); //把生产的鸡丢经容器
  21. System.out.println( "生产了"+i+ "只鸡");
  22. }
  23. }
  24. }
  25. //消费者
  26. class Cousumer extends Thread{
  27. SynContainer container;
  28. public Cousumer (SynContainer container){
  29. this.container = container;
  30. }
  31. //消费
  32. public void run (){
  33. for ( int i = 0; i < 100; i++) {
  34. System.out.println( "消费了"+container.pop().id+ "只鸡");
  35. }
  36. }
  37. }
  38. //产品
  39. class Chicken{
  40. int id; //产品鸡的编号
  41. public Chicken (int id){
  42. this.id=id;
  43. }
  44. }
  45. //缓冲区
  46. class SynContainer{
  47. //需要一个容器大小
  48. Chicken[] chickens = new Chicken[ 10]; //一次性10只鸡
  49. int count= 0; //这里是容器计数器
  50. //生产者放入产品
  51. public synchronized void push (Chicken chicken){
  52. //如果容器满了,就需要等待消费者消费
  53. if(count==chickens.length){
  54. //通知消费者消费,生产者等待
  55. try{
  56. this.wait();
  57. } catch (InterruptedException e) {
  58. e.printStackTrace();
  59. }
  60. }
  61. //这里容器没满,我们把产品丢入
  62. chickens[count]=chicken;
  63. count++;
  64. //可以通知消费者消费
  65. this.notifyAll();
  66. }
  67. //消费者消费产品
  68. public synchronized Chicken pop (){
  69. //判断能否消费
  70. if(count== 0){
  71. try{
  72. this.wait();
  73. } catch (InterruptedException e) {
  74. e.printStackTrace();
  75. }
  76. }
  77. //这里可以消费
  78. count--;
  79. Chicken chicken = chickens[count];
  80. //吃完通知生成者生产
  81. this.notifyAll();
  82. return chicken;
  83. }
  84. }

生产的产品都能被消费者消费到。

解决方式2:信号灯法

并发写作模型“生产者/消费者模式”–>信号灯法==设置标志位

package com.xuyuan;
public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Wathcher(tv).start();
    }
}

//生产者--演员
class Player extends Thread{
    TV tv;
    public Player(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if(i%2==0){
                this.tv.play("快乐大本营");
            }else{
                this.tv.play("天天向上");
            }
        }
    }
}

//观众
class Wathcher extends Thread{
    TV tv;
    public Wathcher(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}

//产品--节目
class TV{
//    演员表演,观众等待  T
//    观众观看,演员等待  F
    String voice;  // 表演节目
    boolean flag = true;

//    表演
    public synchronized void play(String voice){
        if(!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("演员表演了: "+voice);
//        通知观众观看
        this.notifyAll();// 通知唤醒
        this.voice = voice;
        this.flag = !flag;
    }

//    观看
    public synchronized void watch(){
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观看了: "+voice);
//        通知演员表演
        this.notifyAll();
        this.flag = !this.flag;
    }
}package com.wang.gaoji;


public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Wathcher(tv).start();
    }
}

//生产者-->演员
class Player extends Thread{
    TV tv;
    public Player(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if(i%2==0){
                this.tv.play("快乐大本营");
            }else{
                this.tv.play("天天向上");
            }
        }
    }
}

//消费者-->观众
class Wathcher extends Thread{
    TV tv;
    public Wathcher(TV tv){
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}

//产品-->节目
class TV{
    //演员表演,观众等待  T
    //观众观看,演员等待  F
    String voice;  // 表演节目
    boolean flag = true;

    //表演
    public synchronized void play(String voice){
        if(!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("演员表演了: "+voice);
        //通知观众观看
        this.notifyAll();//通知唤醒
        this.voice = voice;
        this.flag = !flag;
    }

    //观看
    public synchronized void watch(){
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观看了: "+voice);
        //通知演员表演
        this.notifyAll();
        this.flag = !this.flag;
    }
}

线程池

使用背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。

思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似于公共交通工具,共享单车。

优点:

(1)提高响应速度(减少创建新线程的时间)

(2)降低资源消耗(重复利用线程池中的线程,不需要每次都创建)

(3)便于线程管理

corePoolSize:核心池的大小

maximumPoolSize;最大线程数

keepAliveTime:线程没有任务时最多保持多长时间后会终止

上代码:


 
  1. package newbegin;
  2. import java.util.concurrent.Executor;
  3. import java.util.concurrent.ExecutorService;
  4. import java.util.concurrent.Executors;
  5. //线程池
  6. public class TestPool {
  7. public static void main (String[] args) {
  8. //1.创建服务,创建线程池
  9. //newFixedThreadPool参数为:线程池大小
  10. ExecutorService service = Executors.newFixedThreadPool( 10);
  11. //执行
  12. service.execute( new MyThread());
  13. service.execute( new MyThread());
  14. service.execute( new MyThread());
  15. service.execute( new MyThread());
  16. //2.关闭线程池连接
  17. service.shutdown();
  18. }
  19. }
  20. class MyThread implements Runnable{
  21. @Override
  22. public void run () {
  23. System.out.println(Thread.currentThread().getName());
  24. }
  25. }

 总结


 
  1. package newbegin;
  2. import org.w3c.dom.ls.LSOutput;
  3. import java.util.concurrent.Callable;
  4. import java.util.concurrent.ExecutionException;
  5. import java.util.concurrent.FutureTask;
  6. //总结线程的创建
  7. public class ThreadNew {
  8. public static void main (String[] args) {
  9. new MyThread1().start();
  10. new Thread( new MyThread2()).start();
  11. FutureTask futureTask = new FutureTask<Integer>( new MyThread3());
  12. new Thread(futureTask).start();
  13. try{
  14. Integer integer = (Integer) futureTask.get();
  15. System.out.println(integer);
  16. } catch (InterruptedException e) {
  17. e.printStackTrace();
  18. } catch (ExecutionException e){
  19. e.printStackTrace();
  20. }
  21. }
  22. }
  23. //1.继承Thread类
  24. class MyThread1 extends Thread{
  25. @Override
  26. public void run () {
  27. System.out.println( "启动线程1");
  28. }
  29. }
  30. //2.实现Runnable接口
  31. class MyThread2 implements Runnable{
  32. @Override
  33. public void run () {
  34. System.out.println( "启动线程2");
  35. }
  36. }
  37. //3.实现Callable接口
  38. class MyThread3 implements Callable<Integer>{
  39. @Override
  40. public Integer call () throws Exception {
  41. System.out.println( "启动线程3");
  42. return 100;
  43. }
  44. }

三个线程正常启动。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值