线程
一、线程的概念
进程是正在执行的程序。在操作系统中进程是进行系统资源分配、调度和管理的最小单位,进程在执行过程中拥有独立的内存单元。比如:Windows采用进程作为最小隔离单位,每个进程有属于自己的数据段、程序段 ,并且与别的进程没有任何关系。
一个或更多的线程构成了一个进程(操作系统是以进程为单位的,而进程是以线程为单位的,进程中必须有一个主线程)。
为了解决进程调度资源的浪费,为了能够共享资源,出现了线程。线程是CPU调度和分派的基本单位,它可与同属一个进程的其他的线程共享进程所拥有的全部资源,多个线程共享内存,从而极大地提高了程序的运行效率。
如果一个进程没有了,那么线程肯定会消失,如果线程消失了,但是进程未必会消失。而且所有线程都是在进程的基础之上并同时运行。
二、创建线程的两种方式及区别
1 继承Thread类
1.1 示例代码
1 package com.study.test; 2 3 //创建线程的两种方式:继承Thread 4 //必须明确的覆写Thread类中的run方法,此方法为线程的主体 5 class AA extends Thread { 6 public void run() { 7 while (true) { 8 System.out.println("world"); 9 try { 10 Thread.sleep(2000); 11 } catch (InterruptedException e) { 12 e.printStackTrace(); 13 } 14 } 15 } 16 } 17 18 public class MyThread { 19 // main函数为主线程,运行时先运行主线程,再运行其他线程 20 public static void main(String[] args) throws InterruptedException { 21 AA a1 = new AA(); 22 a1.start();// 必须先start线程,不能直接调用线程里面的函数,否则归属为主线程 23 while (true) { 24 System.out.println("hello"); 25 Thread.sleep(2000);// 让线程等待2s,先让其他进程运行,2s后再运行 26 } 27 } 28 29 }
2 实现Runnable接口
2.1 定义一个类,实现Runnable接口,并覆盖 run()方法,在这个方法里是我们希望这个线程运行的代码。
2.2 创建一个这个新类的对象。
2.3 创建一个Thread类的对象,用刚才的Runnable对象作为构造函数参数。
2.4 调用Thread对象的start()方法来启动线程。
2.5 示例代码
1 package com.study.test; 2 3 //创建线程的两种方式:实现Runnable接口 4 //必须明确的覆写Thread类中的run方法,此方法为线程的主体 5 6 class AA implements Runnable { 7 public void run() { 8 while (true) { 9 System.out.println("world"); 10 try { 11 Thread.sleep(2000); 12 } catch (InterruptedException e) { 13 e.printStackTrace(); 14 } 15 } 16 } 17 } 18 19 public class MyThread { 20 // main函数为主线程,运行时先运行主线程,再运行其他线程 21 public static void main(String[] args) throws InterruptedException { 22 AA a1 = new AA();// 创建一个这个新类的对象。 23 24 Thread t1 = new Thread(a1);// 创建一个Thread类的对象,用刚才的Runnable对象作为构造函数参数。 25 26 t1.start();// 必须先start线程,不能直接调用线程里面的函数,否则归属为主线程 27 while (true) { 28 System.out.println("hello"); 29 Thread.sleep(2000);// 让线程等待2s,先让其他进程运行,2s后再运行 30 } 31 } 32 33 }
3、实现Runnable接口与继承Thread的区别
3.1 实现Runnable 接口比继承 Thread 类有如下的明显优点:
(1)、适合多个相同程序代码的线程去处理同一个资源。
(2)、可以避免由于单继承局限所带来的影响。
(3)、增强了程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。
3.2 Thread更加简单易用
三、线程的生命周期
多线程在操作过程也有一个固定的操作状态,任何一个线程都具有五种状态:创建、就绪、运行、阻塞、终止
创建状态:准备好的一个多线程的对象
就绪状态:调用了start()方法,等待cpu调度
运行状态:执行run()方法。
阻塞状态:暂停线程,可以将资源交给其他资源使用
终止状态:线程执行完毕,不再使用。
四、线程同步加锁解决资源竟态问题
1. 竞态的概念
1.1 多个线程调用同一资源就会产生竞争状态(竞态)
1.2 访问的同一资源,我们称之为临界资源
1.3 访问临界资源的代码,我们称之为临界区
1.4 如果两个线程同时取得数据,将会造成流水号重复
1.5 示例代码
1 class CThread extends Thread 2 { 3 public static int i=1; //相当于全局变量 临界资源 4 //临界区 5 public void run() 6 { 7 while(true) 8 { 9 try { 10 Thread.sleep(1000); 11 } catch (InterruptedException e) { 12 // TODO Auto-generated catch block 13 e.printStackTrace(); 14 }//模拟做一些事情 15 out.println(CThread.i);//取得数据 16 try { 17 Thread.sleep(1000); 18 } catch (InterruptedException e) { 19 // TODO Auto-generated catch block 20 e.printStackTrace(); 21 } 22 CThread.i++;//流水号加1 23 if (CThread.i==100) break; 24 } 25 } 26 } 27 public class Hello1 { 28 public static void main(String[] args) throws InterruptedException { 29 //多个线程(t1,t2)同时访问全局变量i; 30 Thread t1=new CThread(); 31 t1.start(); 32 Thread t2=new CThread(); 33 t2.start(); 34 35 } 36 }
2. 解决方法是利用同步加锁
2.1 多个线程访问同一资源,线程之间相互协调的功能,叫线程同步。也就是说在同一个时间段内只能有一个线程进行,其他线程要等待此线程完毕之后才可以继续执行。
2.2 在临界资源上放置同步块synchronized()可以解决这个问题
2.3 线程执行到synchronized()代码块时首先看看有没有锁定,如果没有锁定,则获得锁,如果已经锁定,则需要等待,线程离开synchronized()代码块的时候释放锁。
2.4 这一切的过程由系统任意给定的一个对象来监视,这个对象放入synchronized块的()中
2.5 示例代码
1 package com.study.test; 2 3 import static java.lang.System.*; 4 5 //利用同步加锁的方式解决竟态(多个线程竟争同一资源)问题 6 class CThread extends Thread { 7 public static int i = 1; // 相当于全局变量 8 public static Object j = new Object();// 随便一个对象用于监视 9 10 public void run() { 11 while (true) { 12 synchronized (j) // 对对象加锁,这里用的是任意一个对象,当一个对象进来的时候看有没有对象正在使用,如果有就等待,没有的话就使用并加锁,使用完以后进行解锁 13 { // 对临界区加锁实现同步 14 try { 15 Thread.sleep(1000); 16 } catch (InterruptedException e) { 17 e.printStackTrace(); 18 } // 模拟做一些事情 19 out.println(CThread.i);// 取得数据 20 try { 21 Thread.sleep(1000); 22 } catch (InterruptedException e) { 23 e.printStackTrace(); 24 } 25 CThread.i++;// 流水好加1 26 if (CThread.i == 100) 27 break; 28 } // 解锁 29 } 30 } 31 } 32 33 public class MyThread { 34 public static void main(String[] args) throws InterruptedException { 35 Thread t1 = new CThread(); 36 t1.start(); 37 Thread t2 = new CThread(); 38 t2.start(); 39 40 } 41 }
五、线程安全类
1 把一个类中的方法标志为synchronized,我们称之为该类线程安全,多个线程访问同一个对象的内部数据将会采用线程同步
1 package com.study.test; 2 3 import static java.lang.System.out; 4 5 //把一个类中的方法标志为synchronized,我们称之为该类线程安全,多个线程访问同一个对象的内部数据将会采用线程同步 6 class dd// 线程安全类 7 { 8 public int i = 1; 9 10 public void run() { 11 while (true) { 12 synchronized (this) { 13 try { 14 Thread.sleep(1000); 15 } catch (InterruptedException e) { 16 // TODO Auto-generated catch block 17 e.printStackTrace(); 18 } // 模拟做一些事情 19 out.println(i);// 取得数据 20 try { 21 Thread.sleep(1000); 22 } catch (InterruptedException e) { 23 // TODO Auto-generated catch block 24 e.printStackTrace(); 25 } 26 i++;// 流水好加1 27 if (i == 100) 28 break; 29 } 30 } 31 } 32 } 33 34 class CThread extends Thread { 35 // public static int i=1; //相当于全局变量 36 public static dd j = new dd();// 随便一个对象用于监视 37 38 public void run() { 39 j.run(); 40 } 41 } 42 43 public class MyThread { 44 public static void main(String[] args) throws InterruptedException { 45 46 Thread t1 = new CThread(); 47 t1.start(); 48 Thread t2 = new CThread(); 49 t2.start(); 50 51 } 52 }
六、Notify()与notifyall()区别
1. 使用notify(),通知第一个等待线程,所有排队的线程按先后顺序执行。
2. notifyall()通知所有的等待线程,那么优先级别高线程就先执行。
七、yeild()与sleep()的区别
1. yield:发杨雷锋主义精神,主动暂停当前正在执行的线程对象,并执行其他线程。 将执行的权力交给其它线程,自己到队列的最后等待。
2. Thread.yield()线程是从Running状态到Ready状态。
3. Thread.sleep()调用会给较低优先级线程一个运行的机会。
4. Thread.yield()方法只会给相同优先级线程一个执行的机会。
八、join()
在一个线程A里面加入另一个线程B运行,等待B线程运行完以后再运行A线程
九、wait notify 的应用生产者消费者问题
1 package com.study.test; 2 3 import static java.lang.System.in; 4 import static java.lang.System.out; 5 6 import java.io.IOException; 7 import java.util.Scanner; 8 9 class Product { 10 public static Object monitor = new Object();// 监视 11 public static String flowNo = null;// 流水号 12 } 13 14 class Customer extends Thread { 15 Customer(String name) { 16 super(name); 17 } 18 19 public void run() { 20 // while(true) 21 // { 22 synchronized (Product.monitor) { 23 out.println("now the thread:" + Thread.currentThread().getName() + " get lock"); 24 try { 25 out.println("now the thread:" + Thread.currentThread().getName() + " is waiting"); 26 Product.monitor.wait(); // wait的同时会解开锁给其他线程 27 out.println(Product.flowNo); 28 } catch (Exception e) { 29 out.println(e.getMessage()); 30 } 31 } 32 33 // } 34 } 35 } 36 37 class Producter extends Thread { 38 Producter(String name) { 39 super(name); 40 } 41 42 public void run() { 43 // while(true) 44 // { 45 synchronized (Product.monitor) { 46 out.println("now the thread:" + Thread.currentThread().getName() + " get lock"); 47 // 生产产品 48 Scanner sc = new Scanner(in); 49 Product.flowNo = sc.nextLine(); 50 out.println("now notify all thread to work"); 51 Product.monitor.notifyAll();// 通知所有的线程开始运行 52 } 53 54 // } 55 } 56 } 57 58 public class MyThread { 59 public static void main(String[] args) throws InterruptedException, IOException { 60 Customer t1 = new Customer("customer"); 61 t1.start(); 62 Producter p1 = new Producter("producter"); 63 p1.start(); 64 } 65 }
十、线程池以及使用线程池的好处
1、多线程运行时间,系统不断的启动和关闭新线程,成本非常高
2、使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。
1 package com.study.test; 2 3 import static java.lang.System.out; 4 import java.util.concurrent.ExecutorService; 5 import java.util.concurrent.Executors; 6 7 class CThread extends Thread 8 { 9 public static int i=1; //相当于全局变量 10 public static Object j=new Object();//随便一个对象用于监视 11 public void run() 12 { 13 while(true) 14 { 15 synchronized(j) //对对象加锁,这里用的是任意一个对象 16 { //对临界区加锁实现同步 17 try { 18 Thread.sleep(1000); 19 } catch (InterruptedException e) { 20 // TODO Auto-generated catch block 21 e.printStackTrace(); 22 }//模拟做一些事情 23 out.println(CThread.i);//取得数据 24 try { 25 Thread.sleep(1000); 26 } catch (InterruptedException e) { 27 // TODO Auto-generated catch block 28 e.printStackTrace(); 29 } 30 CThread.i++;//流水好加1 31 if (CThread.i==100) break; 32 }//解锁 33 } 34 } 35 } 36 public class MyThread { 37 public static void main(String[] args) throws InterruptedException { 38 // Thread t1=new CThread(); 39 // t1.start(); 40 // Thread t2=new CThread(); 41 // t2.start(); 42 43 44 //现在不要用单个线程去跑,当然这个是服务器端一定干的事,最好使用线程池 45 //使用线程池的好处: 46 // 1、多线程运行时间,系统不断的启动和关闭新线程,成本非常高 47 // 2、使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池, 48 // 线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。 49 50 51 // 调用Executors类的静态工厂方法创建一个ExecutorService对象。 52 ExecutorService server=Executors.newFixedThreadPool(2);//创建两个线程的池 53 // 创建Runnable实现类,作为线程执行任务 54 // 调用ExecutorService对象的submit方法来提交Runnable实例。 55 server.submit(new CThread());//提交线程到线程池去处理 56 server.submit(new CThread()); 57 // 当不想提交任何任务时调用ExecutorService对象的shutdown方法来关闭线程池。 58 server.shutdown();//关闭线程池 59 60 } 61 }
十一、定时调度
1. 定时调度的本质是一个线程,只是系统控制了这个线程运行的时间与频率
1 package com.study.test; 2 3 import static java.lang.System.out; 4 import static java.lang.System.in; 5 6 import java.io.IOException; 7 import java.util.Timer; 8 import java.util.TimerTask; 9 10 class aa extends TimerTask { 11 12 @Override 13 public void run() { 14 out.println("1 "); 15 // 定时调度的本质是一个线程,只是系统控制了这个线程运行的时间与频率 16 out.println(Thread.currentThread().getName()); 17 } 18 19 } 20 21 public class MyTimer { 22 public static void main(String[] args) throws InterruptedException, IOException { 23 24 Timer t1 = new Timer(); 25 t1.schedule(new aa(), 0, 1000); // 利用cpu的时钟去控制的 26 in.read();//输入任意一个值回车时种植执行调度 27 t1.cancel();//表示终止定时器 28 29 } 30 31 }
2. schedule到达某一个时间点开始执行
1 package com.study.test; 2 3 import static java.lang.System.out; 4 import static java.lang.System.in; 5 6 import java.io.IOException; 7 import java.util.Calendar; 8 import java.util.Timer; 9 import java.util.TimerTask; 10 11 class aa extends TimerTask { 12 13 @Override 14 public void run() { 15 out.println("1 "); 16 // 定时调度的本质是一个线程,只是系统控制了这个线程运行的时间与频率 17 out.println(Thread.currentThread().getName()); 18 } 19 20 } 21 22 public class MyTimer { 23 public static void main(String[] args) throws InterruptedException, IOException { 24 25 Timer t1 = new Timer(); 26 aa aa1 = new aa(); 27 Calendar c1 = Calendar.getInstance(); 28 c1.add(Calendar.SECOND, 5); 29 c1.set(2017, 10, 8, 00, 29, 10); 30 t1.schedule(aa1, c1.getTime());// 到了某一个时间点开始执行 31 32 } 33 34 }
3. 到达某一个时间点,再按照频率执行
1 package com.study.test; 2 3 import static java.lang.System.out; 4 import static java.lang.System.in; 5 6 import java.io.IOException; 7 import java.util.Calendar; 8 import java.util.Timer; 9 import java.util.TimerTask; 10 11 class aa extends TimerTask { 12 13 @Override 14 public void run() { 15 out.println("1 "); 16 // 定时调度的本质是一个线程,只是系统控制了这个线程运行的时间与频率 17 out.println(Thread.currentThread().getName()); 18 } 19 20 } 21 22 public class MyTimer { 23 public static void main(String[] args) throws InterruptedException, IOException { 24 25 Timer t1 = new Timer(); 26 aa aa1 = new aa(); 27 Calendar c1 = Calendar.getInstance(); 28 c1.add(Calendar.SECOND, 5); 29 c1.set(2017, 10, 8, 00, 35, 00); 30 // t1.schedule(aa1, c1.getTime());// 到了某一个时间点开始执行 31 t1.schedule(aa1, c1.getTime(), 1000);// 到了某一个时间点开始,按照1秒的频率 32 33 } 34 35 }
4. 延误时间间隔之后执行
1 package com.study.test; 2 3 import static java.lang.System.out; 4 import static java.lang.System.in; 5 6 import java.io.IOException; 7 import java.util.Calendar; 8 import java.util.Timer; 9 import java.util.TimerTask; 10 11 class aa extends TimerTask { 12 13 @Override 14 public void run() { 15 out.println("1 "); 16 // 定时调度的本质是一个线程,只是系统控制了这个线程运行的时间与频率 17 out.println(Thread.currentThread().getName()); 18 } 19 20 } 21 22 public class MyTimer { 23 public static void main(String[] args) throws InterruptedException, IOException { 24 25 Timer t1 = new Timer(); 26 aa aa1 = new aa(); 27 Calendar c1 = Calendar.getInstance(); 28 c1.add(Calendar.SECOND, 5); 29 c1.set(2017, 10, 8, 00, 35, 00); 30 // t1.schedule(aa1, c1.getTime());// 到了某一个时间点开始执行 31 // t1.schedule(aa1, c1.getTime(), 1000);// 到了某一个时间点开始,按照1秒的频率 32 t1.schedule(aa1, 2000, 1000);// 延误多少时间之后,按照1秒的频率 33 34 } 35 36 }
5. 固定频率
1 package com.study.test; 2 3 import static java.lang.System.out; 4 import static java.lang.System.in; 5 6 import java.io.IOException; 7 import java.util.Calendar; 8 import java.util.Timer; 9 import java.util.TimerTask; 10 11 class aa extends TimerTask { 12 13 @Override 14 public void run() { 15 out.println("1 "); 16 // 定时调度的本质是一个线程,只是系统控制了这个线程运行的时间与频率 17 out.println(Thread.currentThread().getName()); 18 } 19 20 } 21 22 public class MyTimer { 23 public static void main(String[] args) throws InterruptedException, IOException { 24 25 Timer t1 = new Timer(); 26 aa aa1 = new aa(); 27 Calendar c1 = Calendar.getInstance(); 28 c1.add(Calendar.SECOND, 5); 29 c1.set(2017, 10, 8, 00, 35, 00); 30 // t1.schedule(aa1, c1.getTime());// 到了某一个时间点开始执行 31 // t1.schedule(aa1, c1.getTime(), 1000);// 到了某一个时间点开始,按照1秒的频率 32 // t1.schedule(aa1, 2000, 1000);// 延误多少时间之后,按照1秒的频率 33 // t1.schedule(aa1,c1.getTime(),5000 );//过去的时间以当前时间为准,然后按照频率运行 34 t1.scheduleAtFixedRate(aa1, c1.getTime(), 5000);// 一定要补足,以固定频率为准 35 36 } 37 38 }
6. 多定时器,多任务
1 package com.study.test; 2 3 import static java.lang.System.out; 4 5 import java.io.IOException; 6 import java.text.SimpleDateFormat; 7 import java.util.Date; 8 import java.util.Timer; 9 import java.util.TimerTask; 10 11 class aa extends TimerTask { 12 13 @Override 14 public void run() { 15 out.println("1 "); 16 } 17 18 } 19 20 class bb extends TimerTask { 21 22 @Override 23 public void run() { 24 // TODO Auto-generated method stub 25 out.println("2 "); 26 } 27 28 } 29 30 class cc extends TimerTask { 31 32 @Override 33 public void run() { 34 SimpleDateFormat sf = new SimpleDateFormat("yyy.MM.dd hh:mm:ss"); 35 String curDate = sf.format(new Date(this.scheduledExecutionTime())); 36 out.println(curDate + " 3 "); 37 } 38 39 } 40 41 public class MyTimer { 42 public static void main(String[] args) throws InterruptedException, IOException { 43 Timer t1 = new Timer(); 44 aa aa1 = new aa(); 45 bb bb1 = new bb(); 46 t1.scheduleAtFixedRate(aa1, 0, 2000);// 加入一个任务 47 t1.scheduleAtFixedRate(bb1, 0, 1000);// 加入一个任务 48 Timer t2 = new Timer(); 49 cc cc1 = new cc(); 50 t2.scheduleAtFixedRate(cc1, 0, 3000);// 另外一个定时器加入任务 51 52 } 53 54 }