Java多线程
本节内容总结来自传智播客毕向东老师的公开课,感谢毕向东老师 !如有错误之处,欢迎大家指教 !
创建线程的两种方法:
1.继承Thread类:
继承Thread类;
重写run方法;
子类创建对象(就是创建线程);
对象调用start()方法,两个作用:启动线程,调用run()方法;
2.实现Runnable接口:
实现Runnable接口;
重写run方法;
子类创建对象(此时仅是创建对象,而不是创建线程);
通过Thread类创建线程对象,并将实现Runnable接口子类对象作为参数放到构造函数中(创建线程);
Thread类对象调用start()方法,启动线程,并运行子类对象的run()方法;
两个比较:
实现Runnnable接口方式更好;原因:1.java是只支持单继承,所以除了可以实现接口的方式之外还可以继承自其它的类; 2.能够共享数据;
为什么要重写run()方法:
Thread类用于描述线程,线程需要执行一些代码实现某个功能,这些实现具体功能的代码就在Thread类的run()方法里面定义;主线程在main()方法中存放实现具体功能的代码;所以run()被称为线程体
多线程出现的安全问题:
多条语句操作多个线程共享的资源时,一个线程只执行了部分语句,还没执行完,另一个线程又进来操作共享数据(执行语句),导致共享数据错误;
解决思想:
只有当一个线程执行完所有语句(操作共享资源的所有语句)之后,才能让另外一个线程进来再进行操作;
具体的两种解决方法:
同步代码块:
synchronized(对象) { 需要被同步的代码块; }
同步函数:
就是把需要同步的代码块放到一个函数里面,在函数上加 synchronized 关键字;
public synchronized void show() { 需要被同步的代码块; }
线程的几种状态及转换:
一些实例:
1 /** 2 * 创建线程的第1种方法:继承Thread类 3 */ 4 package Thread; 5 6 public class ThreadDemo 7 { 8 public static void main(String[] args) 9 { 10 // 3.子类创建对象,就是创建线程,但还没启动 11 MyThread thread = new MyThread(); 12 // 4.调用start()方法,两个作用: 13 // 启动线程 14 // 调用run()方法 15 thread.start(); 16 } 17 } 18 19 // 1.继承Thread类 20 class MyThread extends Thread 21 { 22 @Override // 2.重写run()方法 23 public void run() 24 { 25 System.out.println("MyThread run"); 26 } 27 }
1 /** 2 * 创建两个线程,和主线程交互运行 3 * 进程中至少有一个线程,就是主线程,线程体是main() 4 */ 5 package Thread; 6 7 public class ThreadDemo2 8 { 9 public static void main(String[] args) 10 { 11 Test test1 = new Test("one"); // 创建名为one的线程 12 Test test2 = new Test("two"); // 创建名为two的线程 13 14 test1.start(); // one线程执行 15 test2.start(); // two线程执行 16 17 for(int i=0;i<20;i++) 18 { 19 System.out.println("main run..." + i); // 主线程执行 20 } 21 } 22 } 23 24 class Test extends Thread 25 { 26 private String name; 27 28 public Test(String name) 29 { 30 this.name = name; 31 } 32 33 @Override 34 public void run() 35 { 36 for(int i=0;i<20;i++) 37 { 38 System.out.println(name + " run..." + i); 39 } 40 } 41 }
1 /** 2 * 通过继承Thread类方法,完成下面程序, 3 * 引出创建线程的第2种更好的方法。 4 * 需求:创建四个线程,完成售票任务 5 */ 6 package Thread; 7 8 public class ThreadDemo3 9 { 10 public static void main(String[] args) 11 { 12 // 创建四个线程 13 Ticket t1 = new Ticket(); 14 Ticket t2 = new Ticket(); 15 Ticket t3 = new Ticket(); 16 Ticket t4 = new Ticket(); 17 18 // 启动并运行四个线程 19 t1.start(); 20 t2.start(); 21 t3.start(); 22 t4.start(); 23 24 /** 25 * 结果显示: 26 * 四个线程都卖了100张票,相当于总共有400张票,而不是100张; 27 * 原因:根据目前代码来说,创建一个对象(线程)就拥有100张票,所以应该 28 * 让四个线程共同拥有100张票,使资源被独立共享,将代码进行优化 29 * 30 * 通过优化之后代码,可知第一种创建线程的方法不太满足需求,改成第2种方式 31 */ 32 33 /** 34 * 错误的优化后的代码:只创建一个线程,让它运行四次 35 * 结果抛出异常:IllegalThreadStateException 36 * 无效线程状态异常,因为线程已经被启动了,又重新启动了三次 37 */ 38 /*Ticket t1 = new Ticket(); 39 t1.start(); 40 t1.start(); 41 t1.start(); 42 t1.start();*/ 43 44 } 45 } 46 47 class Ticket extends Thread 48 { 49 // 优化之前的 ticket 属性 50 // private int ticket = 100; 51 52 // 优化之后的 ticket 属性 53 // 但我们一般不定义为 static,声明周期太长 54 private static int ticket = 100; 55 56 @Override 57 public void run() 58 { 59 while(true) 60 { 61 if(ticket > 0) 62 { 63 System.out.println(currentThread().getName() + " sale:" + ticket--); 64 } 65 } 66 } 67 }
1 /** 2 * 创建线程的第2种方法:实现Runnable接口 3 * 优化卖票程序 4 */ 5 package Thread; 6 7 public class ThreadDemo4 8 { 9 public static void main(String[] args) 10 { 11 Tickets t = new Tickets(); 12 13 Thread t1 = new Thread(t); 14 Thread t2 = new Thread(t); 15 Thread t3 = new Thread(t); 16 Thread t4 = new Thread(t); 17 18 t1.start(); 19 t2.start(); 20 t3.start(); 21 t4.start(); 22 } 23 } 24 25 class Tickets implements Runnable 26 { 27 private int ticket = 100; 28 29 @Override 30 public void run() 31 { 32 while(true) 33 { 34 if(ticket > 0) 35 { 36 System.out.println(Thread.currentThread().getName() + " sale:" + ticket--); 37 } 38 } 39 } 40 }
1 /** 2 * 模拟多线程的安全问题,引出解决方法 3 * 一般都是通过Thread类的sleep()方法来模拟多线程改的安全问题 4 */ 5 package Thread; 6 7 public class ThreadDemo5 8 { 9 public static void main(String[] args) 10 { 11 Ticket2 t = new Ticket2(); 12 13 Thread t1 = new Thread(t); 14 Thread t2 = new Thread(t); 15 Thread t3 = new Thread(t); 16 Thread t4 = new Thread(t); 17 18 t1.start(); 19 t2.start(); 20 t3.start(); 21 t4.start(); 22 } 23 } 24 25 class Ticket2 implements Runnable 26 { 27 private int ticket = 100; 28 29 @Override 30 public void run() 31 { 32 while(true) 33 { 34 if(ticket > 0) 35 { 36 try 37 { 38 // 只能被try-catch,不能抛出异常,因为 Runnable接口的run()方法没有抛出这个异常 39 // 加了这句话之后,多线程共享的数据ticket可能最后就会出现负数 40 Thread.sleep(10); 41 } catch (InterruptedException e) 42 { 43 e.printStackTrace(); 44 } 45 System.out.println(Thread.currentThread().getName() + " sale:" + ticket--); 46 } 47 } 48 } 49 50 }
1 /** 2 * 同步代码块 3 */ 4 package Thread; 5 6 public class ThreadDemo6 7 { 8 public static void main(String[] args) 9 { 10 Ticket3 t = new Ticket3(); 11 Thread t1 = new Thread(t); 12 Thread t2 = new Thread(t); 13 Thread t3 = new Thread(t); 14 Thread t4 = new Thread(t); 15 16 t1.start(); 17 t2.start(); 18 t3.start(); 19 t4.start(); 20 } 21 } 22 23 class Ticket3 implements Runnable 24 { 25 private int ticket = 100; 26 27 Object obj = new Object(); 28 29 @Override 30 public void run() 31 { 32 while(true) 33 { 34 // 目前来看,随便放一个对象都可以,但必须是对象 35 synchronized (obj) 36 { 37 if(ticket > 0) 38 { 39 try 40 { 41 Thread.sleep(100); 42 } catch (InterruptedException e) 43 { 44 e.printStackTrace(); 45 } 46 System.out.println(Thread.currentThread().getName() + " sale:" + ticket--); 47 } 48 } 49 50 } 51 } 52 53 }
1 /** 2 * 如何查看多线程代码是否有安全问题: 3 * 1.明确哪些代码是多线程运行代码 4 * 2.明确共享数据 5 * 3.明确多线程运行代码中哪些语句是操作共享数据的, 6 * 如果这些操作共享数据的代码不能被一个线程全部执行完再由其它线程执行,就会出现安全问题 7 * 8 * 需求描述: 9 * 两个顾客存钱,两人都存300,但每次只存100,存3次 10 */ 11 package Thread; 12 13 public class ThreadDemo7 14 { 15 public static void main(String[] args) 16 { 17 Cus c = new Cus(); 18 19 Thread t1 = new Thread(c); 20 Thread t2 = new Thread(c); 21 22 t1.start(); 23 t2.start(); 24 } 25 } 26 27 class Bank 28 { 29 private int sum; 30 Object obj = new Object(); 31 32 public void add(int n) 33 { 34 /** 35 * sum是共享数据,下面两句都是操作共享数据的,可能出现线程安全问题, 36 * 中间加入sleep()来模拟线程安全问题,结果显示确实有安全问题存在, 37 * 所以这两条语句要加上锁 38 */ 39 synchronized (obj) 40 { 41 sum += n; 42 try 43 { 44 Thread.sleep(10); 45 } catch (InterruptedException e) 46 { 47 e.printStackTrace(); 48 } 49 System.out.println("sum:" + sum); 50 } 51 } 52 } 53 54 class Cus implements Runnable 55 { 56 Bank b = new Bank(); 57 58 @Override 59 public void run() 60 { 61 for(int i=0;i<3;i++) 62 { 63 b.add(100); 64 } 65 } 66 }
1 /** 2 * 通过同步函数的方法优化上面的示例 3 */ 4 package Thread; 5 6 public class ThreadDemo8 7 { 8 public static void main(String[] args) 9 { 10 Cus2 c = new Cus2(); 11 12 Thread t1 = new Thread(c); 13 Thread t2 = new Thread(c); 14 15 t1.start(); 16 t2.start(); 17 } 18 } 19 20 class Bank2 21 { 22 private int sum; 23 24 // 因为方法里面只有两条语句,且这两条语句就是操作共享数据的 25 // 所以这个方法可以直接实现同步 26 public synchronized void add(int n) 27 { 28 sum += n; 29 try 30 { 31 Thread.sleep(10); 32 } catch (InterruptedException e) 33 { 34 e.printStackTrace(); 35 } 36 System.out.println("sum:" + sum); 37 } 38 } 39 40 class Cus2 implements Runnable 41 { 42 Bank2 b = new Bank2(); 43 44 @Override 45 public void run() 46 { 47 for(int i=0;i<3;i++) 48 { 49 b.add(100); 50 } 51 } 52 }
1 /** 2 * 使用同步函数的方法来优化卖票的例子 3 */ 4 package Thread; 5 6 public class ThreadDemo9 7 { 8 public static void main(String[] args) 9 { 10 Ticket4 t = new Ticket4(); 11 12 Thread t1 = new Thread(t); 13 Thread t2 = new Thread(t); 14 Thread t3 = new Thread(t); 15 Thread t4 = new Thread(t); 16 17 t1.start(); 18 t2.start(); 19 t3.start(); 20 t4.start(); 21 } 22 } 23 24 class Ticket4 implements Runnable 25 { 26 private int ticket = 100; 27 28 // 不能直接在run()方法上进行同步,因为里面有非共享资源被操作 29 // 一定要确保只同步操纵共享资源的语句 30 @Override 31 public void run() 32 { 33 while(true) 34 { 35 synch(); 36 37 /* 38 * 这段被共享资源被操作的代码放到一个新函数中 39 * if(ticket > 0) 40 { 41 try 42 { 43 Thread.sleep(10); 44 } catch (InterruptedException e) 45 { 46 e.printStackTrace(); 47 } 48 System.out.println(Thread.currentThread().getName() + " sale:" + ticket--); 49 }*/ 50 } 51 } 52 53 // 重新定义一个方法,将只操作共享资源的语句包含进来 54 public synchronized void synch() 55 { 56 if(ticket > 0) 57 { 58 try 59 { 60 Thread.sleep(10); 61 } catch (InterruptedException e) 62 { 63 e.printStackTrace(); 64 } 65 System.out.println(Thread.currentThread().getName() + " sale:" + ticket--); 66 } 67 } 68 }
1 /** 2 * 同步函数用的是哪个锁? 3 * 函数需要被对象调用,那么函数都有一个所属对象引用,就是this 4 * 那么同步函数用的锁就是this 5 * 6 * 验证: 7 * 两个线程实现卖票任务: 8 * 一个线程使用同步代码块 9 * 一个线程使用同步函数 10 */ 11 package Thread; 12 13 public class ThreadDemo10 14 { 15 public static void main(String[] args) 16 { 17 Ticket5 t = new Ticket5(); 18 19 Thread t1 = new Thread(t); 20 Thread t2 = new Thread(t); 21 22 t1.start(); 23 try 24 { 25 // 让主线程停一会,不然可能一下子让两个线程执行的都是synch()方法或代码块内容,达不到印证问题的效果 26 // 不便于问题的出现 27 Thread.sleep(10); 28 } catch (InterruptedException e) 29 { 30 e.printStackTrace(); 31 } 32 t.flag = false; 33 t2.start(); 34 } 35 } 36 37 class Ticket5 implements Runnable 38 { 39 private int ticket = 100; 40 boolean flag = true; 41 Object obj = new Object(); 42 43 @Override 44 public void run() 45 { 46 if(flag) 47 { 48 while(true) 49 { 50 // 用的锁是Object对象 51 // 两个线程用的不是同一个锁,安全问题还是会出现,两个前提没有满足 52 // 把obj改成this,即可解决问题,说明同步函数用的锁就是this 53 synchronized (obj) 54 { 55 if(ticket > 0) 56 { 57 try 58 { 59 Thread.sleep(10); 60 } catch (InterruptedException e) 61 { 62 e.printStackTrace(); 63 } 64 System.out.println(Thread.currentThread().getName() + " code:" + ticket--); 65 } 66 } 67 68 } 69 } 70 else 71 { 72 while(true) 73 { 74 // 用的是锁是this 75 this.synch(); 76 } 77 } 78 79 } 80 81 public synchronized void synch() 82 { 83 if(ticket > 0) 84 { 85 try 86 { 87 Thread.sleep(10); 88 } catch (InterruptedException e) 89 { 90 e.printStackTrace(); 91 } 92 System.out.println(Thread.currentThread().getName() + " synch():" + ticket--); 93 } 94 } 95 96 }
1 /** 2 * 静态方法使用的锁是哪个? 3 * 不是this,静态方法中不可以定义this 4 * 静态进内存时,没有本类对象,但有本类对应的字节码文件对象 5 * 类名.class 该对象的类型为Class 6 * 所以:静态方法使用的锁是该方法所在类的字节码文件对象 7 */ 8 package Thread; 9 10 public class ThreadDemo11 11 { 12 public static void main(String[] args) 13 { 14 Ticket6 t = new Ticket6(); 15 16 Thread t1 = new Thread(t); 17 Thread t2 = new Thread(t); 18 19 t1.start(); 20 try 21 { 22 Thread.sleep(10); 23 } catch (InterruptedException e) 24 { 25 e.printStackTrace(); 26 } 27 t.flag = false; 28 t2.start(); 29 } 30 } 31 32 class Ticket6 implements Runnable 33 { 34 private static int ticket = 100; 35 boolean flag = true; 36 Object obj = new Object(); 37 38 @Override 39 public void run() 40 { 41 if(flag) 42 { 43 while(true) 44 { 45 // 此时使用this,还是会出现安全问题 46 // 将this改成Ticket6.class,安全问题被解决 47 // 说明此时使用的锁是本类对应的字节码文件对象 48 synchronized (this) 49 { 50 if(ticket > 0) 51 { 52 try 53 { 54 Thread.sleep(10); 55 } catch (InterruptedException e) 56 { 57 e.printStackTrace(); 58 } 59 System.out.println(Thread.currentThread().getName() + " code:" + ticket--); 60 } 61 } 62 63 } 64 } 65 else 66 { 67 while(true) 68 { 69 synch(); 70 } 71 } 72 73 } 74 75 public static synchronized void synch() 76 { 77 if(ticket > 0) 78 { 79 try 80 { 81 Thread.sleep(10); 82 } catch (InterruptedException e) 83 { 84 e.printStackTrace(); 85 } 86 System.out.println(Thread.currentThread().getName() + " synch():" + ticket--); 87 } 88 } 89 }
1 /** 2 * 写个程序,引出死锁问题 3 * 一般出现死锁都是因为同步中嵌套着同步,如下面程序: 4 * 同步代码块中嵌套着同步函数 5 * 同步函数中嵌套着同步代码块 6 */ 7 package Thread; 8 9 public class ThreadDemo12 10 { 11 public static void main(String[] args) 12 { 13 Ticket7 t = new Ticket7(); 14 Thread t1 = new Thread(t); 15 Thread t2 = new Thread(t); 16 17 t1.start(); 18 try 19 { 20 Thread.sleep(10); 21 } catch (InterruptedException e) 22 { 23 e.printStackTrace(); 24 } 25 t.flag = false; 26 t2.start(); 27 } 28 } 29 30 class Ticket7 implements Runnable 31 { 32 private int ticket = 100; 33 boolean flag = true; 34 Object obj = new Object(); 35 36 @Override 37 public void run() 38 { 39 if(flag) 40 { 41 while(true) 42 { 43 synchronized(obj) 44 { 45 this.synch(); 46 } 47 } 48 } 49 else 50 { 51 while(true) 52 { 53 this.synch(); 54 } 55 } 56 } 57 58 public synchronized void synch() 59 { 60 synchronized(obj) 61 { 62 if(ticket > 0) 63 { 64 try 65 { 66 Thread.sleep(10); 67 } catch (InterruptedException e) 68 { 69 e.printStackTrace(); 70 } 71 System.out.println(Thread.currentThread().getName() + " code:" + ticket--); 72 } 73 74 } 75 } 76 }
1 /** 2 * 一个简单的死锁程序 3 * 4 * 拿到一个运行结果: 5 * lf locka 6 * else lockb 7 */ 8 package Thread; 9 10 public class ThreadDemo13 11 { 12 public static void main(String[] args) 13 { 14 Thread t1 = new Thread(new DeadLock(true)); 15 Thread t2 = new Thread(new DeadLock(false)); 16 17 t1.start(); 18 t2.start(); 19 } 20 } 21 22 class DeadLock implements Runnable 23 { 24 boolean flag; 25 public DeadLock(boolean flag) 26 { 27 this.flag = flag; 28 } 29 30 @Override 31 public void run() 32 { 33 if(flag) 34 { 35 // 加while循环,是为了多运行几次,那样就肯定会出现死锁问题 36 // 有时候一次两次可能不会出现 37 while(true) 38 { 39 // 根据结果得出: 40 // 一个线程拿到locka锁,但lockb锁进不去 41 // 另一个线程拿到lockb锁,但locka锁进不去 42 // 两者都不肯放弃锁,就变成了死锁 43 synchronized (MyLock.locka) 44 { 45 System.out.println("lf locka"); 46 synchronized (MyLock.lockb) 47 { 48 System.out.println("if lockb"); 49 } 50 } 51 } 52 } 53 else 54 { 55 while(true) 56 { 57 synchronized (MyLock.lockb) 58 { 59 System.out.println("else lockb"); 60 synchronized (MyLock.locka) 61 { 62 System.out.println("else locka"); 63 } 64 } 65 } 66 } 67 } 68 69 } 70 71 class MyLock 72 { 73 // 定义两个锁,目前来看,锁就是对象 74 static Object locka = new Object(); 75 static Object lockb = new Object(); 76 }
1 /** 2 * 线程间通信:wait(),notify(),notifyAll()方法的使用 3 * 4 * 需求: 5 * 一个共享资源 6 * 两个线程 7 * 一个线程往资源里面存内容 8 * 一个线程往资源里面取内容 9 * 10 * 同步的两个前提 11 * 12 * 等待唤醒机制:实现存一次,取一次 13 * 等待和唤醒必须是同一个锁 14 */ 15 package Thread; 16 17 public class ThreadDemo14 18 { 19 public static void main(String[] args) 20 { 21 // 一个共享资源 22 Res r = new Res(); 23 24 // 两个线程共同操作这个资源 25 Input in = new Input(r); 26 Output out = new Output(r); 27 28 Thread t1 = new Thread(in); 29 Thread t2 = new Thread(out); 30 31 t1.start(); 32 t2.start(); 33 34 } 35 } 36 37 class Res 38 { 39 String name; 40 String gender; 41 boolean flag = false; 42 } 43 44 class Input implements Runnable 45 { 46 private Res r; 47 public Input(Res r) 48 { 49 this.r = r; 50 } 51 52 @Override 53 public void run() 54 { 55 int x = 0; 56 57 while(true) 58 { 59 // 对共享资源加锁,实现同步的第1个前提(两个地方都加锁,才能说明是两个线程操作共享资源) 60 // 两个线程用的锁必须是一样的才行,这里用this不行,要体现出唯一性 61 // r,Input.class, Output.class三个锁都可以,因为都是唯一的 62 synchronized (r) 63 { 64 if(r.flag) 65 { 66 try 67 { 68 r.wait(); // 睡眠的线程存放在内存的线程池里面 69 } catch (InterruptedException e) 70 { 71 e.printStackTrace(); 72 } 73 } 74 75 if(x == 0) 76 { 77 r.name = "zhangsan"; 78 r.gender = "nan"; 79 } 80 else 81 { 82 r.name = "lisi"; 83 r.gender = "nv"; 84 } 85 86 x = (x+1)%2; 87 88 r.flag = true; 89 90 r.notify(); // 叫醒线程池里面的线程 91 } 92 93 } 94 } 95 } 96 97 class Output implements Runnable 98 { 99 Res r; 100 public Output(Res r) 101 { 102 this.r = r; 103 } 104 105 @Override 106 public void run() 107 { 108 while(true) 109 { 110 // 对共享资源加锁,实现同步的第1个前提(两个地方都加锁,才能说明是两个线程操作共享资源) 111 synchronized (r) 112 { 113 if(! r.flag) 114 { 115 try 116 { 117 r.wait(); 118 } catch (InterruptedException e) 119 { 120 e.printStackTrace(); 121 } 122 } 123 124 System.out.println("name:" + r.name + ";gender:" + r.gender); 125 126 r.flag = false; 127 128 r.notify(); 129 } 130 } 131 } 132 }
1 /** 2 * 线程间通信,对上面例子进行优化 3 */ 4 package Thread; 5 6 public class ThreadDemo14 7 { 8 public static void main(String[] args) 9 { 10 Res r = new Res(); 11 12 Input in = new Input(r); 13 Output out = new Output(r); 14 15 Thread t1 = new Thread(in); 16 Thread t2 = new Thread(out); 17 18 t1.start(); 19 t2.start(); 20 } 21 } 22 23 class Res 24 { 25 private String name; 26 private String gender; 27 private boolean flag = false; 28 29 public synchronized void set(String name, String gender) 30 { 31 if(flag) 32 { 33 try 34 { 35 this.wait(); 36 } catch (InterruptedException e) 37 { 38 e.printStackTrace(); 39 } 40 } 41 42 this.name = name; 43 this.gender = gender; 44 45 flag = true; 46 47 this.notify(); 48 } 49 50 public synchronized void syso() 51 { 52 if(!flag) 53 { 54 try 55 { 56 this.wait(); 57 } catch (InterruptedException e) 58 { 59 e.printStackTrace(); 60 } 61 } 62 63 System.out.println("name:" + name + ";gender:" + gender); 64 65 flag = false; 66 67 this.notify(); 68 69 } 70 } 71 72 class Input implements Runnable 73 { 74 private Res r; 75 public Input(Res r) 76 { 77 this.r = r; 78 } 79 80 @Override 81 public void run() 82 { 83 int x = 0; 84 85 while(true) 86 { 87 synchronized (r) 88 { 89 if(x == 0) 90 { 91 r.set("zhangsan", "nan"); 92 } 93 else 94 { 95 r.set("lisi", "nv"); 96 } 97 98 x = (x+1)%2; 99 } 100 101 } 102 } 103 } 104 105 class Output implements Runnable 106 { 107 Res r; 108 public Output(Res r) 109 { 110 this.r = r; 111 } 112 113 @Override 114 public void run() 115 { 116 while(true) 117 { 118 synchronized (r) 119 { 120 r.syso(); 121 } 122 } 123 } 124 }
1 /** 2 * 优化上个例子代码,上面的例子只适合有一个存线程,一个取线程 3 * 4 * 生产者-消费者,添加标记 5 * 6 * 解决实际出现的两个问题,在不知两个线程的情况下: 7 * 生产两个但只消费一个产品,或值生产一个但消费两个产品(while循环判断标记解决) 8 * 死锁问题(通知所有等待线程解决) 9 */ 10 package Thread; 11 12 public class ThreadDemo15 13 { 14 public static void main(String[] args) 15 { 16 Resource res = new Resource(); 17 18 Producer pro = new Producer(res); 19 Consumer con = new Consumer(res); 20 21 // 两个生产者 22 Thread t1 = new Thread(pro); 23 Thread t2 = new Thread(pro); 24 // 两个消费者 25 Thread t3 = new Thread(con); 26 Thread t4 = new Thread(con); 27 28 t1.start(); 29 t2.start(); 30 t3.start(); 31 t4.start(); 32 } 33 } 34 35 class Resource 36 { 37 private String name; 38 private int count = 0; 39 boolean flag = false; 40 41 public synchronized void set(String name) 42 { 43 // 将if改成while,否则会出现生产两个消费一个或生产一个消费两个的问题 44 while(flag) 45 { 46 try 47 { 48 this.wait(); 49 } catch (InterruptedException e) 50 { 51 e.printStackTrace(); 52 } 53 } 54 55 this.name = name + (count++); 56 System.out.println(Thread.currentThread().getName() + "生产者.." + this.name); 57 58 flag = true; 59 60 // 将notify()改成notifyAll(),解决死锁问题 61 this.notifyAll(); 62 } 63 64 public synchronized void syso() 65 { 66 while(!flag) 67 { 68 try 69 { 70 this.wait(); 71 } catch (InterruptedException e) 72 { 73 e.printStackTrace(); 74 } 75 } 76 77 System.out.println(Thread.currentThread().getName() + "消费者...." + this.name); 78 79 flag = false; 80 81 this.notifyAll(); 82 } 83 84 } 85 86 class Producer implements Runnable 87 { 88 private Resource res; 89 public Producer(Resource res) 90 { 91 this.res = res; 92 } 93 94 @Override 95 public void run() 96 { 97 while(true) 98 { 99 res.set("商品"); 100 } 101 } 102 103 } 104 105 class Consumer implements Runnable 106 { 107 private Resource res; 108 public Consumer(Resource res) 109 { 110 this.res = res; 111 } 112 113 @Override 114 public void run() 115 { 116 while(true) 117 { 118 res.syso(); 119 } 120 } 121 }
1 /** 2 * 优化上个例子代码 3 * 4 * JDK1.5之后,synchronized、wait、notify、notifyAll都不用了 5 * 有新的接口、类和方法来替代 6 * 并实现的可以在本方只唤醒对象的操作 7 * 8 * 之前的例子是一下子唤醒所有线程,应该只唤醒对方的线程 9 * 10 * 之前一个锁,只能对应一组wait,notify 11 * 现在一个锁,可以对应多组wait,notify 12 */ 13 package Thread; 14 15 import java.util.concurrent.locks.Condition; 16 import java.util.concurrent.locks.Lock; 17 import java.util.concurrent.locks.ReentrantLock; 18 19 public class ThreadDemo15 20 { 21 public static void main(String[] args) 22 { 23 Resource res = new Resource(); 24 25 Producer pro = new Producer(res); 26 Consumer con = new Consumer(res); 27 28 // 两个生产者 29 Thread t1 = new Thread(pro); 30 Thread t2 = new Thread(pro); 31 // 两个消费者 32 Thread t3 = new Thread(con); 33 Thread t4 = new Thread(con); 34 35 t1.start(); 36 t2.start(); 37 t3.start(); 38 t4.start(); 39 } 40 } 41 42 class Resource 43 { 44 private String name; 45 private int count = 0; 46 boolean flag = false; 47 // 产生锁 48 private Lock lock = new ReentrantLock(); 49 50 // 通过锁获得监视器方法(wait,notify,notifyAll)所在类对象 51 // 定义生产者这边的包含监视器方法的对象 52 private Condition condition_pro = lock.newCondition(); 53 // 定义消费者这边的包含监视器方法的对象 54 private Condition condition_con = lock.newCondition(); 55 56 public void set(String name) throws InterruptedException 57 { 58 // 加锁 59 lock.lock(); 60 61 try 62 { 63 while(flag) 64 { 65 // 等待 66 // 生产者已生产了商品,不能再次生产,必须等待消费者消费 67 condition_pro.await(); 68 } 69 70 this.name = name + (count++); 71 System.out.println(Thread.currentThread().getName() + "生产者.." + this.name); 72 73 flag = true; 74 75 // 唤醒 76 // 通知消费者消费商品 77 condition_con.signal(); 78 79 } 80 finally 81 { 82 // 释放锁 83 lock.unlock(); // 释放锁的动作一定要执行 84 } 85 86 } 87 88 public void syso() throws InterruptedException 89 { 90 lock.lock(); 91 92 try 93 { 94 while(!flag) 95 { 96 // 消费者已消费商品,不能再消费,必须等待生产者生产 97 condition_con.await(); 98 } 99 100 System.out.println(Thread.currentThread().getName() + "消费者...." + this.name); 101 102 flag = false; 103 104 // 通知生产者生产商品 105 condition_pro.signalAll(); 106 } 107 finally 108 { 109 lock.unlock(); 110 } 111 } 112 } 113 114 class Producer implements Runnable 115 { 116 private Resource res; 117 public Producer(Resource res) 118 { 119 this.res = res; 120 } 121 122 @Override 123 public void run() 124 { 125 while(true) 126 { 127 try 128 { 129 res.set("商品"); 130 } catch (InterruptedException e) 131 { 132 e.printStackTrace(); 133 } 134 } 135 } 136 137 } 138 139 class Consumer implements Runnable 140 { 141 private Resource res; 142 public Consumer(Resource res) 143 { 144 this.res = res; 145 } 146 147 @Override 148 public void run() 149 { 150 while(true) 151 { 152 try 153 { 154 res.syso(); 155 } catch (InterruptedException e) 156 { 157 e.printStackTrace(); 158 } 159 } 160 } 161 }
1 /** 2 * 实现停止线程 3 * 4 * stop()方法已过时,如何停止线程: 5 * 只有一种方法,就是是run()方法结束 6 * 原理: 7 * 开启多线程运行,运行代码通常是循环结构, 8 * 只要控制住循环,就可以让run方法结束,也就是让线程结束 9 * 10 */ 11 package Thread; 12 13 public class ThreadDemo16 14 { 15 public static void main(String[] args) 16 { 17 Inter inter = new Inter(); 18 19 Thread t1 = new Thread(inter); 20 Thread t2 = new Thread(inter); 21 22 t1.start(); 23 t2.start(); 24 25 int count = 0; 26 27 while(true) 28 { 29 // 实现计数到60之后让线程结束 30 if(count++ == 60) 31 { 32 inter.change(); 33 break; 34 } 35 System.out.println(Thread.currentThread().getName() + "...main..."); 36 } 37 System.out.println(Thread.currentThread().getName() + "...over..."); 38 } 39 } 40 41 class Inter implements Runnable 42 { 43 private boolean flag = true; 44 45 @Override 46 public void run() 47 { 48 while(flag) 49 { 50 System.out.println(Thread.currentThread().getName() + "...run..."); 51 } 52 } 53 54 // 在指定的条件下调用此方法,即可停止循环,结束线程 55 public void change() 56 { 57 flag = false; 58 } 59 }
1 /** 2 * 实现停止线程:interrupt()方法的使用 3 * 4 * stop()方法已过时,如何停止线程: 5 * 只有一种方法,就是是run()方法结束 6 * 原理: 7 * 开启多线程运行,运行代码通常是循环结构, 8 * 只要控制住循环,就可以让run方法结束,也就是让线程结束 9 * 10 * 特殊情况: 11 * 当线程处于冻结状态,就不会读取到标记,那么线程就不会结束 12 * 13 * 当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除, 14 * 使用interrupt()方法来唤醒睡眠的 线程,但会抛出异常 15 * 强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束 16 */ 17 package Thread; 18 19 public class ThreadDemo16 20 { 21 public static void main(String[] args) 22 { 23 Inter inter = new Inter(); 24 25 Thread t1 = new Thread(inter); 26 Thread t2 = new Thread(inter); 27 28 t1.start(); 29 t2.start(); 30 31 int count = 0; 32 33 while(true) 34 { 35 // 实现计数到60之后让通过interrupt()唤醒线程 36 if(count++ == 60) 37 { 38 // 唤醒两个线程 39 t1.interrupt(); 40 t2.interrupt(); 41 break; 42 } 43 System.out.println(Thread.currentThread().getName() + "...main..."); 44 } 45 System.out.println(Thread.currentThread().getName() + "...over..."); 46 } 47 } 48 49 class Inter implements Runnable 50 { 51 private boolean flag = true; 52 53 // 这样写,线程1和线程2都会冻结 54 @Override 55 public synchronized void run() 56 { 57 while(flag) 58 { 59 try 60 { 61 wait(); 62 } catch (InterruptedException e) 63 { 64 // 进入异常,说明就有interrupt()强制唤醒线程, 65 // 接着另 flag = false; 使可以运行的线程正常结束 66 flag = false; 67 System.out.println(Thread.currentThread().getName() + "...exception..."); 68 } 69 System.out.println(Thread.currentThread().getName() + "...run..."); 70 } 71 } 72 73 // 在指定的条件下调用此方法,即可停止循环,结束线程 74 public void change() 75 { 76 flag = false; 77 } 78 }
1 /** 2 * 守护线程 3 * 4 * 守护线程可以理解为后台线程,其他线程可以理解为前台线程 5 * 6 * 区别: 7 * 当守护线程和其它线程一起执行任务时,没有区别,共同抢夺cpu时间片; 8 * 但到其他线程结束时,只要守护线程不是处于结束状态,守护线程都会自动结束 9 */ 10 package Thread; 11 12 public class ThreadDemo16 13 { 14 public static void main(String[] args) 15 { 16 Inter inter = new Inter(); 17 18 Thread t1 = new Thread(inter); 19 Thread t2 = new Thread(inter); 20 21 // 调用此方法是t1和t2两个线程都变为守护线程 22 // 不设为守护线程之前,这段代码最后的结果是t1,t2两个线程一直 23 // 处于运行状态,此程序永远不会结束 24 // 设为守护线程后,当主线程结束后,两个正在执行的线程也会跟着结束 25 t1.setDaemon(true); 26 t2.setDaemon(true); 27 t1.start(); 28 t2.start(); 29 30 int count = 0; 31 32 while(true) 33 { 34 if(count++ == 60) 35 { 36 break; 37 } 38 System.out.println(Thread.currentThread().getName() + "...main..."); 39 } 40 System.out.println(Thread.currentThread().getName() + "...over..."); 41 } 42 } 43 44 class Inter implements Runnable 45 { 46 private boolean flag = true; 47 48 // 这样写,线程1和线程2都会冻结 49 @Override 50 public void run() 51 { 52 while(flag) 53 { 54 System.out.println(Thread.currentThread().getName() + "...run..."); 55 } 56 } 57 }
1 /** 2 * join()方法的特性 3 * 4 * 被用来临时加入线程运行 5 */ 6 package Thread; 7 8 public class ThreadDemo17 9 { 10 public static void main(String[] args) 11 { 12 JoinTest join = new JoinTest(); 13 14 Thread t1 = new Thread(join); 15 Thread t2 = new Thread(join); 16 17 t1.start(); 18 19 try 20 { 21 // 执行这条语句之后,主线程必须让出cpu执行权, 22 // 让t1拿到执行权,把任务执行完毕之后才让出cpu执行权 23 // 让主线程和t2开始抢cpu 24 t1.join(); 25 } catch (InterruptedException e) 26 { 27 e.printStackTrace(); 28 } 29 30 t2.start(); 31 32 33 /*try 34 { 35 // 如果这句话写在这里,那么主线程还是必须先让出执行权, 36 // 让t1和t2争夺cpu执行权还行任务 37 // 当t1线程执行结束之后,主线程就和t2开始抢cpu了 38 t1.join(); 39 } catch (InterruptedException e) 40 { 41 e.printStackTrace(); 42 }*/ 43 44 for(int i=0;i<60;i++) 45 { 46 System.out.println(Thread.currentThread().getName() + "..main.." + i); 47 } 48 System.out.println(Thread.currentThread().getName() + "over"); 49 } 50 } 51 52 class JoinTest implements Runnable 53 { 54 @Override 55 public void run() 56 { 57 for(int i=0;i<60;i++) 58 { 59 System.out.println(Thread.currentThread().getName() + "..run.." + i); 60 } 61 } 62 }