• 多线程学习笔记


    多线程详解

    进程、线程、多线程

    ◆说起进程,就不得不说下程序。程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。 ◆而进程则是执行程序的一-次执行过程,它是一个动态的概念。 是系统资源分配的单位 ◆通常在一个进程中可以包含若干个线程,当然一个进程中至少有一 个线程,不然没有存在的意义。线程是CPU调度和执行的的单位。

    注意:很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu的情况下, 在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错局。


    本章核心概念 ◆线程就是独立的执行路径; ◆在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程; ◆main()称之为主线程,为系统的入口,用于执行整个程序; ◆在一一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的。 ◆对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制; ◆线程会带来额外的开销,如cpu调度时间,并发控制开销。 ◆每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

    Thread

    ◆自定义线程类继承Thread类 ◆重写run()方法,编写线程执行体 ◆创建线程对象,调用star()方法启动线程

    package com.shao.xiancheng;

    //创建线程方式一:继承Thread类,重写run()方法,调用strart()开启线程
    //总结:注意,线程开启不一定立即执行,由CPU调度执行

    public class TestThread1 extends Thread {
       @Override
       public void run() {
           //run方法,线程体
           for (int i = 0; i <100 ; i++) {
               System.out.println("线程1,我在学习Java");
          }
      }

       public static void main(String[] args) {
           TestThread1 testThread1 = new TestThread1();
           testThread1.start();

           for (int i = 0; i <200 ; i++) {
               System.out.println("线程2,我还是在学习");
          }

      }
    }


    //结果:使用start线程是同步执行的,如果使用run,则是同在main方法里顺序执行

    Thread实例,下载网图

    package com.shao.xiancheng;

    import com.sun.org.apache.bcel.internal.generic.NEW;
    import com.sun.org.apache.xpath.internal.objects.XString;
    import org.apache.commons.io.FileUtils;

    import java.io.File;
    import java.io.IOException;
    import java.net.URL;

    //联系Thread,实现多线程同步
    public class TestThread2 extends Thread{
       private String url;//网络图片地址
       private String name;//保存的文件

       //带参数的构造器
       public TestThread2(String url,String name){
           this.url=url;
           this.name=name;
      }

       @Override   //线程体
       public void run() {
           WebDownloader webDownloader = new WebDownloader();
           webDownloader.downloader(url,name);
           System.out.println("下载的文件名为"+ name);
      }

       public static void main(String[] args) {
           TestThread2 T1 = new TestThread2("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2646262196,761815550&fm=26&gp=0.jpg","星空1.jpg");
           TestThread2 T2 = new TestThread2("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1124037946,1369060497&fm=26&gp=0.jpg","星空2.jpg");
           TestThread2 T3 = new TestThread2("https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1465568264,4250740867&fm=26&gp=0.jpg","星空3.jpg");
           T1.start();
           T2.start();
           T3.start();
      }


    }

    //下载器
    class  WebDownloader{
       //下载方法
       public void downloader(String url,String name){
           try {
               //调用FileUtils工具类的COPYURLTofile方法,下载图片,下载的名字要注意后缀
           FileUtils.copyURLToFile(new URL(url),new File(name));
          } catch (IOException e) {
               e.printStackTrace();
               System.out.println("IO异常,downloader方法出现问题");
          }
      }
    }

    /*
    下载的文件名为星空3.jpg
    下载的文件名为星空2.jpg
    下载的文件名为星空1.jpg

    进程已结束,退出代码 0
    线程是同步执行的
    */

     

    实现Runnable

    ◆定义MyRunnable类实现Runnable接口 ◆实现run()方法,编写线程执行体 ◆创建线程对象,调用start()方法启动线程

    package com.shao.xiancheng;

    public class TestThread3  implements Runnable {
       @Override
       public void run() {
           //run方法,线程体
           for (int i = 0; i <100 ; i++) {
               System.out.println("线程1,我在学习Java");
          }
      }

       public static void main(String[] args) {
           //创建Runnable实现类对象
           TestThread3 testThread3 = new TestThread3();
           
           //创建线程对象,通过线程对象来开启我们的线程,代理
           //Thread thread = new Thread(testThread3);
           //thread.start();
           new Thread(testThread3).start();

           for (int i = 0; i <200 ; i++) {
               System.out.println("线程2,我还是在学习");
          }

      }

    }

    Runnable 实例,下载网图

    package com.shao.xiancheng;

    import com.sun.org.apache.bcel.internal.generic.NEW;
    import com.sun.org.apache.xpath.internal.objects.XString;
    import org.apache.commons.io.FileUtils;

    import java.io.File;
    import java.io.IOException;
    import java.net.URL;

    //联系Thread,实现多线程同步
    public class TestThread2 implements Runnable{
       private String url;//网络图片地址
       private String name;//保存的文件

       //带参数的构造器
       public TestThread2(String url,String name){
           this.url=url;
           this.name=name;
      }

       @Override   //线程体
       public void run() {
           WebDownloader webDownloader = new WebDownloader();
           webDownloader.downloader(url,name);
           System.out.println("下载的文件名为"+ name);
      }

       public static void main(String[] args) {

           TestThread2 T1 = new TestThread2("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2646262196,761815550&fm=26&gp=0.jpg","星空1.jpg");
           TestThread2 T2 = new TestThread2("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1124037946,1369060497&fm=26&gp=0.jpg","星空2.jpg");
           TestThread2 T3 = new TestThread2("https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1465568264,4250740867&fm=26&gp=0.jpg","星空3.jpg");
           new Thread(T1).start();//此处通过创建线程对象,调用Runnable实现类对象
           new Thread(T2).start();
           new Thread(T3).start();

      }


    }

    //下载器
    class  WebDownloader{
       //下载方法
       public void downloader(String url,String name){
           try {
               //调用FileUtils工具类的COPYURLTofile方法,下载图片,下载的名字要注意后缀
           FileUtils.copyURLToFile(new URL(url),new File(name));
          } catch (IOException e) {
               e.printStackTrace();
               System.out.println("IO异常,downloader方法出现问题");
          }
      }
    }

     

    龟兔赛跑

    package com.shao.xiancheng;

    public class Race implements Runnable{
       //胜利者
       private static String winner;
       @Override
       public void run() {
           for (int i = 0; i <= 100 ; i++) {
               //模拟兔子休息
               if (Thread.currentThread().getName().equals("兔子") && i%50==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();
      }
    }

     

    实现Callable接口

    1.实现Callable接口,需要返回值类型 2.重写call方法,需要抛出异常3.创建目标对象 4.创建执行服务: ExecutorService ser = Executors.newFixedThreadPool(1); 5.提交执行: Future<Boolean> result1 = ser. submit(t1); 6.获取结果: boolean r1 = result1.get() 7.关闭服务: ser.shutdownNow();

    package com.shao.xiancheng;

    import org.apache.commons.io.FileUtils;

    import java.io.File;
    import java.io.IOException;
    import java.net.URL;
    import java.util.concurrent.*;

    //有返回值
    public class TestCallable1 implements Callable<Boolean> {
       private String url;//网络图片地址
       private String name;//保存的文件

       //带参数的构造器
       public TestCallable1 (String url,String name){
           this.url=url;
           this.name=name;
      }

       @Override   //线程体
       public Boolean call() {
           WebDownloader1 webDownloader = new WebDownloader1();
           webDownloader.downloader(url,name);
           System.out.println("下载的文件名为"+ name);
           return true;
      }

       public static void main(String[] args) throws ExecutionException, InterruptedException {

           TestCallable1 t1 = new TestCallable1("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=2646262196,761815550&fm=26&gp=0.jpg","星空1.jpg");
           TestCallable1 t2 = new TestCallable1("https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=1124037946,1369060497&fm=26&gp=0.jpg","星空2.jpg");
           TestCallable1 t3 = new TestCallable1("https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1465568264,4250740867&fm=26&gp=0.jpg","星空3.jpg");
           
           //4.创建执行服务:
            ExecutorService ser = Executors.newFixedThreadPool(3);
           //5.提交执行:,用服务来提交
           Future<Boolean> result1 = ser. submit(t1);
           Future<Boolean> result2 = ser. submit(t2);
           Future<Boolean> result3 = ser. submit(t3);
           //6.获取结果:
           boolean rs1 = result1.get();
           boolean rs2 = result2.get();
           boolean rs3 = result3.get();
           System.out.println(rs1);
           System.out.println(rs2);
           System.out.println(rs3);
           //7.关闭服务:
           ser.shutdownNow();

      }
    }

    //下载器
    class  WebDownloader1{
       //下载方法
       public void downloader(String url,String name){
           try {
               //调用FileUtils工具类的COPYURLTofile方法,下载图片,下载的名字要注意后缀
               FileUtils.copyURLToFile(new URL(url),new File(name));
          } catch (IOException e) {
               e.printStackTrace();
               System.out.println("IO异常,downloader方法出现问题");
          }
      }
    }

    静态代理

    ◆你:真实角色 ◆婚庆公司:代理你,帮你处理结婚的事 ◆结婚:实现都实现结婚接口即可

    Lambda表达式

    ◆入希腊字母表中排序第十-位的字母,英语名称为L ambda ◆可以让你的代码看起来很简洁 ◆避免匿名内部类定义过多 ◆其实质属于函数式编程的概念

    (params) -> expression [表达式]
    (params) -> statement [ 语句]
    (params) -> { statements }
    a-> System. out. println("i like lambda-->"+a) ;
    new Thread ()->System.out.println(“多线程学习。 。。。")) .start()

    ◆理解Functional Interface (函数式接口)是学习Java8 lambda表达式的关键所在。

    ◆函数式接口的定义: . ◆任何接口,如果只包含唯- -个抽象方法,那么它就是一个函数式接口。 ◆对于函数式接口,我们可以通过lambda表达式来创建该接口的对象。

    package com.shao.xiancheng;
    /*
       推导Lambda表达式
    */


    public class TestLambda1 {

       //3.静态内部类
       static class Like2 implements ILike{
           @Override
           public void lambda() {
               System.out.println("I like lambda2");
          }
      }
       public static void main(String[] args) {
           ILike like = new Like1();//接口new一个实现类
           like.lambda();

           like = new Like2();
           like.lambda();

           //4.局部内部类
           class Like3 implements ILike{
               @Override
               public void lambda() {
                   System.out.println("I like lambda3");
              }
          }
           like = new Like3();
           like.lambda();

           //5.匿名内部类,没有类的名称,必须借助接口或者父类
           like = new ILike(){
               @Override
               public void lambda() {
                   System.out.println("I like lambda4");
              }
          };//因为这里相当于一行语句,所以要用分号
           like.lambda();

           //6.用lambda简化
           like =()-> {
               System.out.println("I like lambda5");
          };
           like.lambda();

           //
           ILove love1 = null;
           love1 = (a,b,c)-> {
               System. out.println("i love you-->"+a+" "+b+" "+c) ;
               System. out. println("i love you-->too") ;
          };
           love1.love(20,30,40);
           //总结:
           //lambda表达式只能有一行代码的情况下才能简化成为-行,如果有多行,那么就用代码块包裹。
           //前提是接口为函数式接口
           //多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号,

      }
    }

    //1.定义一个函数式接口
    interface ILike{
       void lambda();//这里因为是接口里面,所有就是抽象的
    }

    interface ILove{
       void love(int a,int b,int c);//这里因为是接口里面,所有就是抽象的
    }

    //2.实现类
    class Like1 implements ILike{
       @Override
       public void lambda() {
           System.out.println("I like lambda1");
      }
    }

    /*
    I like lambda1
    I like lambda2
    I like lambda3
    I like lambda4
    I like lambda5
    i love you-->20 30 40
    i love you-->too

    进程已结束,退出代码 0
    */

    线程停止

    image-20200815212921999

    image-20200815213058871

    package com.shao.xiancheng;

    //测试stop
    //1.建议线程正常停止-->利用次数,不建议死循环
    //2.建议使用标志位-->设置一个标志位
    //3.不要使用stop或者destory等过时或者JDK不建议使用的方法
    public class TestStop1 implements Runnable{
       //1.设置一个标识;定义线程体使用的标识
       private boolean flag = true;

       @Override
       public void run() {
           int i = 0 ;
           //3.线程体使用改标识
           while (flag){
               System.out.println("run...Thread"+i++);
          }
      }

       //2.设置一个公开的方法停止线程,转换标志位
       public void stop(){
           this.flag = false;
      }

       public static void main(String[] args) {
           TestStop1 testStop1 = new TestStop1();
           new Thread(testStop1).start();
           for (int i = 0; i <100 ; i++) {
               System.out.println("main"+ i);
               if (i==90){
                   //4.调用Stop方法切换标志位,让线程停止
                   testStop1.stop();
                   System.out.println("线程停止");//此处停止的是线程,不是主线程,主线程要跑到100才停

              }

          }

      }

    }

    线程休眠

    ◆sleep (时间)指定当前线程阻塞的毫秒数; ◆sleep存在异常InterruptedException; ◆sleep时间达到后线程进入就绪状态; ◆sleep可以模拟网络延时,倒计时等。 ◆每一个对象都有一 个锁,sleep不会释放锁;


    public class TestSleep2 {
       //打印系统当前时间
       public static void main(String[] args) throws InterruptedException {
           int i=10;
           Date startTime = new Date(System.currentTimeMillis());//获取系统当前时间
           while (i>0){
               Thread.sleep(1000);
               System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
               startTime = new Date(System.currentTimeMillis());//更新当前时间
               i--;
          }
      }
    }
    /*
    22:44:22
    22:44:23
    22:44:24
    22:44:25
    22:44:26
    22:44:27
    22:44:28
    22:44:29
    22:44:30
    22:44:31

    进程已结束,退出代码 0
    */

    线程礼让

    ◆礼让线程,让当前正在执行的线程暂停,但不阻塞 ◆将线程从运行状态转为就绪状态 ◆让cpu重新调度,礼让不- -定成功!看CPU心情

    public class TestYield {
       public static void main(String[] args) {
           MyYield myYield = new MyYield();
           new Thread (myYield,  "a") .start();
           new Thread (myYield,  "b") .start();
      }
    }
    class MyYield implements Runnable{

       @Override
       public void run() {
           System. out . println(Thread. currentThread() . getName() +"线程开始执行");
           Thread.yield();//礼让
           System. out .println(Thread.currentThread() .getName()+"线程停止执行");
      }
    }
    /*
    a线程开始执行
    b线程开始执行
    b线程停止执行
    a线程停止执行

    进程已结束,退出代码 0
    */![image-20200815230309374](C:Users23195AppDataRoamingTypora ypora-user-imagesimage-20200815230309374.png)

    Join插队

    image-20200815230420457

    线程状态。

    • 线程可以处于以下状态之一:

      ◆NEW

      尚未启动的线程处于此状态。

      ◆RUNNABLE 在Java虚拟机中执行的线程处于此状态。

      ◆BLOCKED

      被阻塞等待监视器锁定的线程处于此状态。

      ◆WAITING 正在等待另一个线程执行特定动作的线程处于此状态。

      ◆TIMED_WAITING

      正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。

      ◆TERMINATED

      已退出的线程处于此状态。

      一个线程可以在给定时间点处于一个状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。

      死亡之后的线程不能再次启动线程只能启动一次

    //此程序还有问题有待完善
    package com.shao.xiancheng;

    public class TestState {
       public static void main(String[] args) {
           Thread thread1 = new Thread(()->{
               for (int i = 0; i <5 ; i++) {
                   try {
                       Thread.sleep(300);
                  } catch (InterruptedException e) {
                       e.printStackTrace();
                  }
              }
               System.out.println("//////");
          });

           //观察状态,使用thread.getState()方法,按ALT+ENTER,创建对象名
           Thread.State state1 = thread1.getState();
           System.out.println("线程1"+state1);  //NEW

           //观察启动后
           thread1.start();//启动线程
           state1 = thread1.getState();
           System.out.println("线程1"+state1);  //RUNNABLE

           Thread thread2 = new Thread();
           Thread.State state2 = thread2.getState();
           thread2.start();
           System.out.println("主线程"+state2);

           while (state1 != Thread.State.TERMINATED){//只要线程不停止就一直输出状态
               try {
                  thread2.sleep(100);
                   state1 = thread1.getState();//更新线程状态
                   state2 = thread2.getState();

                   System.out.println("线程1"+state1);
                   System.out.println("主线程"+state2);
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
      }
    }

    //此程序还有问题有待完善

    线程优先级

    ◆Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程, 线程调度器按照优先级决定应该调度哪个线程来执行。 ◆线程的优先级用数字表示,范围从1~10. ◆Thread.MIN_ PRIORITY = 1; ◆Thread.MAX PRIORITY= 10; ◆Thread.NORM_ _PRIORITY = 5; ◆使用以下方式改变或获取优先级 ◆getPriority() . setPriority(int xxx)

    优先级低只是意味着获得调度的 概率低并不是优先级低就不会 被调用了.这都是看CPU的调度

    package com.shao.xiancheng;

    //测试线程的优先级
    public class TestPriority {
       public static void main(String[] args) {
           //main线程默认优先级是 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);//MAX_PRIORITY =10
           t4.start();
      }

    }

    class Mypriority implements Runnable{
       @Override
       public void run() {
           System.out.println(Thread.currentThread().getName()+"-->"+ Thread.currentThread().getPriority());
      }
    }

    /*
    main-->5
    Thread-0-->5
    Thread-2-->4
    Thread-1-->1
    Thread-3-->10

    进程已结束,退出代码 0

    */

    守护(daemon)线程

    ◆线程分为用户线程和守护线程 ◆虚拟机必须确保用户线程执行完毕 ◆虚拟机不用等待守护线程执行完毕 ◆如,后台记录操作日志,监控内存垃圾回收等待..

    package com.shao.xiancheng;

    public class TestDaemon {

       public static void main(String[] args) {
           God god = new God();
           Human you = new Human();


           Thread thread1 = new Thread(god);
           thread1.setDaemon(true);//设置为守护线程。默认是false表示是用户线程,正常的线程都是用户线程

           thread1.start();
           new Thread(you).start();
      }
    }




    class God implements Runnable{
       @Override
       public void run() {
           while (true){
               System.out.println("守护进程运行中");
          }
      }
    }


    class Human implements Runnable {
       @Override
       public void run() {
           for (int i = 0; i < 10; i++) {
               System.out.println("进程运行中");
          }
           System.out.println("进程结束");
      }
    }

    /*
    守护线程不用特意停止,它会自动在其他线程停止后停止,其他线程停止后,他还会运行一会,再停止,因为CPU的调度。
    */

    线程同步synchronized

    多线程操作同一个资源

    ◆处理多线程问题时,多个线程访问同-个对象,并且某些线程还想修改这个对象.这时候我们就需要线程同步.线程同步其实就是一种等待机制 ,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用

    ◆每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

    ◆由于同- -进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized ,当-一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可. 存在以下问题: ◆一个线程持有锁会导致其他所有需要此锁的线程挂起; ◆在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题; ◆如果一个优先级高的线程等待一个优先级低的线程释放锁 会导致优先级倒 置,引起性能问题.

    package com.shao.xiancheng;

    public class TestNoSafe1 {
       public static void main(String[] args) {
           BuyTicket buyTicket = new BuyTicket();
           new Thread(buyTicket,"小明").start();
           new Thread(buyTicket,"小红").start();
           new Thread(buyTicket,"小刘").start();

      }

    }
    class BuyTicket implements Runnable{
       private int ticketNums = 10;
       boolean flag = true;//外部停止方式
       @Override
       public  void run() {
           while (flag){
               buy();
          }
      }

       private synchronized void buy(){//此处在方法里设置锁,synchronized。
           if (ticketNums <= 0){
               flag = false;
               return;
          }
           System.out.println(Thread.currentThread().getName()+"获得第"+ticketNums--+"票");
      }
    }

    同步块

    ◆同步块: synchronized (Obj){} ◆Obj 称之为同步监视器 ◆Obj可以是任何对象,但是推荐使用共享资源作为同步监视器 ◆同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this ,就是这个对象本身, 或者是class [反射中讲解] ◆同步监视器的执行过程 1.第一个线程访问 ,锁定同步监视器,执行其中代码. 2.第二个线程访问 ,发现同步监视器被锁定,无法访问. 3.第一个线程访问完毕 ,解锁同步监视器. 4.第二个线程访问,发现同步监视器没有锁,然后锁定并访问

    package com.shao.xiancheng;
    //不安全的取钱
    //两个人去银行取钱,账户
    public class TestNoSafe2 {
       public static void main(String[] args) {
           //账户
           Account account = new Account(100, "工资卡");
           Drawing man = new Drawing(account,40,"男人");
           Drawing girl = new Drawing(account,70,"女人");

           man.start();
           girl.start();
      }
    }

    //账户
    class Account{
       int money;
       String name1;

       public Account(int money, String name1) {
           this.money = money;
           this.name1 = name1;   //银行卡的名字
      }
    }

    //银行
    class Drawing extends Thread{
       Account account;//账户
       int drawingMoney;//取了的钱
       int nowMoney;//现在手里有的钱


       public Drawing(Account account,int drawingMoney,String name){
           super(name);    //super必须在构造器第一行*************************
           this.account =account;
           this.drawingMoney = drawingMoney;

      }

       //取钱操作
       @Override
       public void run() {
           synchronized (account){
               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.name1+"余额为:"+account.money);
               // this。getName() = Thread.currentThread().getName()   因为此处继承了Thread,可以使用Thread全部方法
               System.out.println(this.getName()+":手里的钱"+nowMoney);
          }

      }
    }

    /*
    工资卡余额为:60
    男人:手里的钱40
    女人:卡内余额不够

    进程已结束,退出代码 0
    */

    死锁

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

     

    package com.shao.xiancheng;
    //死锁,多个线程互相抱着对方需要的资源,然后形成僵持
    public class TestDeadLock {
        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 girlFriend;
    
        public Makeup(int choice,String girlFriend) {
            this.choice=choice;
            this.girlFriend=girlFriend;
        }
    
        @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.girlFriend+"获得口红的锁");
                    Thread.sleep(2000);
                    synchronized (mirror){
                        System.out.println(this.girlFriend+"获得镜子的锁");
                    }
                }
            }else {
                synchronized (mirror){
                    System.out.println(this.girlFriend+"获得镜子的锁");
                    Thread.sleep(1000);
                    synchronized (lipstick){
                        System.out.println(this.girlFriend+"获得口红的锁");
                    }
                }
            }
        }
    
    }
    
    /*
    小晚获得口红的锁
    小星获得镜子的锁
    */

    死锁避免方法

    ◆产生死锁的四个必要条件:

    1.互斥条件: -个资源每次只能被一 个进程使用。 2.请求与保持条件: -个进程因请求资源而阻塞时,对已获得的资源保持不放。 3.不剥夺条件 :进程已获得的资源,在末使用完之前,不能强行剥夺。 4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。 上面列出了死锁的四个必要条件,我们只要想办法破其中的任意一个或多个条件就可以避免死锁发生

    Lock(锁)

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

    //定义lock锁************************************
    private final ReentrantLock lock = new ReentrantLock() ;
    @override
    public void run() {
    	while (true){
            
    		try {
    			lock.lock(); //加锁
    			if (ticketNums>0){
    				try {
    					Thread. sleep( millis: 1000) ; .
    				} catch (Inter ruptedException e) {
    					e. printStackTrace();
                    }
    					System. out . println(ticketNums-- ) ;
    			}else {
    				break;
    			}
    		}finally{
    			//解锁
    			lock . unlock();
    		}
    	}
    
    

    image-20200817112102816

    synchronized与Lock的对比

    ◆Lock是显式锁(手动开启和关闭锁,别忘记关闭锁) synchronized是隐式锁, 出了作用域自动释放 ◆Lock只有代码块锁,synchronized有代码块锁和方法锁 ◆使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类) ◆优先使用顺序: ◆Lock >同步代码块(已经进入了方法体,分配了相应资源) >同步方法(在方法体之外)



    线程通信

    ◆应用场景:生产者和消费者问题 ◆假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费. ◆如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止, ◆如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止, Producer(生产者)-------->数据缓存区-------->Consumer(消费者)


    线程通信-分析

    这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件. ◆对于生产者,没有生产产品之前,要通知消费者等待.而生产了产品之后,又需要马上通知消费者消费 ◆对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费. ◆在生产者消费者问题中,仅有synchronized是不够的 ◆synchronized可阻止并发更新同一个共享资源,实现了同步 ◆synchronized 不能用来实现不同线程之间的消息传递(通信)


    并发协作模型“生产者1消费者模式”-->管程法

    ◆生产者:负责生产数据的模块(可能是方法,对象,线程,进程) ; ◆消费者:负责处理数据的模块(可能是方法,对象,线程,进程); ◆缓冲区:消费者不能直接使用生产者的数据,他们之间有个“缓冲区

    生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据


    信号灯法

    这个类似于缓冲区为1 的管程法


    使用线程池

    ◆背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。 ◆思路: 提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。 ◆可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。 好处: ◆提高响应速度(减少了创建新线程的时间) ◆降低资源消耗(重复利用线程池中线程,不需要每次都创建) ◆便于线程管理(.... ◆corePoolSize:核心池的大小 ◆maximumPoolSize:最大线程数 ◆keepAliveTime:线程没有任务时最多保持多长时间后会终止

    加油! 加油!
  • 相关阅读:
    开放个人电脑端口[Windows]
    mysql局域网服务搭建
    JS数据分组[JSON]
    网站缓存【我只是单纯的保存网站!】
    JS生成数字加减乘法验证码
    【http反向代理】多个域名指向同一个ip的不同网站解决方法
    JQuery获取ID含有特殊字符的DOM元素
    java.lang.NoClassDefFoundError: org/apache/commons/collections4/ListValuedMap
    大型网站技术架构,6网站的伸缩性架构之分布式缓存集群的伸缩性设计
    大型网站技术架构,6网站的伸缩性架构之应用服务器集群的伸缩性设计
  • 原文地址:https://www.cnblogs.com/shaozhi/p/13887995.html
Copyright © 2020-2023  润新知