进程指的是一个正在执行中的程序,而线程则是进程中一个负责程序执行的控制单元。一个进程中可以有多个执行路径,这就是多线程。开启多个线程可以运行多部分代码,这样就能运行多个功能
一、多线程的创建
在java中,创建多线程主要有以下两个方式:
1、继承Thread类:
1 public class test { 2 3 public static void main(String[] args) { 4 5 //创建对象 6 7 ProducerProject p1=new ProducerProject(); 8 9 p1.setName("生产者1"); 10 11 ProducerProject p2=new ProducerProject(); 12 13 p2.setName("生产者2"); 14 15 //开始执行线程 16 17 p1.start(); 18 19 p2.start(); 20 21 } 22 23 } 24 25 //继承Thread类 26 27 class ProducerProject extends Thread{ 28 29 //覆盖Thread类中 的run()方法 30 31 public void run(){ 32 33 for(int i=0;i<5;i++){ 34 35 System.out.println(Thread.currentThread().getName()+"..生产商品"+i); 36 37 } 38 39 } 40 41 }
2、实现Runnable接口
1 public class test2{ 2 public static void main(String[] args){ 3 //创建ProducerProject2对象 4 ProducerProject2 p=new ProducerProject2(); 5 //将p作为参数构造Thread对象,创建线程 6 Thread t1=new Thread(p); 7 Thread t2=new Thread(p); 8 9 t1.setName("生产者1"); 10 t2.setName("生产者1"); 11 //启动线程 12 t1.start(); 13 t2.start(); 14 } 15 } 16 17 //实现Runnable接口 18 class ProducerProject2 implements Runnable{ 19 //实现run()方法 20 public void run() { 21 for(int i=0;i<5;i++){ 22 System.out.println(Thread.currentThread().getName()+"..生产商品"+i); 23 } 24 } 25 }
实现Runnable接口避免了单继承的局限性,并且可以实现资源的共享,适合多个相同的程序代码的线程去处理同一资源,所以一般建议使用第二种线程创建方式。我们可以用静态方法currentThread()来获取当前线程对象,getName()和setName()可以用来获取和设置线程名称。
二、synchronized 锁和wait、notify、notifyAll 的应用
1 /* 2 * 线程间通信 3 * 其实是多个线程在操作同一个资源 4 * 但是操作的动作不同 5 * 6 * 等待 唤醒机制 7 * 8 * wait() 9 * nitify() 10 * notifyAll() 11 * 都使用在同步中。 12 * 因为要对持有监视器(锁)的线程操作 13 * 所以要使用在同步中,只有同步才具有锁 14 * 为什么这些操作要定义在Object类中 15 * 因为这些方法在操作同步线程中,都必须 16 * 标示他们所操作线程中的锁 17 * 只有同一个锁上的被等待线程, 18 * 可以被同一个锁上的notify 唤醒 19 * 不可以对不同锁上的线程进行唤醒 20 * 21 * 也就是说,等待唤醒的线程必须是同一个锁上 22 * 而锁可以是任意对象,所以可以被任意对象调用的方法定义在 23 * Object类 24 * 25 * */ 26 class Res{ 27 String name; 28 String sex; 29 boolean rw=false; 30 } 31 class Input implements Runnable{ 32 Res res; 33 public Input(Res res) { 34 this.res=res; 35 } 36 public void run() { 37 boolean flag=true; 38 while (true) { 39 synchronized (res) { 40 if(res.rw) 41 try { 42 res.wait(); 43 } catch (InterruptedException e) { 44 e.printStackTrace(); 45 } 46 if (flag) { 47 res.name="mike"; 48 res.sex="man"; 49 }else { 50 res.name="丽丽"; 51 res.sex="女"; 52 } 53 flag=flag==true?false:true; 54 res.rw=true; 55 res.notify(); 56 } 57 } 58 } 59 } 60 class Output implements Runnable{ 61 Res res; 62 public Output(Res res) { 63 this.res=res; 64 } 65 public void run() { 66 while (true) { 67 synchronized (res) { 68 if(!res.rw) 69 try { 70 res.wait(); 71 } catch (InterruptedException e) { 72 e.printStackTrace(); 73 } 74 System.out.println(res.name+"-"+res.sex); 75 res.rw=false; 76 res.notify(); 77 } 78 } 79 } 80 } 81 public class InputOutputDemo { 82 public static void main(String[] args) { 83 Res res=new Res(); 84 Input input=new Input(res); 85 Output output=new Output(res); 86 Thread threadIn=new Thread(input); 87 Thread threadOut=new Thread(output); 88 threadIn.start(); 89 threadOut.start(); 90 } 91 92 }
-----------可以对代码进行优化,得到以下代码
1 /* 2 * 线程间通信 3 * 其实是多个线程在操作同一个资源 4 * 但是操作的动作不同 5 * 6 * 等待 唤醒机制 7 * 8 * wait() 9 * nitify() 10 * notifyAll() 11 * 都使用在同步中。 12 * 因为要对持有监视器(锁)的线程操作 13 * 所以要使用在同步中,只有同步才具有锁 14 * 为什么这些操作要定义在Object类中 15 * 因为这些方法在操作同步线程中,都必须 16 * 标示他们所操作线程中的锁 17 * 只有同一个锁上的被等待线程, 18 * 可以被同一个锁上的notify 唤醒 19 * 不可以对不同锁上的线程进行唤醒 20 * 21 * 也就是说,等待唤醒的线程必须是同一个锁上 22 * 而锁可以是任意对象,所以可以被任意对象调用的方法定义在 23 * Object类中 24 * 25 * ------------------------------代码优化----------------------------------- 26 * */ 27 class Res2{ 28 private String name; 29 private String sex; 30 private boolean rw=false; 31 public synchronized void setNameSex(String name,String sex) { 32 if (rw) { 33 try { 34 this.wait(); 35 } catch (InterruptedException e) { 36 e.printStackTrace(); 37 } 38 } 39 this.name = name; 40 this.sex = sex; 41 rw=true; 42 this.notify(); 43 } 44 public synchronized void getSexName() { 45 if (!rw) { 46 try { 47 this.wait(); 48 } catch (InterruptedException e) { 49 e.printStackTrace(); 50 } 51 } 52 System.out.println(name+"---"+sex); 53 rw=false; 54 this.notify(); 55 } 56 } 57 class Input2 implements Runnable{ 58 Res2 res; 59 public Input2(Res2 res) { 60 this.res=res; 61 } 62 public void run() { 63 boolean flag=true; 64 while (true) { 65 66 if (flag) { 67 res.setNameSex("mike","man"); 68 }else { 69 res.setNameSex("丽丽","女"); 70 } 71 flag=flag==true?false:true; 72 } 73 } 74 } 75 class Output2 implements Runnable{ 76 Res2 res; 77 public Output2(Res2 res) { 78 this.res=res; 79 } 80 public void run() { 81 while (true) { 82 res.getSexName(); 83 } 84 } 85 } 86 public class InputOutputDemo2 { 87 public static void main(String[] args) { 88 Res2 res=new Res2(); 89 // Input2 input=new Input2(res); 90 // Output2 output=new Output2(res); 91 // Thread threadIn=new Thread(input); 92 // Thread threadOut=new Thread(output); 93 // threadIn.start(); 94 // threadOut.start(); 95 new Thread(new Input2(res)).start(); 96 new Thread(new Output2(res)).start(); 97 98 } 99 }
三、用Lock 代替synchronized 实现线程同步
1 import java.util.concurrent.locks.Condition; 2 import java.util.concurrent.locks.Lock; 3 import java.util.concurrent.locks.ReentrantLock; 4 5 /*对于多个生产者和消费者 6 * 为什么要定义while 判断标记 7 * 原因:为了让被唤醒的线程再一次判断标记 8 * 9 * 为什么定义notifyAll() 10 * 因为需要唤醒对方线程 11 * 因为只用notify ,容易出现只唤醒本方线程的情况,导致程序中的所有线程都等待 12 * 13 * 14 * jdk 1.5 中国提供了多线程升级解决方案 15 * 将同步 的synchronized替换成Lock操作 16 * 将 Object 中的 wait notify notifyAll 替换了Condition 对象 17 * 该对象可以Lock锁进行获取 18 * 在该例子中,实现了本方只唤醒对方线程操作 19 * */ 20 public class ProduceConsumeLockDemo { 21 22 public static void main(String[] args) { 23 Resource2 resource=new Resource2(); 24 Produce2 produce=new Produce2(resource); 25 Consume2 consume=new Consume2(resource); 26 27 Thread threadPro1=new Thread(produce); 28 Thread threadPro2=new Thread(produce); 29 Thread threadConsume2e1=new Thread(consume); 30 Thread threadConsume2e2=new Thread(consume); 31 threadPro1.start(); 32 threadPro2.start(); 33 threadConsume2e1.start(); 34 threadConsume2e2.start(); 35 } 36 37 } 38 class Produce2 implements Runnable{ 39 private Resource2 resource; 40 public Produce2(Resource2 resource) { 41 this.resource=resource; 42 } 43 public void run() { 44 while (true) { 45 try { 46 resource.set("+商品+"); 47 } catch (InterruptedException e) { 48 e.printStackTrace(); 49 } 50 } 51 } 52 } 53 class Consume2 implements Runnable{ 54 private Resource2 resource; 55 public Consume2(Resource2 resource) { 56 this.resource=resource; 57 } 58 public void run() { 59 while (true) { 60 try { 61 resource.out(); 62 } catch (InterruptedException e) { 63 e.printStackTrace(); 64 } 65 } 66 } 67 68 } 69 class Resource2{ 70 private String name; 71 private int count =1; 72 private boolean flag=false; 73 //--------------------------------------------------------- 74 private Lock lock =new ReentrantLock(); 75 private Condition con_Produce=lock.newCondition(); 76 private Condition con_Consume=lock.newCondition(); 77 //--------------------------------------------------------- 78 public void set (String name)throws InterruptedException { 79 lock.lock();//----------------------- 80 try { 81 while(flag){ 82 // con.await(); 83 con_Produce.await(); 84 } 85 this.name=name+"--"+count++; 86 System.out.println(Thread.currentThread().getName()+"--生产者--"+this.name); 87 flag=true; 88 // con.signal(); 89 // con.signalAll();//还是唤醒了所有线程 90 con_Consume.signal(); 91 }finally{ 92 lock.unlock();//-------------- 93 //释放锁的动作一定要执行 94 } 95 } 96 public void out() throws InterruptedException{ 97 lock.lock();//进来,先拿到锁 98 try { 99 while(!flag){ 100 con_Consume.await(); 101 } 102 System.out.println(Thread.currentThread().getName()+"----消费者-----" 103 + this.name); 104 flag=false; 105 //con.signal(); 106 con_Produce.signal(); 107 }finally{ 108 lock.unlock(); 109 } 110 111 } 112 }
四、Thread的stop()方法和interrup()方法
1 /* 2 * stop 方法以及过时 3 * 如何停止线程 4 * 只有一种方法 run 方法 运行结束 5 * 6 * 开启多线程运行,运行代码通常是循环结构 7 * 只要控制住循环,就可以让run方法结束,也就是线程结束 8 * 9 * 特殊情况 10 * 当线程处于冻结状态,就不会读到标记 11 * 那么线程就不会结束 12 * 13 * interrupt 强制将冻结状态的线程 运行起来 14 * 当没有指定的方式让冻结的线程回复都爱运行状态时 15 * 16 * 这时需要对冻结状态进行清除 17 * 强制让线程恢复到运行状态中来,就可以操作标记你,让线程结束 18 * Thread类 interrupt 提供了该功能 19 * 20 * 守护线程 或者 用户线程 21 * 22 * 标记为后台线程 : 23 * 处理结束和普通线程有区别外,其他都一样 24 * 当所有前台线程都结束后,后台线程自动结束(有依赖的意思) 25 * 主线程是前台线程。 26 * 当正在运行的线程都是守护线程是。jvm自动停止 27 * */ 28 public class StopThreadDemo { 29 public static void main(String[] args) { 30 StopThread st=new StopThread(); 31 Thread t1=new Thread(st); 32 Thread t2=new Thread(st); 33 t1.setDaemon(true); 34 t2.setDaemon(true); 35 t1.start(); 36 t2.start(); 37 38 int num=0; 39 while (true) { 40 if (num++==60) { 41 // st.changFlag(); 42 // t1.interrupt(); 43 // t2.interrupt(); 44 break; 45 } 46 System.out.println(Thread.currentThread().getName()+"main -"+num); 47 } 48 System.out.println("over"); 49 } 50 } 51 class StopThread implements Runnable{ 52 private boolean flag=true; 53 public synchronized void run() { 54 while(flag){ 55 try { 56 wait(); 57 } catch (InterruptedException e) { 58 // e.printStackTrace(); 59 System.out.println(Thread.currentThread().getName()+"----InterruptedException"); 60 flag=false; 61 } 62 System.out.println(Thread.currentThread().getName()+"----run"); 63 } 64 } 65 public void changFlag(){ 66 flag=false; 67 } 68 }
五、Thread的join()方法
1 /* 2 * 当 A线程执行到了 B线程的Join方法时 3 * A线程就会等待B线程终止,A才会 执行 4 * join可以用来临时加入线程执行 5 * 6 * Thread[Thread-0,5,main]--- run29 7 Thread[Thread-1,5,main]--- run32 8 Thread[Thread-0,5,main]--- run30 9 Thread【线程名,优先级,线程组】 一般谁开启了线程,线程属于哪个组 10 优先级 代表 使用cpu 的使用频率 11 默认优先级是 5 12 优先级 分 1 ----10 级 13 static int MAX_PRIORITY 10 14 线程可以具有的最高优先级。 15 static int MIN_PRIORITY 1 16 线程可以具有的最低优先级。 17 static int NORM_PRIORITY 5 18 分配给线程的默认优先级。 19 20 * */ 21 public class ClassJoinDemo { 22 public static void main(String[] args) throws Exception { 23 JoinDmeo joinDmeo=new JoinDmeo(); 24 Thread thread1=new Thread(joinDmeo); 25 Thread thread2=new Thread(joinDmeo); 26 27 thread1.start(); 28 // thread1.setPriority(Thread.MAX_PRIORITY); 29 30 // thread1.join(); 31 /*thread1.join(); 32 * thread1 请求执行权 33 * 主线程处于冻结状态 34 * thread1 结束后 主线程才恢复到运行状态 35 * 等待这个线程 终止 36 * 当进行多线程运行时, 37 * 临时加入一个线程,让这个线程运算完, 38 * */ 39 thread2.start(); 40 // thread1.join(); 41 for (int i = 0; i < 80; i++) { 42 System.out.println("mian "+i); 43 } 44 System.out.println("mina over"); 45 } 46 47 } 48 class JoinDmeo implements Runnable{ 49 public void run() { 50 for (int i = 0; i < 70; i++) { 51 System.out.println(Thread.currentThread().toString()+"--- run"+i); 52 Thread.yield();// 释放执行权 53 } 54 } 55 56 }