day006-多线程

1、 线程概念

a)     什么是主线程

Java程序启动过程中自动创建的并执行main方法的线程称为主线程

  •  主线程的执行路径:

    从main方法开始到main方法结束

b)什么是子线程

除了主线程的其它所有线程都是子线程。

  •   子线程的执行路径:

    从run方法到run方法结束

C)线程的运行模式

  •  分时式模式:每个线程平均分配CPU使用权,每一个线程使用CPU的时间是相同的。
  • 抢占式模式:优先级高的线程抢到CPU的概率高,如果优先级都相同,就是随机抢占cpu的使用权
  •   Java程序的线程运行模式属于抢占式模式

2、 多线程内存图解

 

    三句重要的小结:

扫描二维码关注公众号,回复: 1401232 查看本文章

    每一个线程都会有自己独立的栈空间

    进程的堆空间是被该进程的所有线程共享

    在同一个线程中,代码是按顺序从上向下执行的。

 

3、 创建线程的两种方式

3.1创建一个类继承Thread类

重写run方法:将线程任务写在run()方法体内

调用start()方法,开启线程干活

例子:

 1 package com.yangzhihui.level03.test02;
 2 
 3 import java.util.Random;
 4 
 5 public class ThreadDemo {
 6     public static void main(String[] args) {
 7         MyThread myThread01 = new MyThread("线程1:");
 8         MyThread myThread02 = new MyThread("线程2:");
 9         myThread01.start();
10         myThread02.start();
11     }
12 }
13 
14 class MyThread extends Thread {
15     public MyThread(String name) {
16         super(name);
17     }
18 
19     @Override
20     public void run() {
21         Random random = new Random();
22 
23         int sum = 0;
24         for (int i = 0; i < 30; i++) {
25             int num;
26             num = random.nextInt(901) + 100;
27             sum += num;
28             System.out.println(Thread.currentThread().getName() + num);
29         }
30 
31         System.out.println("10个100~1000随机数的和是:" + Thread.currentThread().getName() + sum);
32     }
33 }
View Code

 

3.2 实现Runnable

  •  步骤:

    A)创建一个类实现Runnable接口,重写方法:将线程任务相关的代码写在run方法中

    B) 创建实现类对象,根据实现类对象创建Thread对象

    C) 调用线程对象的start方法开启线程:会在新的路径中执行run方法

  •   使用Runnable接口的好处:

    屏蔽了Java类单继承的局限性

    可以更好的在多线程之间共享数据

    将线程和任务进行分离,降低了程序的耦合性

    为线程池提供前提条件

  •   匿名内部类

    什么时候使用匿名内部类创建线程

      当任务只需要执行一次时,可以考虑使用匿名内部类

例子:

实现Runnable接口
1.定义一个子任务类,实现Runnable接口;
2.在子任务类中重写run方法,在run方法中打印子线程的名称;
3.定义一个测试类;
4.在main方法中打印主线程的名称;
5.在main方法中创建一个子任务对象;
6.在main方法中创建一个Thread类的对象,并把子任务对象传递给Thread类的构造方法;
7.调用Thread类对象的start方法开启子线程;

 1 package level01.test03;
 2 
 3 class MyRunnable03 implements Runnable {
 4     @Override
 5     public void run() {
 6         System.out.println("实现接口方式:子线程的名称--" + Thread.currentThread().getName());
 7     }
 8 }
 9 
10 
11 package level01.test03;
12 
13 public class TestMain {
14     public static void main(String[] args) {
15         //打印主线程的名称
16         System.out.println(Thread.currentThread().getName());
17 
18      
19         //通过Runnable接口实现类对象,开启多线程
20         MyRunnable03 myRunnable03 = new MyRunnable03();
21         new Thread(myRunnable03, "线程2").start();
22     }
23 }
View Code

4、 线程安全

4.1 概念

        指两个或两个以上的线程在同时操作一个共享资源时仍然得到正确的结果则就是线程安全。

  • 线程安全的案例

     火车站买票案例

4.2实现线程安全的方式

4.2.1:同步代码块

同步:每一个线程按顺序执行

异步:可以同时执行(多线程的代名词)

同步代码块格式:                                        

Synchronized(锁对象){

       //操作共享资源的代码

}

同步代码块的原理:

       能够保证同一时间只有一个对象操作共享资源的代码

锁对象注意事项:

       锁对象可以使任意类型的对象。

       所有线程必须共用一把锁

例子:

1. 有100个限量版的水杯,但是只能通过实体店和官网才能进行购买,并且分别统计卖了多少。请用线程进行模
拟并设置线程名称用来代表售出途径,再将信息打印出来。
比如(实体店卖出第1个,总共剩余n个..)

 1 package level01.test06;
 2 
 3 public class Cup implements Runnable{
 4     private int sellNum = 0;
 5     private final int NUM = 100;
 6     private String cupLock = "lock";
 7     @Override
 8     public void run() {
 9         while(true){
10             synchronized (cupLock){
11                 if(sellNum < NUM){
12                     try {
13                         Thread.sleep(20);
14                     } catch (InterruptedException e) {
15                         e.printStackTrace();
16                     }
17                     System.out.println(Thread.currentThread().getName() + "卖出第" + (++sellNum) +
18                         "个,总共剩余" + (NUM - sellNum));
19                 }else {
20                     System.out.println(Thread.currentThread().getName() + "买完了");
21                     break;
22                 }
23             }
24         }
25     }
26 }
27 
28 
29 package level01.test06;
30 
31 public class TestMain {
32     public static void main(String[] args) {
33         Cup cup = new Cup();
34 
35         new Thread(cup, "实体店").start();
36         new Thread(cup, "官网").start();
37     }
38 }
View Code

4.2.2 同步方法

同步方法格式:
       修饰符 synchronized 返回值类型 方法名(参数列表){…}

同步方法:

能够保证同一时间,只有一个线程执行方法体内的代码

同步方法的注意事项

  •   静态同步方法锁对象是:类名.class

    附录:每一个类都会有一个Class对象,而且是唯一的。

    Class c1 = TicketThread.class;

    Class c2 = TicketThread.class;

  •   非静态同步方法锁对象是:this
  •  
  • 静态方法和非静态方法的选择

    当方法体内部需要访问到任何非静态成员时,可以定义为静态方法。否则定义为非静态方法。

例子:

1. 有一辆班车除司机外只能承载80个人,假设前中后三个车门都能上车,如果坐满则不能再上车。请用线程模
拟上车过程并且在控制台打印出是从哪个车门上车以及剩下的座位数。
比如:(前门上车---还剩N个座...)

 1 package level01.test07;
 2 
 3 public class Bus implements Runnable {
 4     private final int NUM = 80;
 5     private int seatNum = 0;
 6 
 7     @Override
 8     public void run() {
 9         while(seatNum < NUM){
10             aboardBus();
11         }
12         System.out.println("车座已满");
13     }
14 
15     public synchronized void aboardBus(){
16         if(seatNum < NUM){
17             try {
18                 Thread.sleep(100);
19             } catch (InterruptedException e) {
20                 e.printStackTrace();
21             }
22             seatNum++;
23             System.out.println("从" + Thread.currentThread().getName() +
24                     "上车,剩下" + (NUM - seatNum) + "座位");
25         }
26     }
27 }
28 
29 
30 package level01.test07;
31 
32 import java.io.BufferedWriter;
33 
34 public class TestMain {
35     public static void main(String[] args) {
36         Bus bus = new Bus();
37 
38         new Thread(bus, "前门").start();
39         new Thread(bus, "中门").start();
40         new Thread(bus, "后门").start();
41     }
42 }
View Code

4.2.3

Lock接口:提供了比synchronized代码块和synchronized方法更广泛的锁操作,同步代码块/同步方法具有的功能Lock都有,除了之外更强大,更体现面向对象。

  •   常用方法:

      lock():上锁

   unlock():释放锁

  •  Lock使用注意事项

     lock()和unlock()必须成对出现。一定要 注意在线程执行完共享代码后,要释放锁。

例子:

写一个卖票的程序,
1. 写一个类,该类继承Thread,定义一个变量记录总票数。
2. 开启四个卖票窗口(开始四个线程),同时执行卖票的程序。
要求每卖一张票在控制台输出:当前窗口为:窗口a卖了一张票,剩余票数为19。其中窗口a为线程的名字。

 1 package level01.test08;
 2 
 3 import java.util.concurrent.locks.ReentrantLock;
 4 
 5 public class Ticket implements Runnable{
 6     private final int NUM = 20;
 7     private int seatNum = 0;
 8     private ReentrantLock seatLock = new ReentrantLock();
 9 
10     @Override
11     public void run() {
12         while(seatNum < 20){
13             seatLock.lock();
14 
15             try {
16                 if(seatNum < NUM){
17                     Thread.sleep(100);
18                     seatNum++;
19                     System.out.println("当前窗口:" + Thread.currentThread().getName() +
20                             "卖了一张牌,剩余票数为" + (NUM - seatNum));
21                 }else {
22                     System.out.println(Thread.currentThread().getName() + "票卖完了");
23                 }
24             } catch (InterruptedException e) {
25                 e.printStackTrace();
26             }finally {
27                 seatLock.unlock();
28             }
29         }
30     }
31 }
32 
33 
34 package level01.test08;
35 
36 public class TestMain {
37     public static void main(String[] args) {
38         Ticket ticket = new Ticket();
39         new Thread(ticket,"线程1:").start();
40         new Thread(ticket,"线程2:").start();
41         new Thread(ticket,"线程3:").start();
42         new Thread(ticket,"线程4:").start();
43     }
44 }
View Code

5、 线程状态

NEW(新建):新建状态,刚刚创建出来,还没有调用start方法

Runnable(可运行):可运行状态,有资格运行,可能正在运行中,也可以不是正在运行

Blocked(锁阻塞):等待其他线程释放锁对象

Wating(无限等待):无限等待

TimedWating(计时等待):限时等待,调用了sleep方法或者wait(毫秒数),需要被其他线程调用notify方法唤醒

Teminated(被终止):死亡状态,任务执行完毕或调用了stop方法

猜你喜欢

转载自www.cnblogs.com/gzyzhui/p/9125328.html