多线程学习笔记

多线程详解

进程、线程、多线程

◆说起进程,就不得不说下程序。程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。 ◆而进程则是执行程序的一-次执行过程,它是一个动态的概念。 是系统资源分配的单位 ◆通常在一个进程中可以包含若干个线程,当然一个进程中至少有一 个线程,不然没有存在的意义。线程是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:\Users\23195\AppData\Roaming\Typora\typora-user-images\image-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:线程没有任务时最多保持多长时间后会终止

posted @ 2020-10-27 22:14  骑牛西出函谷关  阅读(97)  评论(0编辑  收藏  举报