转载和引用,请注明原文出处! Fork me on GitHub
结局很美妙的事,开头并非如此!

线程

一、线程的概念

进程是正在执行的程序。在操作系统中进程是进行系统资源分配、调度和管理的最小单位,进程在执行过程中拥有独立的内存单元。比如: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 }
View Code

 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 }
View Code

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 }
View Code

      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 }
View Code

五、线程安全类

       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 }
View Code

六、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 }
View Code

十、线程池以及使用线程池的好处

       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 }
View Code

十一、定时调度

          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 }
View Code

           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 }
View Code

           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 }
View Code

           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 }
View Code

           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 }
View Code

           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 }
View Code

 

posted @ 2017-10-08 00:44  小不点啊  阅读(441)  评论(0编辑  收藏  举报