java-API之多线程

初识多线程

进程:正在运行的程序。也就是代表了程序锁占用的内存区域。一个软件的运行,必须启动至少一个进程,也可以多个。

单线程:一个进程如果只启动了一个线程在干活,这个程序就是单线程程序

多线程:一个进程如果启动了多个线程在干活,这个程序就是多线程程序,执行效率高。

进程和线程的关系:一个软件的运行以来至少一个进程,进程的运行也是至少依赖一个线程,线程是调度运算的最小单位。(面试题)

并发:是指同一时刻,多个程序在抢占共享资源,同时抢占CPU来执行程序

并行:是指同一时刻,有多个CPU在干活,但是一个CPU只干一件事

线程的状态:新建状态、可运行状态、运行状态、终止状态、阻塞状态。

多线程编程

构造方法

  • Thread() 分配新的 Thread 对象。
  • Thread(Runnable target) 分配新的 Thread 对象。
  • Thread(Runnable target, String name) 分配新的 Thread 对象。
  • Thread(String name) 分配新的 Thread 对象。

常用方法

  • static Thread currentThread() 返回对当前正在执行的线程对象的引用。
  • long getId() 返回该线程的标识符。
  • String getName() 返回该线程的名称。
  • void run() 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
  • static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
  • void setName(String name) 改变线程名称,使之与参数 name 相同。
  • void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
  • void stop() 已过时。 终止线程

实现多线程方法一——继承Thread类

 1 public class Test2_Thread {
 2 
 3     public static void main(String[] args) {
 4         
 5         // 创建线程
 6         MyThread thread = new MyThread("线程1");
 7         // 启动线程
 8         // start()从新建状态变成可执行状态,自动调用run方法
 9         thread.start();
10         
11         MyThread thread2 = new MyThread("线程2");
12         thread2.start();
13         
14     }
15 }
16 
17 
18 // 创建多线程类 继承于  Thread 类
19 class MyThread extends Thread {
20     public MyThread() {
21         super();
22     }
23 
24     public MyThread(String name) {
25         super(name);
26     }
27     
28     // 把多线程业务写到run方法内
29     @Override
30     public void run() {
31         for (int i = 0; i < 10; i++) {
32             // getName() 获取当前线程的名称 
33             System.out.println(getName()+ "--"+ i);
34         }
35     }
36 }

 

实现多线程方法一——实现Runnable接口

public class Test3_Runnable {
    
    public static void main(String[] args) {
        //  创建多个线程对象执行任务
        MyRunnable runable = new MyRunnable();
        // 把接口类型的对象,转成Thread类型,因为想调用Thread的start()启动线程——Thread(Runable target)
        Thread thread = new Thread(runable,"线程1");
        // 调用start()启动线程
        thread.start();
        
        
        Thread thread2 = new Thread(runable,"线程2");
        thread2.start();
    }
}

// 创建实现类,实现Runnable接口
class MyRunnable implements Runnable {
    //  把多线程业务放在run方法内
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            // Thread.currentThread()获取当前正在执行任务的线程对象
            System.out.println(Thread.currentThread().getName()+"=="+i);
        }
    }
    
}

 Thread和Runable实现多线的比较

方式  优点  缺点
Thread 编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。 线程类已经继承了Thread类,所以不能再继承其他父类
Runnable

线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。

在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。

编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。

多线程售票案例

 1 public class Test1_MyTicket {
 2     public static void main(String[] args) {
 3         Runnable mt = new MyTicket();
 4         
 5         // 创建线程
 6         Thread t1 = new Thread(mt,"窗口1");
 7         // 启动线程
 8         t1.start();
 9         Thread t2 = new Thread(mt,"窗口2");
10         t2.start();
11         Thread t3 = new Thread(mt,"窗口3");
12         t3.start();
13         Thread t4 = new Thread(mt,"窗口4");
14         t4.start();
15     }
16 }
17 
18 
19 class MyTicket implements Runnable {
20     int count = 100; // 记录当前票的数量
21 
22     @Override
23     public void run() {
24         while(true) {
25             if(count > 0) {
26                 try {
27                     Thread.sleep(100); //让线程休息一会
28                     // Thread.currentThread().getName() 获取当前线程名称
29                     System.out.println(Thread.currentThread().getName() + "==" + count--); 
30                 } catch (InterruptedException e) {
31                     e.printStackTrace();
32                 }
33             }else {
34                 break;
35             }
36         }
37         
38     }
39 }

 经过测试,我们发现以上代码出现以下问题:

  1. 问题1: 重复卖票,同一张票卖给了多人
  2. 问题2:超卖,也就是票数出现了负数

同步锁解决方案

异步:多个线程可以同时操作共享数据,互相不需要等待。效率较高,但是数据存在安全性问题。

同步:同一时刻只能有一个线程操作共享资源,相当于独占资源(其它线程必须等待其完成才能继续)。牺牲了程序的执行效率,提高了资源的安全性。

同步锁synchronized使用场景:将那些有多线程并发数据存在安全隐患的代码用同步包起来。它可以修饰代码块,也可以修饰方法,一般情况下使用代码块;如果方法里都是同步代码,可以把synchronized直接写在方法上。在保证解决数据安全的前提下,锁定的范围越小,程序的效率越高。多个线程,必须使用同一个锁(也就是关键字synchronized后面对象参数所指的对象,多个线程要使用同一对象,这样锁住共享资源,否则仍有安全隐患)。代码演示如下:

 

 1 public class Test2_Synchronized {
 2     public static void main(String[] args) {
 3         Runnable run = new MyTicket2();
 4 
 5         Thread t1 = new Thread(run, "窗口1");
 6         t1.start();
 7         Thread t2 = new Thread(run, "窗口2");
 8         t2.start();
 9         Thread t3 = new Thread(run, "窗口3");
10         t3.start();
11         Thread t4 = new Thread(run, "窗口4");
12         t4.start();
13     }
14 }
15 
16 class MyTicket2 implements Runnable {
17     int count = 100;
18 
19     @Override
20     public void run() {
21         while (true) {
22             // synchronized同步代码块,把所有的问题的代码包起来,来实现同步代码
23             // 必须是多个线程使用的是同一个对象
24 
25             synchronized (this) {  // synchronized后面参数中的对象,必须是多个线程使用的是同一个对象
26                 if (count > 0) {
27                     try {
28                         Thread.sleep(10);
29                     } catch (InterruptedException e) {
30                         e.printStackTrace();
31                     }
32                     System.out.println(Thread.currentThread().getName() + "===" + count--);
33 
34                 } else {
35                     break;
36                 }
37             }
38         }
39     }
40 }

 

posted @ 2020-03-10 20:43  技术狂-CYL  阅读(198)  评论(0编辑  收藏  举报