1 /* 2 多线程: 3 4 进程:正在执行中的程序,一个应用程序启动后在内存中运行的那片空间。进程具有动态性和并发性。 5 6 线程:进程中的一个执行单元。负责进程中的程序的运行的。一个进程中至少要有一个线程。 7 一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。 8 9 程序启动了多线程,有什么应用呢? 10 可以实现多部分程序同时执行。专业术语称之为 并发。 11 12 多线程的使用可以合理使用cpu的资源,如果线程过多会导致降低性能。 13 14 cpu在处理程序时是通过快速切换完成的。在我们看来好像随机一样。 15 */
1 //03-多线程-主线程的运行方式&创建线程的第一种方式。 2 /* 3 通过代码来演示之前和之后的区别。 4 5 在之前的代码中,jvm启动后,必然有一个执行路径(线程)从main方法开始的, 6 一直执行到main方法结束。这个线程在java中称之为主线程。 7 8 当主线程在这个程序中执行时,如果遇到了循环而导致停留时间过长, 9 就无法执行下面的程序。 10 可不可以实现一个主线程负责执行其中一个循环,由另一个线程负责其他代码的执行。 11 实现多部分的代码同时执行。 12 这就是多线程技术可以解决的问题。 13 14 该如何创建线程呢? 15 16 通过API中的英文Thread搜索。查到了Thread类。 17 通过阅读Thread类中的描述。 18 创建线程有两种方式。 19 1,继承Thread类。 20 1.1 定义一个类去继承Thread. 21 1.2 重写run方法。 22 1.3 创建子类对象。就是创建线程对象。 23 1.4 调用strat方法。开启线程并让线程执行,同时还会告诉jvm调用run方法。 24 25 26 为什么要这么做? 27 继承Thread类:因为Thread类描述线程事物,具备线程应该有的功能。 28 那为什么不直接创建Thread类的对象呢? 29 Thread t1 = new Thread(); 30 t.strat();//这么做没有错,但是该strat调用的是Thread类中的run方法, 31 而这个run方法没有做什么事情,更重要的是这个run方法中并没有定义我们需要让 32 线程执行的代码。 33 34 创建线程的目的是什么?是为了建立单独的执行路径,让多部分代码实现同时执行。 35 也就是线程创建并执行需要给定代码(线程的任务)。 36 对于之前所讲的主线程,它的任务定义在了主函数中。 37 自定义的线程需要执行的任务都定义在run方法中。 38 Thread类中run方法内部的任务并不是我们所需要的。只要重写这个run方法就行了。 39 既然Thread类已经定义了线程任务的位置,只要在位置中定义任务代码即可。 40 所以进行了重写run方法动作。 41 42 43 多线程执行时,在栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间。 44 进行方法的压栈和弹栈。 45 46 当执行线程的任务结束了,线程自动在栈内存中释放了。 47 当所有的执行线程都结束了,进程就结束了。 48 49 //获取线程名称。可以通过Thread类中的一个currentThreadd()方法。怎么获取名称呢? 50 getName(); 51 52 Thread.currentThread().getName(); 53 54 主线程的名称是:main 55 自定义的线程名称是:Thread-1 线程多个时,数字依次递增。 56 */ 57 58 class Demo extends Thread 59 { 60 private String name; 61 Demo(String name) 62 { 63 this.name = name; 64 } 65 public void run() 66 { 67 //int[] arr = new int[3]; 68 //System.out.println(arr[4]); 69 for(int x=1;x<=20;x++) 70 { 71 System.out.println("name="+name+"....."+Thread.currentThread().getName()+".."+x); 72 } 73 } 74 } 75 76 class ThreadDemo1 77 { 78 public static void main(String[] args) 79 { 80 //创建了两个线程对象。 81 Demo d1 = new Demo("小强"); 82 Demo d2 = new Demo("旺财"); 83 d2.start();//将d2这个线程开启。 84 d1.run();//由主线程负责。 85 86 87 /* 88 线程对象调用run方法和调用start方法的区别? 89 调用run方法不开启线程,仅是对象调用方法。 90 调用start开启线程,并让jvm调用run方法在开启的线程中执行。 91 */ 92 } 93 }
1 //创建线程的第二种方式。 2 /* 3 创建线程的第二种方式:使用Runnable接口。 4 1,定义类实现Runnable接口。//避免了单继承的局限性。 5 2,覆盖接口中的run方法。将线程任务代码定义到run方法中。 6 3,创建Thread类的对象。 //只有创建Thread类的对象才可以创建线程。 7 4,将Runnable接口的子类对象作为参数传递给Thread类的构造函数。 8 因为线程任务已被封装到Runnable接口的run方法中,而这个run方法所属于Runnable接口的子类对象。 9 所以将这个子类对象作为参数传递给Thread的构造函数,这样,线程对象创建时就可以明确要运行的任务。 10 5,调用Thread类的strat方法开启线程。 11 12 第二种方式实现Runnable接口避免了单继承的局限性。所以,较为常用。 13 实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。 14 继承Thread类,线程对象和线程任务耦合在一起,一旦创建Thread类的子类对象,既是线程对象,又有线程任务。 15 实现Runnable接口,将线程任务单独分离出来封装成对象。类型就是Runnable接口类型。 16 Runnable接口对线程对象和线程任务解耦。 17 18 //通过源代码的形式讲解了一些Runnable接口的子类对象作为参数传递给Thread构造函数的原因。 19 class Thread{ 20 private Runnable target; 21 22 Thread(Runnable traget) 23 { 24 this.target = target; 25 } 26 public void run() { 27 if (target != null) { 28 target.run(); 29 } 30 } 31 public void strat() 32 { 33 run(); 34 } 35 } 36 37 Demo d = new Demo();//Runnable d = new Demo(); 38 Thread t = new Thread(d); 39 t.strat(); 40 */ 41 42 class Demo implements Runnable 43 { 44 private String name; 45 Demo(String name) 46 { 47 this.name = name; 48 } 49 //覆盖了接口Runnable的run方法。 50 public void run() 51 { 52 for(int x=1;x<20;x++) 53 { 54 System.out.println("name="+name+"....."+Thread.currentThread().getName()+".."+x); 55 } 56 } 57 } 58 class ThreadDemo2 59 { 60 public static void main(String[] args) 61 { 62 //创建Runnable子类的对象。注意它并不是线程对象。 63 Demo d = new Demo("Demo"); 64 //创建Thread类的对象,将Runnable接口的子类对象作为参数传递给Thread类的构造函数。 65 Thread t1 = new Thread(d); 66 Thread t2 = new Thread(d); 67 t1.start(); 68 t2.start(); 69 70 System.out.println(Thread.currentThread().getName()+"------>"); 71 } 72 }
1 //多线程的安全问题产生及原因以及解决思路 2 /* 3 案例:售票的例子。 4 5 售票的动作需要同时执行。所以要使用多线程技术。 6 7 发生了线程安全问题,出现了错误的数据;0 -1 -2 8 9 问题产生的原因: 10 1,线程任务在操作共享的数据。 11 2,线程任务操作共享数据的代码有多条(运算有多个)。 12 13 解决思路: 14 只要让一个线程在执行线程任务时,将多条操作共享数据的代码执行完, 15 在执行过程中,不要让其他线程参与运算就行了。 16 17 18 代码体现: 19 Java中解决此问题通过代码块来完成的, 20 这个代码代码块称之为同步代码块 synchronized 21 格式: 22 synchronized(对象) 23 { 24 //需要被同步的代码。 25 } 26 27 同步好处:同步代码块解决了多线程安全问题。 28 29 同步的弊端: 30 降低了程序的性能。 31 32 同步前提: 33 必须保证多个线程在同步中使用的是同一个锁 34 解决了什么问题?当多线程安全问题发生时,加入了同步后,问题依旧发生时, 35 就要通过这个同步的前提来判断同步是否正确。 36 */ 37 38 class Ticket implements Runnable 39 { 40 //1,描述票的数量 41 private int tickets = 100; 42 43 //2,售票的动作。这个动作需要被多线程执行,那就是线程任务代码, 44 //需要定义在run方法中。 45 //记住,线程任务中通常都有循环结构。 46 private Object obj = new Object(); 47 public void run() 48 { 49 while(true) 50 { 51 synchronized(obj)//如果写 new Object() 则用的就不是同一个锁。 52 { 53 if(tickets > 0) 54 { 55 //要让线程在这里稍停,模拟问题的发生。sleep 看到了 0 -1 -2 这样的错误的数据,这就是传说中的安全问题。 56 try{Thread.sleep(1);}catch(InterruptedException e){/*未写处理方式,后面讲*/} 57 58 System.out.println(Thread.currentThread().getName()+"...."+tickets--);//打印线程名称 59 } 60 } 61 } 62 } 63 } 64 65 66 class ThreadDemo3 67 { 68 public static void main(String[] args) 69 { 70 //1,创建Runnable接口的子类对象。 71 Ticket t = new Ticket(); 72 73 //创建四个线程对象。并将Runnable接口的子类对象作为参数传递给Thread的构造函数。 74 Thread t1 = new Thread(t); 75 Thread t2 = new Thread(t); 76 Thread t3 = new Thread(t); 77 Thread t4 = new Thread(t); 78 79 //3,开启四个线程 80 t1.start(); 81 t2.start(); 82 t3.start(); 83 t4.start(); 84 } 85 }