zoukankan      html  css  js  c++  java
  • 线程

    一、线程的概念

    进程是正在执行的程序。在操作系统中进程是进行系统资源分配、调度和管理的最小单位,进程在执行过程中拥有独立的内存单元。比如: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
  • 相关阅读:
    java——对象学习笔记
    ORACLE中如何查看分区表信息
    postgresql相关系统表查询
    oracle系统表查询
    linux shell常用函数,ps
    文本文件合并
    nginx多版本PHP配置
    webuploader实现文件上传
    open abc.txt: The system cannot find the file specified
    PHP在线批量下载文件
  • 原文地址:https://www.cnblogs.com/leeSmall/p/7636360.html
Copyright © 2011-2022 走看看