第12篇-JAVA 多线程

第12篇-JAVA 多线程

  • 每篇一句 :不要只看到艰难,要看艰难后面的胜利
  • 初学心得: 敢于尝试,就等于你已经向成功迈出了第一步
  • (笔者:JEEP/711)[JAVA笔记 | 时间:2017-04-20| JAVA 多线程 ]

1.进程与线程

1.什么是进程

  • 程序是指令和数据的有序的集合,其本身没有任何运行的含义,是一个静态的概念
  • 进程是一个具有一定独立功能的程序,一个实体
  • 几乎所有的操作系统都支持同时运行多个任务,一个任务通常就是一个程序,每个运行中的程序就是一个进程
  • 当一个程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程

2.进程的状态: 进程执行时的间断性,决定了进程可能具有多种状态,事实上,运行中的进程具有

  • 以下三种基本状态:
  • 1.就绪状态(Ready)
  • 2.运行状态(Running)
  • 3.阻塞状态(Blocked)

3.线程

  • 线程实际上是进程基础之上的进一步划分,一个进程启动之后,里面的若干程序,又可以划分成若干个线程
  • 线程:是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换
  • 并发执行,一个进程最少有一个线程(单线程程序)

4.线程实现的两种方式

  • 在java中如果想要实现多线程操作,两种实现方法
  • 1.一种是继承Thrcad类
  • 2.另一种是实现Runnable接口

5.多线程编程的优势

  • 进程间不能共享内存,但线程之间共享内存非常容易
  • 系统创建进程需要为该进程重新分配系统资源,但创建线程则代价小得多,因此使用多线程来实现多任务并发比多进程的效率高
  • Java语言内置的多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了Java的多线程编程

2.继承Thread类创建线程类

(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就是代表了线程需要完成的任务 
因此,我们经常把run方法称为线程执行体 
(2)创建Thread子类的实例,即创建了线程对象 
(3)调用线程对象的start方法来启动该线程 
(4)join线程:Thread提供了让一个线程等待另一个线程完成的方法:join() 方法 
当在某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到被join方法加入的join线程完成为止 
join()方法通常由使用线程的程序调用,以将大问题划分成许多小问题,每个小问题分配一个线程。当所有的小问题都得到处理后,再调用主线程来进一步操作

3.实现Runnable接口创建线程类

(1)定义Runnable接口的实现类,并重写该接口的run方法,该run方法的方法体同样是该线程的线程执行体 
(2)创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象 
(3)调用线程对象的start方法来启动该线程

4.线程睡眠

如果我们需要让当前正在执行的线程暂停一段时间,并进入阻塞状态 
则可以通过调用Thread类的静态sleep方法,sleep方法有两种重载的形式:

  • static void sleep(long millis):让当前正在执行的线程暂停millis毫秒,并进入阻塞状态,该方法受到系统计时器和线程调度器的精度和准确度的影响
  • static void sleep(long millis, int nanos):让当前正在执行的线程暂停millis毫秒加nanos毫微妙,并进入阻塞状态,该方法受到系统计时器和线程调度器的精度和准确度的影响

5.两种线程方式的对比

采用实现Runnable接口方式的多线程:

  • 线程类只是实现了Runnable接口,还可以可以继承其他类
  • 在这种方式下,可以多个线程共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况
  • 从而可以将CPU,代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想
  • 劣势:编程稍稍复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法

采用继承Thread类方式的多线程:

  • 劣势:因为线程类已经继承了Thread类,所以不能再继承其他父类
  • 优势:编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this即可获得当前线程

6.线程生命周期

线程声明周期:

  • 就绪状态
  • 执行状态
  • 阻塞状态

7.线程安全问题

多条线程并发修改共享资源就容易引发线程安全问题 
使用同步解决多线程共享数据出现安全问题 
多个线程有可能同时处理同一个资源 
线程同步就是指多个线程在同一个时间段内只能有一个线程执行指定代码 
其他线程等待此线程完成之后才可以继续执行 
同步代码块synchronized(要同步对象){要同步的操作} 
同步方法public synchronized void method(){要同步的操作} 
同步代码会带来性能降低的问题,提高数据的安全性,牺牲性能来保证安全 
同步代码块 
Java的多线程支持引入了同步监视器来解决这个问题,使用同步监视器的通用方法就是同步代码块 
synchronized后括号里的obj就是同步监视器,上面代码的含义是:线程开始执行同步代码块之前,必须先获得对同步监视器的锁定 
同步方法 
Java的多线程安全支持还提供了同步方法,同步方法就是使用synchronized关键字来修饰某个方法,则该方法成为同步方法 
对于同步方法而言,无需显式指定同步监视器,同步方法的同步监视器是this,也就是该对象本身 
线程死锁 
过多的同步有可能死锁,死锁的操作一般是在程序运行的时候才有可能出现 
多线程中要进行资源的共享,就需要同步,但同步过多,就可能造成死锁

8.线程创建启动实例

1.package cn.jeep;
2.//继承Thread类
3.public class XianChengDemo extends Thread{
4.  private int i;//私有成员属性
5.  //重写run方法,run方法的方法体就是线程执行体
6.  public void run(){
7.      for(;i<100;i++){
8.          //当线程类继承Thread类时,直接使用this.及格获取当前线程
9.          //Thread对象的getNname()返回当前线程名字
10.          //因此可以直接调用getName方法返回当前线程名字
11.          System.out.println(getName()+" "+i);
12.      }
13.  }
14.  //主方法入口
15.  public static void main(String[] args) {
16.      for(int i=0;i<100;i++){
17.          //调用Thread的currenTread方法获取当前线程
18.          System.out.println(Thread.currentThread().getName()+" "+i);
19.          if(i == 20){
20.              //创建并启动第一个线程
21.              new XianChengDemo().start();
22.              //创建并启动第二个线程
23.              new XianChengDemo().start();    
24.          }
25.      }
26.  }
27.}
1.package cn.runnable;
2.//声明一个类
3.public  class RunnableDenmo implements Runnable{
4.  private int i;//私有属性
5.  //重写run方法
6.  public void run(){
7.      for(;i<100;i++){
8.          //当线程类实现Runnable接口时
9.          //如果想获取当前线程,只能用Thread.currentTread()方法
10.          System.out.println(Thread.currentThread().getName()+" "+i);
11.      }
12.  }
13.  //主方法
14.  public static void main(String[] args) {
15.      for(int i=0;i<100;i++){
16.          System.out.println(Thread.currentThread().getName()+" "+i);
17.          if(i == 20){
18.              RunnableDenmo st = new RunnableDenmo();
19.              //通过new Thread(target,name)方法创建新线程
20.              new Thread(st,"新线程1").start();
21.              new Thread(st,"新线程2").start();
22.          }
23.      }
24.  }
25.}
1.package cn.callable;
2.import java.util.concurrent.Callable;
3.import java.util.concurrent.FutureTask;
4.public class CallableDemo {
5.  public static void main(String[] args) {
6.      //创建Callable对象
7.      @SuppressWarnings("unused")
8.      CallableDemo cl = new CallableDemo();
9.      //先使用Lambda表达式创建Callable<Integer>对象
10.      //使用FutureTask来包装Callable对象
11.      FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)()->{
12.          int i = 0;
13.          for(;i<100;i++){
14.              System.out.println(Thread.currentThread().getName()+"的循环变量的i的值:"+i);
15.          }
16.          //call()方法可以有返回值
17.          return i;
18.      });
19.      for(int i=0;i<100;i++){
20.          if(i==20){
21.              //实质还是以Callable对象来创建并启动线程
22.              new Thread(task,"有返回值得线程").start();
23.          }
24.      }
25.      try{
26.      //获取线程返回值
27.      System.out.println("子线程的返回值:"+task.get());
28.          }catch(Exception ex){
29.      ex.printStackTrace();
30.      }
31.  }
32.}

第12篇-JAVA 多线程

  • 每篇一句 :不要只看到艰难,要看艰难后面的胜利
  • 初学心得: 敢于尝试,就等于你已经向成功迈出了第一步
  • (笔者:JEEP/711)[JAVA笔记 | 时间:2017-04-20| JAVA 多线程 ]

目录导航


1.进程与线程

1.什么是进程

  • 程序是指令和数据的有序的集合,其本身没有任何运行的含义,是一个静态的概念
  • 进程是一个具有一定独立功能的程序,一个实体
  • 几乎所有的操作系统都支持同时运行多个任务,一个任务通常就是一个程序,每个运行中的程序就是一个进程
  • 当一个程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程

2.进程的状态: 进程执行时的间断性,决定了进程可能具有多种状态,事实上,运行中的进程具有

  • 以下三种基本状态:
  • 1.就绪状态(Ready)
  • 2.运行状态(Running)
  • 3.阻塞状态(Blocked)

3.线程

  • 线程实际上是进程基础之上的进一步划分,一个进程启动之后,里面的若干程序,又可以划分成若干个线程
  • 线程:是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换
  • 并发执行,一个进程最少有一个线程(单线程程序)

4.线程实现的两种方式

  • 在java中如果想要实现多线程操作,两种实现方法
  • 1.一种是继承Thrcad类
  • 2.另一种是实现Runnable接口

5.多线程编程的优势

  • 进程间不能共享内存,但线程之间共享内存非常容易
  • 系统创建进程需要为该进程重新分配系统资源,但创建线程则代价小得多,因此使用多线程来实现多任务并发比多进程的效率高
  • Java语言内置的多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了Java的多线程编程

2.继承Thread类创建线程类

(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就是代表了线程需要完成的任务 
因此,我们经常把run方法称为线程执行体 
(2)创建Thread子类的实例,即创建了线程对象 
(3)调用线程对象的start方法来启动该线程 
(4)join线程:Thread提供了让一个线程等待另一个线程完成的方法:join() 方法 
当在某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到被join方法加入的join线程完成为止 
join()方法通常由使用线程的程序调用,以将大问题划分成许多小问题,每个小问题分配一个线程。当所有的小问题都得到处理后,再调用主线程来进一步操作

3.实现Runnable接口创建线程类

(1)定义Runnable接口的实现类,并重写该接口的run方法,该run方法的方法体同样是该线程的线程执行体 
(2)创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象 
(3)调用线程对象的start方法来启动该线程

4.线程睡眠

如果我们需要让当前正在执行的线程暂停一段时间,并进入阻塞状态 
则可以通过调用Thread类的静态sleep方法,sleep方法有两种重载的形式:

  • static void sleep(long millis):让当前正在执行的线程暂停millis毫秒,并进入阻塞状态,该方法受到系统计时器和线程调度器的精度和准确度的影响
  • static void sleep(long millis, int nanos):让当前正在执行的线程暂停millis毫秒加nanos毫微妙,并进入阻塞状态,该方法受到系统计时器和线程调度器的精度和准确度的影响

5.两种线程方式的对比

采用实现Runnable接口方式的多线程:

  • 线程类只是实现了Runnable接口,还可以可以继承其他类
  • 在这种方式下,可以多个线程共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况
  • 从而可以将CPU,代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想
  • 劣势:编程稍稍复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法

采用继承Thread类方式的多线程:

  • 劣势:因为线程类已经继承了Thread类,所以不能再继承其他父类
  • 优势:编写简单,如果需要访问当前线程,无需使用Thread.currentThread()方法,直接使用this即可获得当前线程

6.线程生命周期

线程声明周期:

  • 就绪状态
  • 执行状态
  • 阻塞状态

7.线程安全问题

多条线程并发修改共享资源就容易引发线程安全问题 
使用同步解决多线程共享数据出现安全问题 
多个线程有可能同时处理同一个资源 
线程同步就是指多个线程在同一个时间段内只能有一个线程执行指定代码 
其他线程等待此线程完成之后才可以继续执行 
同步代码块synchronized(要同步对象){要同步的操作} 
同步方法public synchronized void method(){要同步的操作} 
同步代码会带来性能降低的问题,提高数据的安全性,牺牲性能来保证安全 
同步代码块 
Java的多线程支持引入了同步监视器来解决这个问题,使用同步监视器的通用方法就是同步代码块 
synchronized后括号里的obj就是同步监视器,上面代码的含义是:线程开始执行同步代码块之前,必须先获得对同步监视器的锁定 
同步方法 
Java的多线程安全支持还提供了同步方法,同步方法就是使用synchronized关键字来修饰某个方法,则该方法成为同步方法 
对于同步方法而言,无需显式指定同步监视器,同步方法的同步监视器是this,也就是该对象本身 
线程死锁 
过多的同步有可能死锁,死锁的操作一般是在程序运行的时候才有可能出现 
多线程中要进行资源的共享,就需要同步,但同步过多,就可能造成死锁

8.线程创建启动实例

1.package cn.jeep;2.//继承Thread类3.public class XianChengDemo extends Thread{4.  private int i;//私有成员属性5.  //重写run方法,run方法的方法体就是线程执行体6.  public void run(){7.      for(;i<100;i++){8.          //当线程类继承Thread类时,直接使用this.及格获取当前线程9.          //Thread对象的getNname()返回当前线程名字10.          //因此可以直接调用getName方法返回当前线程名字11.          System.out.println(getName()+" "+i);12.      }13.  }14.  //主方法入口15.  public static void main(String[] args) {16.      for(int i=0;i<100;i++){17.          //调用Thread的currenTread方法获取当前线程18.          System.out.println(Thread.currentThread().getName()+" "+i);19.          if(i == 20){20.              //创建并启动第一个线程21.              new XianChengDemo().start();22.              //创建并启动第二个线程23.              new XianChengDemo().start();    24.          }25.      }26.  }27.}
1.package cn.runnable;2.//声明一个类3.public  class RunnableDenmo implements Runnable{4.  private int i;//私有属性5.  //重写run方法6.  public void run(){7.      for(;i<100;i++){8.          //当线程类实现Runnable接口时9.          //如果想获取当前线程,只能用Thread.currentTread()方法10.          System.out.println(Thread.currentThread().getName()+" "+i);11.      }12.  }13.  //主方法14.  public static void main(String[] args) {15.      for(int i=0;i<100;i++){16.          System.out.println(Thread.currentThread().getName()+" "+i);17.          if(i == 20){18.              RunnableDenmo st = new RunnableDenmo();19.              //通过new Thread(target,name)方法创建新线程20.              new Thread(st,"新线程1").start();21.              new Thread(st,"新线程2").start();22.          }23.      }24.  }25.}
1.package cn.callable;2.import java.util.concurrent.Callable;3.import java.util.concurrent.FutureTask;4.public class CallableDemo {5.  public static void main(String[] args) {6.      //创建Callable对象7.      @SuppressWarnings("unused")8.      CallableDemo cl = new CallableDemo();9.      //先使用Lambda表达式创建Callable<Integer>对象10.      //使用FutureTask来包装Callable对象11.      FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)()->{12.          int i = 0;13.          for(;i<100;i++){14.              System.out.println(Thread.currentThread().getName()+"的循环变量的i的值:"+i);15.          }16.          //call()方法可以有返回值17.          return i;18.      });19.      for(int i=0;i<100;i++){20.          if(i==20){21.              //实质还是以Callable对象来创建并启动线程22.              new Thread(task,"有返回值得线程").start();23.          }24.      }25.      try{26.      //获取线程返回值27.      System.out.println("子线程的返回值:"+task.get());28.          }catch(Exception ex){29.      ex.printStackTrace();30.      }31.  }32.}

9.线程同步实例

1.package cn.tongbu;
2.public class TB {
3.  public static void main(String[] args) {
4.      TbDemo tb = new TbDemo(); 
5.      Thread t1 = new Thread(tb,"小明");
6.      Thread t2 = new Thread(tb,"小白");
7.      Thread t3 = new Thread(tb,"小红");
8.      Thread t4 = new Thread(tb,"小黑");
9.      Thread t5 = new Thread(tb,"小华");
10.      t1.start();
11.      t2.start();
12.      t3.start();
13.      t4.start();
14.      t5.start();
15.  }
16.}
17.class TbDemo implements Runnable{
18.  Object obj = new Object();//同步得标记
19.  @Override
20.  public void run() {
21.      //线程同步代码块
22.      //synchronized(obj){
23.      //}
24.      say();//调用同步方法
25.  }
26.  /**
27.   * 同步方法
28.   */
29.  public synchronized void say(){
30.      System.out.println(Thread.currentThread().getName()+"正在通话中....");
31.      try {
32.          Thread.sleep(2000);//2秒
33.      } catch (InterruptedException e) {
34.          e.printStackTrace();
35.      }
36.      System.out.println(Thread.currentThread().getName()+"通话结束!");
37.  }
38.}

10.线程死锁实例

1.package cn.sisuo;
2.public class SiSuoDemo {
3.  public static void main(String[] args) {
4.      new Ss();
5.  }
6.}
7.//警
8.class XingJing{
9.  //同步方法
10.  public synchronized void say(FeiTu f){
11.      System.out.println("警方说先放人质,在实行抓捕");
12.      f.dos();
13.  }
14.  public synchronized void dos(){
15.      System.out.println("同意匪徒要求,在实行抓捕");
16.  }
17.}
18.//匪徒
19.class FeiTu{
20.  //同步方法
21.  public synchronized void say(XingJing x){
22.      System.out.println("匪徒说先找一辆车,在放人质");
23.      x.dos();
24.  }
25.  public synchronized void dos(){
26.      System.out.println("先找一辆车,再放人质");
27.  }
28.}
29./**
30. * 死锁线程
31. * @author JEEP-711
32. *
33. */
34.class Ss implements Runnable{
35.  XingJing xj = new XingJing();
36.  FeiTu ft = new FeiTu();
37.  //模拟线程死锁构造方法
38.  public Ss(){
39.      new Thread(this).start();//为了快速实现
40.      ft.say(xj);
41.  }
42.  @Override
43.  public void run() {
44.      xj.say(ft);
45.  }
46.}

11.多线程同步银行取钱实例

1.package cn.chinabank;//中国银行包
2.//声明一个账户类
3.public class Account {
4.  private String name;//私有属性-姓名
5.  private double number=0.0;//私有属性-余额
6.  //构造方法并传值
7.  public Account(String name,double number){
8.      this.name = name;//当前姓名
9.      this.number = number;//当前金额
10.  }
11.  //取得getName方法
12.  public String getName() {
13.      return name;
14.  }
15.  //设置setName方法
16.  public void setName(String name) {
17.      this.name = name;
18.  }
19.  //取得getNumber方法
20.  public double getNumber() {
21.      return number;
22.  }
23.  //设置setNumber方法
24.  public void setNumber(double number) {
25.      this.number = number;
26.  }
27.}
1.package cn.chinabank;//中国银行包
2.import java.util.Scanner;
3.//声明一个中国银行类
4.public class ChinaBank {
5.  //定义主方法
6.  public static void main(String[] args) {
7.      @SuppressWarnings("resource")
8.      Scanner sc = new Scanner(System.in);//接收控制台键盘输入
9.      System.out.println("----------------------");
10.      System.out.println("----欢迎您进入中国银行-----");
11.      System.out.println("---请选择您需要办理的业务---");
12.      System.out.println("1.存款"+"\t"+" 2.取款"+"\t"+"3.退出");
13.      System.out.println("----------------------");
14.      int sr = sc.nextInt();//接收一个数
15.      //接收用户输入
16.      switch(sr){
17.      //输入1则进入存款功能
18.          case 1:
19.               System.out.println("请您输入存款金额:");//提示用户存款金额
20.              double  number = sc.nextDouble();//接收用户输入金额
21.              @SuppressWarnings("unused")
22.              Account account1 = new Account("asdd",number);//创建Accound对象
23.              System.out.println("请您将钞票叠整齐后放入存钞口..");//提示用户将钞票放入存钞口
24.              try{
25.                  Thread.sleep(2000);//模仿现实银行存钞,设置睡眠时间两秒
26.                  System.out.println("正在存钞,请稍后...");//将输出等待
27.                  Thread.sleep(4000);//将其过程等待四秒
28.                  System.out.println("存款成功!");//则输出存款成功
29.          System.out.println("您的存款金额为:"+number+"\t"+"当前账户余额为:"+number);
30.                                                              //输出存款金额
31.          System.out.println("1.是否打印凭条"+"\t"+"2.是否继续存款"+"\t"+"3.退出");
32.                                                            //用户继续选择
33.                  int sr1 = sc.nextInt();//接收用户输入
34.                  switch(sr1){
35.                  //接收用户是否打印凭条
36.                      case 1:
37.                          System.out.println("dce");
38.                          System.out.println("打印完成,退出打印");
39.                          break;
40.                      //接收用户是否继续存款
41.                      case 2:
42.                          System.out.println("继续存款");
43.                          banking();
44.                          break;
45.                      //接收用户退出
46.                      case 3:
47.                          System.out.println("您已退出存款选项");
48.                          break;
49.                      //防止随意输入    
50.                      default :
51.                          System.out.println("您输入有误,请重新输入");
52.                          break;
53.                  }
54.              }catch(Exception e){
55.                  e.printStackTrace();
56.              }
57.              break;
58.          //输入2则进入取款功能    
59.          case 2:System.out.println("请输出您的取款金额:");
60.              Account account2 = new Account("小明",7000);//创建Account对象并传参数
61.              DrawMoney ka = new DrawMoney(account2,3000);//创建银行卡取款3000元
62.              DrawMoney zhe = new DrawMoney(account2,4000);//创建存折取款2000元
63.              new Thread(ka).start();//开启银行卡线程
64.              new Thread(zhe).start();//开启存折线程
65.              break;
66.          //输入3则退出银行系统
67.          case 3:
68.              System.out.println("您已安全退出中国银行系统,感谢您的光临!");
69.              System.exit(0);//退出指令
70.              break;
71.          //防止用户随意输入则进行友情提示
72.          default :
73.              System.out.println("您输入有误,请重新输入");
74.              break;
75.      }
76.  }
77.  //继承存款金额
78.  public static void banking(){
79.      @SuppressWarnings("resource")
80.      Scanner sc2 = new Scanner(System.in);//接收控制台键盘输入
81.               System.out.println("请您输入存款金额:");//提示用户存款金额
82.              double  number = sc2.nextDouble();//接收用户输入金额
83.              @SuppressWarnings("unused")
84.              Account account2 = new Account("asdd",number);//创建Accound对象
85.              System.out.println("请您将钞票叠整齐后放入存钞口..");//提示用户将钞票放入存钞口
86.              try{
87.                  Thread.sleep(2000);//模仿现实银行存钞,设置睡眠时间两秒
88.                  System.out.println("正在存钞,请稍后...");//将输出等待
89.                  Thread.sleep(4000);//将其过程等待四秒
90.                  System.out.println("存款成功!");//则输出存款成功
91.          System.out.println("您的存款金额为:"+number+"\t"+"当前账户余额为:"+number);
92.                                                       //输出存款金额
93.                  System.out.println("1.是否打印凭条"+"\t"+"\t"+"2.退出");//用户继续选择
94.                  int sr3 = sc2.nextInt();//接收用户输入
95.                  switch(sr3){
96.                  //接收用户是否打印凭条
97.                      case 1:
98.                          System.out.println("dce");
99.                          System.out.println("打印完成,退出打印");
100.                          break;
101.                      case 2:
102.                          System.out.println("您已退出存款选项");
103.                          break;
104.                      //防止随意输入    
105.                      default :
106.                          System.out.println("您输入有误,请重新输入");
107.                          break;
108.                  }
109.              }catch(Exception e){
110.                  e.printStackTrace();
111.              }   
112.  }
113.}
1.package cn.chinabank;//中国银行包
2.//取钱类实现Runnable接口
3.public class DrawMoney implements Runnable{
4.  private Account account;//私有属性-账户
5.  private double money;//私有属性-金额
6.  //构造方法并传值
7.  public DrawMoney(Account account,double money){
8.      this.account = account;//当前账户
9.      this.money = money;//当前金额
10.  }
11.  @Override
12.  public void run() {
13.      while(true){
14.          //线程同步块
15.          //相当于将account队形锁住,只有执行完代码块,才可以释放,其他线程才能拿到
16.          //account必须是同一个
17.          synchronized(account){
18.              //如果账户金额大于等于金额
19.              if(account.getNumber()>=money){
20.                  account.setNumber(account.getNumber()-money);//账户余额减去取款金额
21.                  //输出取款成功,并显示当前账户剩余金额
22.                  System.out.println("你已取款成功,您取款金额为:"+money+"元,剩余金额为:"+account.getNumber()+"元");
23.                  //异常处理
24.                  try{
25.                  Thread.sleep(2000);//调用方法设置睡眠两秒
26.                  }catch(InterruptedException e){
27.                      e.printStackTrace();
28.              }
29.              //否则提示取款不成功,显示当前账户余额
30.              }else{
31.                  System.out.println("对不起,您的账户余额不足,您当前账户余额剩余"+account.getNumber()+"元");//输出账户金额
32.              }
33.          }
34.          break;
35.      }   
36.  }

12.多线程 - 生产者与消费者应用案例

多线程的开发中有一个最经典的操作案例,就是生产者-消费者 
生产者不断生产产品,消费者不断取走产品

1./**
2.* 生产者与消费者应用案例
3.* sleep与wait区别
4.* sleep让当前的线程进入休眠状态,让出cpu,让其他线程执行
5.* 如果用同步的话,有对象锁的时候,是不会释放的,只能等待此线程使用完,才可以使用
6.* wait会释放对象锁,必须等待其他线程唤醒
7.* @author JEEP-711
8.*
9.*/
10.public class ScXf {
11.  public static void main(String[] args) {
12.      Phones p = new Phones(null, null);//创建Phones对象
13.      PhoneSc s = new PhoneSc(p);//创建PhoneSc对象 
14.      PhoneXf x = new PhoneXf(p);//创建PhoneXf对象
15.      new Thread(s).start();//启动生产者线程
16.      new Thread(x).start();//启动消费者线程
17.  }
18.}
19./**
20. * 手机生产者,单独的生产者,实现Runnable接口
21. * @author JEEP-711
22. *
23. */
24.class PhoneSc implements Runnable{
25.  private Phones phones;
26.  public PhoneSc(Phones phones){
27.      this.phones = phones;
28.  }
29.  @Override
30.  public void run() {
31.      //不断地生产20份,生产的过程
32.      for (int i = 0; i < 50; i++) {
33.          if(i%2==0){
34.              phones.set("金立手机", "金立手机,中国造!");
35.          }else{
36.              phones.set("小米手机", "小米手机,为发烧而生!");
37.          }
38.      }
39.  }
40.}
41./**
42.* 手机消费者,顾客
43. * @author JEEP-711
44. *
45. */
46.class PhoneXf implements Runnable{
47.  private Phones phones;
48.  public PhoneXf(Phones phones){
49.      this.phones = phones;
50.  }
51.  @Override
52.  public void run() {
53.      for (int i = 0; i < 50; i++) {
54.          phones.get();//调用消费产品方法
55.      }
56.  }
57.}
58./**
59.* 产品的对象,生产的手机
60.* @author JEEP-711
61.*
62. */
63.class Phones{
64.  @Override
65.  public String toString() {
66.      return "Phones [name=" + name + ", content=" + content + "]";
67.  }
68.  private String name;
69.  private String content;
70.  /**true表示可以生产,false表示可以消费
71.   * 作为标记,如何flag等于true表示可以生产,如何flag等于false表示不可生产
72.   * 如果flag等于false表示可以消费状态,可以取走,flag等于true表示不能取走
73.   * 解决重复值得问题
74.   */
75.  private boolean flag = true;//表示可以生产,false表示可以消费
76.  //构造方法
77.  public Phones(String name, String content) {
78.      super();
79.      this.name = name;
80.      this.content = content;
81.  }
82.  //取得名称方法
83.  public String getName() {
84.      return name;
85.  }
86.  //设置名称方法
87.  public void setName(String name) {
88.      this.name = name;
89.  }
90.  //取得内容方法
91.  public String getContent() {
92.      return content;
93.  }
94.  //设置内容方法
95.  public void setContent(String content) {
96.      this.content = content;
97.  }
98.  /**
99.   * 通过同步,解决了取值错误问题
100.   * @param name
101.   * @param content
102.   */
103.  //生产制造同步方法
104.  public synchronized void set(String name, String content){
105.      if(!flag){
106.          try {
107.          //调用该方法,当前线程进入等待池等待状态,没有指定时间,
108.          //需要其他线程唤醒,释放对象锁,让出cpu
109.              this.wait();
110.          } catch (InterruptedException e) {
111.              e.printStackTrace();
112.          }
113.      }
114.      this.setName(name);
115.      try {
116.          Thread.sleep(300);
117.      } catch (InterruptedException e) {
118.          e.printStackTrace();
119.      }
120.      this.setContent(content);
121.      flag = false;//表示可以消费,取走
122.      this.notify();//唤醒在该监视器上的一个线程
123.  }
124.  //消费产品同步取值方法
125.  public synchronized void get(){
126.      if(flag){
127.          try {
128.              //调用该方法,当前线程进入等待池等待状态,没有指定时间,
129.              //需要其他线程唤醒,释放对象锁,让出cpu
130.              this.wait();
131.          } catch (InterruptedException e) {
132.              e.printStackTrace();
133.          }
134.      }
135.      try {
136.          Thread.sleep(300);
137.      } catch (InterruptedException e) {
138.          e.printStackTrace();
139.      }
140.      System.out.println(this.getName()+":"+this.getContent());
141.      flag = true;
142.      this.notify();
143.  }
144.}

13.线程池

线程池是预先创建线程的一种技术,线程池在还没有任务到来之前,创建一定数量的线程,放入空闲队列中,然后对这些资源进行复用,减少频繁的创建和销毁对象 
java里面线程池的顶级接口是Executor,是一个执行线程的工具 
线程池接口是ExecutorServise 
1.java.util.concurrent包:并发编程中很常用的实用工具包 
2.Executor接口:执行已提交Runnable任务的对象

ExecutorService接口: 
Executor提供了管理终止的方法,以及可能为跟踪一个或 
多个异步任务执行状况而发生的Future的方法 
Executors类:此包中所定义的Executor、ExecutorService等的工厂和实现方法 
在Executors类里提供了一些静态工厂,生成一些常用的线程池 
newSingleThreadExecutor: 
创建一个单线程的线程池,这个线程池只有一个线程在工作,也就是相当于单线程 
串行执行所有的任务,如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它,此线程池保证所有任务的执行顺序按照任务的提交顺序执行 
newFixedThreadPool: 
创建固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程 
newCacheadThreadPool: 
创建一个可缓存的线程池,如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务,此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说jvm)能够创建的最大线程大小 
newSchediledThreadPool: 
创建一个大小无限制的线程池,此线程池支持定时以及周期性执行任务需求

使用线程池的步骤:

  • (1)调用Executors类的静态工厂方法创建一个ExecutorService对象或ScheduledExecutorService对象,其中前者代表简单的线程池,后者代表能以任务调度方式执行线程的线程池
  • (2)创建Runnable实现类或Callable实现类的实例,作为线程执行任务
  • (3)调用ExecutorService对象的submit方法来提交Runnable实例或Callable实例;或调用ScheduledExecutorService的schedule来执行线程
  • (4)当不想提交任何任务时调用ExecutorService对象的shutdown方法来关闭线程池

初学(JAVA 多线程 高级阶段) 难点: ★★★★★

希望每一篇文章都能够对读者们提供帮助与提升,这乃是每一位笔者的初衷


感谢您的阅读 欢迎您的留言与建议

时间: 2024-10-17 12:58:09

第12篇-JAVA 多线程的相关文章

Java总结篇系列:Java多线程(三)

一.一个典型的Java线程安全例子 1 public class ThreadTest { 2 3 public static void main(String[] args) { 4 Account account = new Account("123456", 1000); 5 DrawMoneyRunnable drawMoneyRunnable = new DrawMoneyRunnable(account, 700); 6 Thread myThread1 = new Thr

Java总结篇系列:Java多线程(二)

四.Java多线程的阻塞状态与线程控制 上文已经提到Java阻塞的几种具体类型.下面分别看下引起Java线程阻塞的主要方法. 1.join() join -- 让一个线程等待另一个线程完成才继续执行.如A线程线程执行体中调用B线程的join()方法,则A线程被阻塞,知道B线程执行完为止,A才能得以继续执行. 1 public class ThreadTest { 2 3 public static void main(String[] args) { 4 5 MyRunnable myRunna

关于Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文)

Java多线程的线程同步和线程通信的一些小问题(顺便分享几篇质量高的博文) 前言:在学习多线程时,遇到了一些问题,这里我将这些问题都分享出来,同时也分享了几篇其他博客主的博客,并且将我个人的理解也分享给大家. 一.对于线程同步和同步锁的理解(注:分享了三篇高质量的博客) 以下我精心的挑选了几篇博文,分别是关于对线程同步的理解和如何选择线程锁以及了解线程锁的作用范围. <一>线程同步锁的选择 1. 这里我推荐下Java代码质量改进之:同步对象的选择这篇博文. 2. 以上推荐的博文是以卖火车票为例

Java总结篇系列:Java多线程(一)

多线程作为Java中很重要的一个知识点,在此还是有必要总结一下的. 一.线程的生命周期及五种基本状态 关于Java中线程的生命周期,首先看一下下面这张较为经典的图: 上图中基本上囊括了Java中多线程各重要知识点.掌握了上图中的各知识点,Java中的多线程也就基本上掌握了.主要包括: Java线程具有五中基本状态 新建状态(New):当线程对象对创建后,即进入了新建状态,如:Thread t = new MyThread(); 就绪状态(Runnable):当调用线程对象的start()方法(t

java多线程12设计模式

1.Single Threaded Execution Pattern(单线程运行模式) 2.Immutable Pattern(一成不变的模式) 3.Guarded Suspension Pattern(国防暂停模式) 4.Balking Pattern(止步模式,阻行模式) 5.Producer-Consumer Pattern(生产者-消费者模式) 6.Read-Write Lock Pattern(读-写锁模式) 7.Thread-Per-Message Pattern(每一个消息一个线

带你玩转java多线程系列 “道篇” 多线程的优势及利用util.concurrent包测试单核多核下多线程的效率

java多线程 “道篇” - 多线程的优势及用concurrent包测试单核多核下多线程的效率 1 超哥对于多线程自己的理解 2 测试代码 3 CountDownLatch这个同步辅助类科普 4 如何把电脑设置成单核 5 测试结果 1 超哥对于多线程自己的理解 超哥的理解:对于多线程,无非是对于顺序执行下任务的一种抽取和封装,将原来顺序执行的任务单独拿出来放到线程类的run方法中,通过线程类的start方法进行执行,对于多线程访问共同资源时,我们需要加锁,也就是只有某个线程在拥有锁的时候,才能够

(转)java多线程的一篇好文

云转型基石ThinkServer特性解析 2013-05-29 10:47 佚名 importnew 字号:T | T 本文只是一些针对初学者或者新手的问题,如果你已经具备良好的基础,那么你可以跳过本文,直接尝试针对进阶水平的Java多线程编程问题及解答. AD:WOT2014:用户标签系统与用户数据化运营培训专场 英文原文:java-success.blogspot,编译:王晓杰 如果你即将去一家从事大型系统研发的公司进行Java面试,不可避免的会有多线程相关的问题.下面是一些针对初学者或者新

Java多线程系列--“基础篇”01之 基本概念

多线程是Java中不可避免的一个重要主体.从本章开始,我们将展开对多线程的学 习.接下来的内容,是对“JDK中新增JUC包”之前的Java多线程内容的讲解,涉及到的内容包括,Object类中的wait(), notify()等接口:Thread类中的接口:synchronized关键字. 注:JUC包是指,Java.util.concurrent包,它是由Java大师Doug Lea完成并在JDK1.5版本添加到Java中的. 在进入后面章节的学习之前,先对了解一些多线程的相关概念.线程状态图

Java多线程学习篇(三)Lock

Lock 是Java多线程的一个同步机制,用来控制线程对共享资源的访问.线程在执行同步方法或者代码块之前必须先获得一个锁. Lock 的 lock() 和 unlock() 方法; lock():获得一个锁,如果锁不可用,则当前线程将因线程调度目的而被禁用,并在获得锁之前处于休眠状态. unlock():释放掉获得的锁. Lock的作用范围: 若 Lock 是静态的,则作用范围是整个类. public class Test { public static void main(String[] a