黑马Java学习笔记之-----多线程
---------------------- android培训、java培训、期待与您交流! ----------------------
线程:
线程是一个程序内部的顺序控制流(执行路径)。
线程和进程的区别:
(1):每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销。
(2):线程可以看成是轻量级的进程,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换的开销小。
(3):多进程:在操作系统中能同时运行多个任务(程序)。
(4):多线程:在同一应用程序中有多个顺序流同时执行。
- Java的线程是通过java.lang.Thread类来实现的。
- VM启动时会有一个由主方法(main方法)所定义的线程。
- 每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。run()方法结束,线程就结束了。
- 通过调用Thread类的start()方法来启动一个线程。
创建线程的第一种方式:继承Thread类。
步骤:
1 ,定义类继承Thread
2,复写Thread类中的run方法
目的:将自定义代码存储在run方法中,让线程运行。
3,调用线程的start方法,该方法两个作用:启动线程,调用run方法
发现每一次结果都不同。
因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)
cpu在做着快速的切换,以达到看上去是同时运行的效果。
我们可以形象的把多线程的运行行为看作在互相抢夺cpu的执行权。
这就是多线程的一个特性,随机性。谁抢到,谁执行,至于执行多长,cpu说了算。
创建线程的第二种方式:实现Runable接口。
步骤:
1,定义类实现Runnable接口
2,覆盖Runnable接口中的run方法。
将线程要运行的代码存放在该run方法中。
3,通过Thread类建立线程对象。
4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数。
因为,自定义的run方法所属的对象是Runnable接口的子类对象。
所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。
5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
实现Runnable接口相对于继承Thread类来说,有如下优势:
1. 适合多个相同程序代码的线程去处理同一资源的情况。
2. 可以避免由于Java的单继承特性带来的局限。
3. 增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。
(建议使用Runnable接口实现多线程)。
线程状态的转换:
线程的同步:
多线程运行出现安全问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。
解决办法:同步代码块。
synchronized(对象)
{
需要被同步的代码
}
对象如同锁,持有锁的线程可以在同步中执行。
没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
同步的前提:
1. 必须要有两个或者两个以上的线程。
2. 必须是多个线程使用同一个锁。
同步函数的锁是this(当前对象)。
静态同步函数的锁是Class对象(该方法所在类的字节码文件对象)。类名。class
1 public class SyncTest implements Runnable{ 2 3 Timer timer = new Timer(); 4 public static void main(String[] args) { 5 SyncTest stest = new SyncTest(); 6 Thread t1 = new Thread(stest,"t1"); 7 Thread t2 = new Thread(stest,"t2"); 8 t1.start(); 9 t2.start(); 10 11 } 12 13 public void run(){ 14 timer.add(Thread.currentThread().getName()); 15 } 16 17 } 18 19 class Timer{ 20 private static int num = 0; 21 22 public synchronized void add(String name){ //不加synchronized会出现安全问题 23 num++; 24 try{ 25 Thread.sleep(1); 26 }catch(InterruptedException e){ 27 28 } 29 System.out.println(name+":你是第"+num+"个使用Timer的线程"); 30 } 31 }
死锁:
是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
一个死锁程序:
1 public class TestDeadLock implements Runnable { 2 public int flag = 1; 3 static Object o1 = new Object(), o2 = new Object(); 4 public void run() { 5 System.out.println("flag=" + flag); 6 if(flag == 1) { 7 synchronized(o1) { 8 try { 9 Thread.sleep(500); 10 } catch (Exception e) { 11 e.printStackTrace(); 12 } 13 synchronized(o2) { 14 System.out.println("1"); 15 } 16 } 17 } 18 if(flag == 0) { 19 synchronized(o2) { 20 try { 21 Thread.sleep(500); 22 } catch (Exception e) { 23 e.printStackTrace(); 24 } 25 synchronized(o1) { 26 System.out.println("0"); 27 } 28 } 29 } 30 } 31 32 public static void main(String[] args) { 33 TestDeadLock td1 = new TestDeadLock(); 34 TestDeadLock td2 = new TestDeadLock(); 35 td1.flag = 1; 36 td2.flag = 0; 37 Thread t1 = new Thread(td1); 38 Thread t2 = new Thread(td2); 39 t1.start(); 40 t2.start(); 41 42 } 43 }
生产者消费者问题:
1 //java多线程模拟生产者消费者问题 2 //ProducerConsumer是主类,Producer生产者,Consumer消费者,Product产品 3 //Storage仓库 4 5 public class ProducerConsumer { 6 7 public static void main(String[] args) { 8 Storage s = new Storage(); 9 Producer p = new Producer(s); 10 Consumer c = new Consumer(s); 11 Thread tp = new Thread(p); 12 Thread tc = new Thread(c); 13 tp.start(); 14 tc.start(); 15 16 } 17 } 18 19 class Consumer implements Runnable {//消费者 20 Storage s = null; 21 public Consumer(Storage s){ 22 this.s = s; 23 } 24 public void run() { 25 for(int i=0; i<20; i++){ 26 Product p = s.pop();//取出产品 27 try { 28 Thread.sleep((int)(Math.random()*1500)); 29 } catch (InterruptedException e) { 30 e.printStackTrace(); 31 } 32 } 33 34 } 35 36 } 37 38 class Producer implements Runnable {//生产者 39 Storage s = null; 40 41 public Producer(Storage s){ 42 this.s = s; 43 } 44 45 public void run() { 46 for(int i=0; i<20; i++){ 47 Product p = new Product(i); 48 s.push(p); //放入产品 49 // System.out.println("生产者放入:" + p); 50 try { 51 Thread.sleep((int)(Math.random()*1500)); 52 } catch (InterruptedException e) { 53 e.printStackTrace(); 54 } 55 } 56 57 } 58 } 59 60 class Product { 61 int id; 62 63 public Product(int id){ 64 this.id = id; 65 } 66 67 public String toString(){//重写toString方法 68 return "产品:"+this.id; 69 } 70 } 71 72 73 class Storage { 74 int index = 0; 75 Product[] products = new Product[5]; 76 77 public synchronized void push(Product p){//放入 78 while(index==this.products.length){ 79 try { 80 this.wait(); 81 } catch (InterruptedException e) { 82 e.printStackTrace(); 83 } 84 } 85 this.products[index] = p; 86 System.out.println("生产者放入"+index+"位置:" + p); 87 index++; 88 this.notifyAll(); 89 } 90 91 public synchronized Product pop(){//取出 92 while(this.index==0){ 93 try { 94 this.wait(); 95 } catch (InterruptedException e) { 96 e.printStackTrace(); 97 } 98 } 99 index--; 100 this.notifyAll(); 101 System.out.println("消费者从"+ index+ "位置取出:" + this.products[index]); 102 return this.products[index]; 103 } 104 }
---------------------- android培训、java培训、期待与您交流! ----------------------