精华内容
下载资源
问答
  • 创建线程方式一:继承Thread类 步骤: 1,定义一个类继承Thread类。 ...3,直接创建Thread的子类对象创建线程。... 2 * 需求:我们要实现多线程的程序。 3 * 如何实现呢? 4 * 由于线程是依赖进程

    创建线程方式一:继承Thread类

    步骤:

    1,定义一个类继承Thread类。

    2,覆盖Thread类中的run方法。

    3,直接创建Thread的子类对象创建线程。

    4,调用start方法开启线程并调用线程的任务run方法执行。

     

    复制代码
     1 /*
     2  * 需求:我们要实现多线程的程序。
     3  * 如何实现呢?
     4  *     由于线程是依赖进程而存在的,所以我们应该先创建一个进程出来。
     5  *     而进程是由系统创建的,所以我们应该去调用系统功能创建一个进程。
     6  *     Java是不能直接调用系统功能的,所以,我们没有办法直接实现多线程程序。
     7  *     但是呢?Java可以去调用C/C++写好的程序来实现多线程程序。
     8  *     由C/C++去调用系统功能创建进程,然后由Java去调用这样的东西,
     9  *     然后提供一些类供我们使用。我们就可以实现多线程程序了。
    10 
    11  * 那么Java提供的类是什么呢?
    12  *         Thread
    13  *         通过查看API,我们知道了有2中方式实现多线程程序。
    14  
    15  * 方式1:继承Thread类。
    16  * 步骤
    17  *     A:自定义类MyThread继承Thread类。
    18  *     B:MyThread类里面重写run()?
    19  *     C:创建对象 new
    20  *     D:启动线程 start
    21  */
    22 
    23 
    24 /*
    25  * 该类要重写run()方法,为什么呢?
    26  * 不是类中的所有代码都需要被线程执行的。
    27  * 而这个时候,为了区分哪些代码能够被线程执行,java提供了Thread类中的run()用来包含那些被线程执行的代码。
    28  */
    29 
    30 public class MyThread extends Thread {
    31     @Override
    32     public void run() {
    33         // 自己写代码
    34         // 一般来说,被线程执行的代码肯定是比较耗时的。所以我们用循环
    35         for (int x = 0; x < 200; x++) {
    36             System.out.println(x);
    37         }
    38     }
    39 }
    复制代码

    若执行以下代码:

    1
    2
    3
    MyThread my = new MyThread();
    my.run();
    my.run();

    观察结果:程序是顺序执行的,并没有交替执行的现象的,为什么?

    答:调用run()方法是单线程的。

          因为run()方法直接调用其实就相当于普通的方法调用,所以你看到的是单线程的效果

    要想看到多线程的效果,就必须说说另一个方法:start()

    MyThread my = new MyThread();

    my.start();

     

    面试题:run()和start()的区别?

    run():仅仅是封装被线程执行的代码,直接调用是普通方法

    start():首先启动了线程,然后再由jvm去调用该线程的run()方法

     

    由于只start()了一次,观察不到想要的结果,那么就会想出如下的代码:

    MyThread my = new MyThread();

    my.start();

    my.start();

    运行后会发现抛出错误: IllegalThreadStateException:非法的线程状态异常

    为什么呢?

    因为start()2次相当于是my线程被调用了两次,而不是两个线程启动。

    一个线程只能调用一次

    要想观察到多线程的效果,正确的代码是:

    1 MyThread my1 = new MyThread();
    2 MyThread my2 = new MyThread();
    3 my1.start();
    4 my2.start();

    运行后就能够看见效果了。

     

    1.1线程名字

    通过运行结果我们可以发现:我们并不知道谁是谁? 即哪个线程在执行

    如何获取线程的名称呢?  getName()方法

    复制代码
     1 public class MyThread extends Thread {
     2     public void run() {
     3        for (int x = 0; x < 100; x++) {
     4            System.out.println(getName() + ":" + x);
     5        }
     6     }
     7 }
     8 
     9 MyThread my1 = new MyThread();
    10 MyThread my2 = new MyThread();
    11 my1.start();
    12 my2.start();
    复制代码

    发现:可以通过Thread的getName获取线程的名称 Thread-编号(从0开始)

    名称为什么是:Thread-? 编号

    下面看一下Thread的代码:

    复制代码
     1 class MyThread extends Thread {
     2     public MyThread() {
     3         super();
     4     }
     5 }
     6 
     7 class Thread {
     8     private char name[];
     9 
    10     public Thread() {
    11         init(null, null, "Thread-" + nextThreadNum(), 0);
    12     }
    13     
    14     private void init(ThreadGroup g, Runnable target, String name,long stackSize) {
    15         init(g, target, name, stackSize, null);
    16     }
    17     
    18      private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc) {
    19         //大部分代码被省略了
    20         this.name = name.toCharArray();
    21     }
    22     
    23     public final void setName(String name) {
    24         this.name = name.toCharArray();
    25     }
    26     
    27     private static int threadInitNumber; //0,1,2
    28     private static synchronized int nextThreadNum() {
    29         return threadInitNumber++; //return 0,1
    30     }
    31     
    32     public final String getName() {
    33         return String.valueOf(name);
    34     }
    35 }
    复制代码

    由原码可知:Thread通过一个字符数组name表示线程名称,用threadInitNumber变量来记录线程的编号

                    每当一个线程调用时,threadInitNumber++以达到计数的目的,然后 "Thread-" + threadInitNumber转成字符数组给name

                    调用getName()是将name转换为string,这就是我们看到的效果。

    但默认的方法对我们来说往往没有意义。但我们可以通过setName(String name)来设置我们想要的名字

    复制代码
     1 我们还可以通过有参数的构造方法来命名线程
     2 public class MyThread extends Thread {
     3     public MyThread() {
     4     }
     5     
     6     public MyThread(String name){
     7         super(name);
     8     }
     9 
    10     @Override
    11     public void run() {
    12         for (int x = 0; x < 100; x++) {
    13             System.out.println(getName() + ":" + x);
    14         }
    15     }
    16 }
    17 //带参构造方法给线程起名字
    18 MyThread my1 = new MyThread("林青霞");
    19 MyThread my2 = new MyThread("刘意");
    20 my1.start();
    21 my2.start();
    复制代码

     

    主线程的名字就是main 我们如何获取呢?

    遇到这种情况,Thread类提供了一个很好玩的方法: public static Thread currentThread():返回当前正在执行的线程对象

    在main方法中使用下面语句就可以得到多线程的名称 System.out.println(Thread.currentThread().getName());

     

    1.2线程调度优先级

     

    假如我们的计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,

    线程只有得到 CPU时间片,也就是使用权,才可以执行指令。

    那么Java是如何对线程进行调用的呢?

     

    线程有两种调度模型:

      分时调度模型     所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片

      抢占式调度模型   优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。

     

     Java使用的是抢占式调度模型:

    static int MAX_PRIORITY 线程可以具有的最高优先级    10

    static int MIN_PRIORITY  线程可以具有的最低优先级   1

    static int NORM_PRIORITY 分配给线程的默认优先级     5

     

    public final void setPriority(int newPriority)更改线程的优先级

    public final int getPriority()返回线程的优先级

     

     * 注意:

    线程默认优先级是NORM_PRIORITY = 5

    线程优先级的范围是:1-10

    线程优先级高仅仅表示线程获取的 CPU时间片的几率高(并非高优先级的线程一定每次都优先),

    但是要在次数比较多,或者多次运行的时候才能看到比较好的效果。

    复制代码
     1 public class ThreadPriority extends Thread {
     2 
     3     public void run() {
     4 
     5        for (int x = 0; x < 100; x++) {
     6            System.out.println(getName() + ":" + x);
     7        }
     8     }
     9 }
    10 
    11 ThreadPriority tp1 = new ThreadPriority();
    12 ThreadPriority tp2 = new ThreadPriority();
    13 ThreadPriority tp3 = new ThreadPriority();
    14 
    15  
    16 tp1.setName("东方不败");
    17 tp2.setName("岳不群");
    18 tp3.setName("林平之");
    19 
    20  
    21 // 获取默认优先级
    22 System.out.println(tp1.getPriority());
    23 System.out.println(tp2.getPriority());
    24 System.out.println(tp3.getPriority());
    25 
    28 //设置线程优先级
    29 tp1.setPriority(10);
    30 tp2.setPriority(1);
    31 
    34 tp1.start();
    35 tp2.start();
    36 tp3.start();
    37 
    复制代码

     

    如果设置线程优先级: tp1.setPriority(100000);

    报错:

    IllegalArgumentException:非法参数异常。

    抛出的异常表明向方法传递了一个不合法或不正确的参数。

     

    1.3 sleep

     线程休眠  public static void sleep(long millis)

      Thread.sleep(1000); //1秒

      在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),

      此操作受到系统计时器和调度程序精度和准确性的影响。

      该线程不丢失任何监视器的所属权时间到后该线程继续执行 即不会执行的权利 时间到后继续往下走

    我们可以通过以下代码感受:

    复制代码
     1 public class ThreadSleep extends Thread {
     2     @Override
     3     public void run() {
     4         for (int x = 0; x < 100; x++) {
     5             System.out.println(getName() + ":" + x + ",日期:" + new Date());
     6             try {
     7                 Thread.sleep(1000);
     8             } catch (InterruptedException e) {
     9                 e.printStackTrace();
    10             }
    11         }
    12     }
    13 }
    14 public class ThreadSleepDemo {
    15     public static void main(String[] args) {
    16         ThreadSleep ts1 = new ThreadSleep();
    17         ThreadSleep ts2 = new ThreadSleep();
    18         ThreadSleep ts3 = new ThreadSleep();
    19 
    20         ts1.setName("林青霞");
    21         ts2.setName("林志玲");
    22         ts3.setName("林志颖");
    23 
    24         ts1.start();
    25         ts2.start();
    26         ts3.start();
    27     }
    28 }
    复制代码

     

    创建线程的第二种方式:实现Runnable接口

    1,定义类实现Runnable接口。

    2,覆盖接口中的run方法,将线程的任务代码封装到run方法中。

    3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。

        为什么?

           为线程的任务都封装在Runnable接口子类对象的run方法中。

          所以要在线程对象创建时就必须明确要运行的任务。

    4,调用线程对象的start方法开启线程。

     

    实现Runnable接口的好处:

    1,将线程的任务从线程的子类中分离出来,进行了单独的封装。按照面向对象的思想将任务的封装成对象。

    2,避免了java单继承的局限性。

    所以,创建线程的第二种方式较为常用。

    实例

    复制代码
     1 /*
     2  * 方式2:实现Runnable接口
     3  * 步骤:
     4  *         A:自定义类MyRunnable实现Runnable接口
     5  *         B:重写run()方法
     6  *         C:创建MyRunnable类的对象
     7  *         D:创建Thread类的对象,并把C步骤的对象作为构造参数传递
     8  */
     9 
    10 public class MyRunnable implements Runnable {
    11 
    12     @Override
    13     public void run() {
    14         for (int x = 0; x < 100; x++) {
    15             // 由于实现接口的方式就不能直接使用Thread类的方法了,但是可以间接的使用
    16             System.out.println(Thread.currentThread().getName() + ":" + x);
    17         }
    18     }
    19 
    20 }
    21 
    22 使用:
    23 MyRunnable my = new MyRunnable();
    24 // 创建Thread类的对象,并把C步骤的对象作为构造参数传递 Thread(Runnable target)
    25 Thread t1 = new Thread(my);
    26 Thread t2 = new Thread(my);
    27 t1.setName("林青霞");
    28 t2.setName("刘意");
    29 t1.start();
    30 t2.start();
    31 
    32 MyRunnable my = new MyRunnable();
    33 // Thread(Runnable target, String name)
    34 Thread t1 = new Thread(my, "林青霞");
    35 Thread t2 = new Thread(my, "刘意");
    36 t1.start();
    37 t2.start();
    复制代码

    2.1比较

    2.2 实现Runnable的最大好处就是可以实现共享同一资源

    案例2:经典的买票问题

    某电影院目前正在上映贺岁大片(红高粱,少林寺传奇藏经阁),

    共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。

     

    先用Thread实现

    复制代码
    public class SellTicket extends Thread {
    
        // 定义100张票
        // private int tickets = 100;
        // 为了让多个线程对象共享这100张票,我们其实应该用静态修饰
        private static int tickets = 100;
    
        public void run() {
    
           // 定义100张票
           // 每个线程进来都会走这里,这样的话,每个线程对象相当于买的是自己的那100张票,这不合理,所以应该定义到外面
           // int tickets = 100;
           // 是为了模拟一直有票
           while (true) {
               if (tickets > 0) {
                  System.out.println(getName() + "正在出售第" + (tickets--) + "张票");
               }
           }
        }
    }
    
    public class SellTicketDemo {
    
        public static void main(String[] args) {
    
           // 创建三个线程对象
           SellTicket st1 = new SellTicket();
           SellTicket st2 = new SellTicket();
           SellTicket st3 = new SellTicket();
    
           // 给线程对象起名字
           st1.setName("窗口1");
           st2.setName("窗口2");
           st3.setName("窗口3");
    
           // 启动线程
           st1.start();
           st2.start();
           st3.start();
        }
    }
    复制代码

     

    现在用Runable接口:

    复制代码
     1 public class SellTicket implements Runnable {
     2     // 定义100张票
     3     private int tickets = 100;
     4     public void run() {
     5        while (true) {
     6            if (tickets > 0) {
     7 
     8               try{Thread.sleep(100)}catch(Exception e){}
     9               
    10               System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
    11            }
    12        }
    13     }
    14 }

    1
    2
    3
    new Thread(new SellTicket()).start();
    new Thread(new SellTicket()).start();
    new Thread(new SellTicket()).start();
    复制代码

     

    我们每次卖票都延迟100毫秒,此时你就会出现问题

    原因就是因为多个线程并发导致了共享数据的安全问题

     

    如何解决线程安全问题呢?

     要想解决问题,就要知道哪些原因会导致出问题:(而且这些原因也是以后我们判断一个程序是否会有线程安全问题的标准)

     A:是否是多线程环境

     B:是否有共享数据

     C:是否有多条语句操作共享数据

    我们来回想一下我们的程序有没有上面的问题呢?

    A:是否是多线程环境  是

    B:是否有共享数据   是

    C:是否有多条语句操作共享数据   是

     

    由此可见我们的程序出现问题是正常的,因为它满足出问题的条件。

    接下来才是我们要想想如何解决问题呢?

    A和B的问题我们改变不了,我们只能想办法去把C改变一下。

     

    Solution:

     基本上所有的并发模式在解决线程安全问题时,都采用“序列化访问临界资源”的方案,即在同一时刻,只能有一个线程访问临界资源,也称作同步互斥访问

     通常来说,是在访问临界资源前面加上一个锁,当访问完临界资源后释放锁,让其他线程继续访问。

       解决思就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程时不可以参与运算的。必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。 

     

    Java提供的解决方案

    思想:

    把多条语句操作共享数据的代码给包成一个整体,让某个线程在执行的时候,别人不能来执行。

    问题是我们不知道怎么包啊?Java给我们提供了:同步机制。

     

    解决思就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,

    其他线程时不可以参与运算的。必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。

    在java中,用同步代码块就可以解决这个问题。

     

    同步代码块的格式:

    synchronized(对象)

    {

        需要被同步的代码;

    }

     

    A:对象是什么呢?

        我们可以随便创建一个对象试试。(对象其实可以是任意类型的,但必须是同一个对象)

           synchronized(new Object()){}  No

           synchronized(object){}         Yes  可以使用 class文件

     

    B:需要同步的代码是哪些呢?

      把多条语句操作共享数据的代码的部分给包起来

     

    注意:

    同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。

    多个线程必须是同一把锁。

     

    同步的好处:解决了线程的安全问题。

    同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。

    同步的前提:同步中必须有多个线程并使用同一个锁。

     

    l  synchronized 代码块

     synchronized(...){
        .....
      }
     
    卖票问题的解决:
    复制代码
     1 public class ThreadDemo03 {
     2     public static void main(String[] args) 
     3     {
     4         Ticket t = new Ticket();//创建一个线程任务对象  此时只有一个Ticket对象  且只能有一个
     5 
     6         Thread t1 = new Thread(t);
     7         Thread t2 = new Thread(t);
     8         Thread t3 = new Thread(t);
     9         Thread t4 = new Thread(t);
    10 
    11         t1.start();
    12         t2.start();
    13         t3.start();
    14         t4.start();
    15     }
    16 }
    17 
    18 class Ticket implements Runnable{
    19     private  int num = 100;    //共享资源 必须放在这里 而不能在run()中
    20     Object obj = new Object();//对象锁只能有一个
    21 
    22     public void run()
    23     {
    24         while(true)
    25         {
    26             synchronized(obj)
    27             {
    28                 if(num>0)
    29                 {
    30                     try{Thread.sleep(1000);}catch (InterruptedException e){}
    31                     System.out.println(Thread.currentThread().getName()+".....sale...."+num--);
    32                 }
    33             }
    34         }
    35     }
    36 }
    复制代码

    l  synchronized 方法:

    public synchronized type function (... ...); 
    复制代码
     1 private static synchronized void sellTicket() {
     2         if (tickets > 0) {
     3         try {
     4                 Thread.sleep(100);
     5         } catch (InterruptedException e) {
     6                 e.printStackTrace();
     7         }
     8         System.out.println(Thread.currentThread().getName()
     9                     + "正在出售第" + (tickets--) + "张票 ");
    10         }
    11 }
    复制代码

     

    l  当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?

       对象的synchronized方法不能进入了,但它的其他非synchronized方法还是可以访问的

    复制代码
     1 public class TT implements Runnable {
     2 
     3      int b = 100;
     4 
     5      public synchronized void m1(){
     6             b = 10000;
     7             try {
     8                 Thread. sleep(5000);
     9                 System. out.println( "m1----b="+ b);
    10            } catch (InterruptedException e) {
    11                 e.printStackTrace();
    12            }
    13      }
    14 
    15      public void m2(){
    16            System. out.println( "m2====="+ b);
    17      }
    18 
    19      @Override
    20      public void run() {
    21            m1();
    22      }
    23 
    24      public static void main(String[] args) {
    25            TT tt = new TT();
    26            Thread t = new Thread(tt);
    27            t.start();
    28 
    29             try {
    30                 Thread. sleep(1000);
    31            } catch (InterruptedException e) {
    32                 e.printStackTrace();
    33            }
    34 
    35            tt.m2();
    36      }
    37 
    38 }
    复制代码

     

    锁对象是谁?

     * A:同步代码块的锁对象是谁呢?

     *     任意对象。

     

     * B:同步方法的格式及锁对象问题?

     *     把同步关键字加在方法上。

     *     同步方法是谁呢?

     *         this

     

     * C:静态方法及锁对象问题?

     *     静态方法的锁对象是谁呢?

     *     类的字节码文件对象。(反射会讲)

     

     

    线程安全的类

    StringBuffer sb = new StringBuffer();

    Vector<String> v = new Vector<String>();

    Hashtable<String, String> h = new Hashtable<String, String>();

    Vector是线程安全的时候才去考虑使用的,但是即使要安全,我也不用你  

    那么到底用谁呢?public static <T> List<T> synchronizedList(List<T> list)

    List<String> list1 = new ArrayList<String>();// 线程不安全

    List<String> list2 = Collections.synchronizedList(new ArrayList<String>()); // 线程安全

     

    synchornized

    synchronized是java中的一个关键字,也就是说是Java语言内置的特性。

    如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:

      1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;

      2)线程执行发生异常,此时JVM会让线程自动释放锁。

     

    原理示意图

    jdk1.5还提供了lock锁 后面再说



    转载地址:http://www.cnblogs.com/wihainan/p/4760874.html

    展开全文
  • 所有程序运行结果 请自行得出 创建线程方式一:继承Thread类 步骤: 1,定义一个类继承Thread类。 2,覆盖Thread类中的run方法。... 2 * 需求:我们要实现多线程的程序。 3 * 如何实现呢? 4 * 由于线程...

    所有程序运行结果 请自行得出

    创建线程方式一:继承Thread类

    步骤:

    1,定义一个类继承Thread类。

    2,覆盖Thread类中的run方法。

    3,直接创建Thread的子类对象创建线程。

    4,调用start方法开启线程并调用线程的任务run方法执行。

     

     1 /*
     2  * 需求:我们要实现多线程的程序。
     3  * 如何实现呢?
     4  *     由于线程是依赖进程而存在的,所以我们应该先创建一个进程出来。
     5  *     而进程是由系统创建的,所以我们应该去调用系统功能创建一个进程。
     6  *     Java是不能直接调用系统功能的,所以,我们没有办法直接实现多线程程序。
     7  *     但是呢?Java可以去调用C/C++写好的程序来实现多线程程序。
     8  *     由C/C++去调用系统功能创建进程,然后由Java去调用这样的东西,
     9  *     然后提供一些类供我们使用。我们就可以实现多线程程序了。
    10 
    11  * 那么Java提供的类是什么呢?
    12  *         Thread
    13  *         通过查看API,我们知道了有2中方式实现多线程程序。
    14  
    15  * 方式1:继承Thread类。
    16  * 步骤
    17  *     A:自定义类MyThread继承Thread类。
    18  *     B:MyThread类里面重写run()?
    19  *     C:创建对象 new
    20  *     D:启动线程 start
    21  */
    22 
    23 
    24 /*
    25  * 该类要重写run()方法,为什么呢?
    26  * 不是类中的所有代码都需要被线程执行的。
    27  * 而这个时候,为了区分哪些代码能够被线程执行,java提供了Thread类中的run()用来包含那些被线程执行的代码。
    28  */
    29 
    30 public class MyThread extends Thread {
    31     @Override
    32     public void run() {
    33         // 自己写代码
    34         // 一般来说,被线程执行的代码肯定是比较耗时的。所以我们用循环
    35         for (int x = 0; x < 200; x++) {
    36             System.out.println(x);
    37         }
    38     }
    39 }

    若执行以下代码:

    MyThread my = new MyThread();
    my.run();
    my.run();
    

    观察结果:程序是顺序执行的,并没有交替执行的现象的,为什么?

    答:调用run()方法是单线程的。

          因为run()方法直接调用其实就相当于普通的方法调用,所以你看到的是单线程的效果

    要想看到多线程的效果,就必须说说另一个方法:start()

    MyThread my = new MyThread();

    my.start();

     

    面试题:run()和start()的区别?

    run():仅仅是封装被线程执行的代码,直接调用是普通方法

    start():首先启动了线程,然后再由jvm去调用该线程的run()方法

     

    由于只start()了一次,观察不到想要的结果,那么就会想出如下的代码:

    MyThread my = new MyThread();

    my.start();

    my.start();

    运行后会发现抛出错误: IllegalThreadStateException:非法的线程状态异常

    为什么呢?

    因为start()2次相当于是my线程被调用了两次,而不是两个线程启动。

    一个线程只能调用一次

    要想观察到多线程的效果,正确的代码是:

    1 MyThread my1 = new MyThread();
    2 MyThread my2 = new MyThread();
    3 my1.start();
    4 my2.start();

    运行后就能够看见效果了。

     

    1.1线程名字

    通过运行结果我们可以发现:我们并不知道谁是谁? 即哪个线程在执行

    如何获取线程的名称呢?  getName()方法

     1 public class MyThread extends Thread {
     2     public void run() {
     3        for (int x = 0; x < 100; x++) {
     4            System.out.println(getName() + ":" + x);
     5        }
     6     }
     7 }
     8 
     9 MyThread my1 = new MyThread();
    10 MyThread my2 = new MyThread();
    11 my1.start();
    12 my2.start();

    发现:可以通过Thread的getName获取线程的名称 Thread-编号(从0开始)

    名称为什么是:Thread-? 编号

    下面看一下Thread的代码:

     1 class MyThread extends Thread {
     2     public MyThread() {
     3         super();
     4     }
     5 }
     6 
     7 class Thread {
     8     private char name[];
     9 
    10     public Thread() {
    11         init(null, null, "Thread-" + nextThreadNum(), 0);
    12     }
    13     
    14     private void init(ThreadGroup g, Runnable target, String name,long stackSize) {
    15         init(g, target, name, stackSize, null);
    16     }
    17     
    18      private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc) {
    19         //大部分代码被省略了
    20         this.name = name.toCharArray();
    21     }
    22     
    23     public final void setName(String name) {
    24         this.name = name.toCharArray();
    25     }
    26     
    27     private static int threadInitNumber; //0,1,2
    28     private static synchronized int nextThreadNum() {
    29         return threadInitNumber++; //return 0,1
    30     }
    31     
    32     public final String getName() {
    33         return String.valueOf(name);
    34     }
    35 }

    由原码可知:Thread通过一个字符数组name表示线程名称,用threadInitNumber变量来记录线程的编号

                    每当一个线程调用时,threadInitNumber++以达到计数的目的,然后 "Thread-" + threadInitNumber转成字符数组给name

                    调用getName()是将name转换为string,这就是我们看到的效果。

    但默认的方法对我们来说往往没有意义。但我们可以通过setName(String name)来设置我们想要的名字

     1 我们还可以通过有参数的构造方法来命名线程
     2 public class MyThread extends Thread {
     3     public MyThread() {
     4     }
     5     
     6     public MyThread(String name){
     7         super(name);
     8     }
     9 
    10     @Override
    11     public void run() {
    12         for (int x = 0; x < 100; x++) {
    13             System.out.println(getName() + ":" + x);
    14         }
    15     }
    16 }
    17 //带参构造方法给线程起名字
    18 MyThread my1 = new MyThread("林青霞");
    19 MyThread my2 = new MyThread("刘意");
    20 my1.start();
    21 my2.start();

     

    主线程的名字就是main 我们如何获取呢?

    遇到这种情况,Thread类提供了一个很好玩的方法: public static Thread currentThread():返回当前正在执行的线程对象

    在main方法中使用下面语句就可以得到多线程的名称 System.out.println(Thread.currentThread().getName());

     

    1.2线程调度优先级

     

    假如我们的计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,

    线程只有得到 CPU时间片,也就是使用权,才可以执行指令。

    那么Java是如何对线程进行调用的呢?

     

    线程有两种调度模型:

      分时调度模型     所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片

      抢占式调度模型   优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。

     

     Java使用的是抢占式调度模型:

    static int MAX_PRIORITY 线程可以具有的最高优先级    10

    static int MIN_PRIORITY  线程可以具有的最低优先级   1

    static int NORM_PRIORITY 分配给线程的默认优先级     5

     

    public final void setPriority(int newPriority)更改线程的优先级

    public final int getPriority()返回线程的优先级

     

     * 注意:

    线程默认优先级是NORM_PRIORITY = 5

    线程优先级的范围是:1-10

    线程优先级高仅仅表示线程获取的 CPU时间片的几率高(并非高优先级的线程一定每次都优先),

    但是要在次数比较多,或者多次运行的时候才能看到比较好的效果。

     1 public class ThreadPriority extends Thread {
     2 
     3     public void run() {
     4 
     5        for (int x = 0; x < 100; x++) {
     6            System.out.println(getName() + ":" + x);
     7        }
     8     }
     9 }
    10 
    11 ThreadPriority tp1 = new ThreadPriority();
    12 ThreadPriority tp2 = new ThreadPriority();
    13 ThreadPriority tp3 = new ThreadPriority();
    14 
    15  
    16 tp1.setName("东方不败");
    17 tp2.setName("岳不群");
    18 tp3.setName("林平之");
    19 
    20  
    21 // 获取默认优先级
    22 System.out.println(tp1.getPriority());
    23 System.out.println(tp2.getPriority());
    24 System.out.println(tp3.getPriority());
    25 
    28 //设置线程优先级
    29 tp1.setPriority(10);
    30 tp2.setPriority(1);
    31 
    34 tp1.start();
    35 tp2.start();
    36 tp3.start();
    37 

     

    如果设置线程优先级: tp1.setPriority(100000);

    报错:

    IllegalArgumentException:非法参数异常。

    抛出的异常表明向方法传递了一个不合法或不正确的参数。

     

    1.3 sleep

     线程休眠  public static void sleep(long millis)

      Thread.sleep(1000); //1秒

      在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),

      此操作受到系统计时器和调度程序精度和准确性的影响。

      该线程不丢失任何监视器的所属权时间到后该线程继续执行 即不会执行的权利 时间到后继续往下走

    我们可以通过以下代码感受:

     1 public class ThreadSleep extends Thread {
     2     @Override
     3     public void run() {
     4         for (int x = 0; x < 100; x++) {
     5             System.out.println(getName() + ":" + x + ",日期:" + new Date());
     6             try {
     7                 Thread.sleep(1000);
     8             } catch (InterruptedException e) {
     9                 e.printStackTrace();
    10             }
    11         }
    12     }
    13 }
    14 public class ThreadSleepDemo {
    15     public static void main(String[] args) {
    16         ThreadSleep ts1 = new ThreadSleep();
    17         ThreadSleep ts2 = new ThreadSleep();
    18         ThreadSleep ts3 = new ThreadSleep();
    19 
    20         ts1.setName("林青霞");
    21         ts2.setName("林志玲");
    22         ts3.setName("林志颖");
    23 
    24         ts1.start();
    25         ts2.start();
    26         ts3.start();
    27     }
    28 }

     

    创建线程的第二种方式:实现Runnable接口

    1,定义类实现Runnable接口。

    2,覆盖接口中的run方法,将线程的任务代码封装到run方法中。

    3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。

        为什么?

           为线程的任务都封装在Runnable接口子类对象的run方法中。

          所以要在线程对象创建时就必须明确要运行的任务。

    4,调用线程对象的start方法开启线程。

     

    实现Runnable接口的好处:

    1,将线程的任务从线程的子类中分离出来,进行了单独的封装。按照面向对象的思想将任务的封装成对象。

    2,避免了java单继承的局限性。

    所以,创建线程的第二种方式较为常用。

    实例

     1 /*
     2  * 方式2:实现Runnable接口
     3  * 步骤:
     4  *         A:自定义类MyRunnable实现Runnable接口
     5  *         B:重写run()方法
     6  *         C:创建MyRunnable类的对象
     7  *         D:创建Thread类的对象,并把C步骤的对象作为构造参数传递
     8  */
     9 
    10 public class MyRunnable implements Runnable {
    11 
    12     @Override
    13     public void run() {
    14         for (int x = 0; x < 100; x++) {
    15             // 由于实现接口的方式就不能直接使用Thread类的方法了,但是可以间接的使用
    16             System.out.println(Thread.currentThread().getName() + ":" + x);
    17         }
    18     }
    19 
    20 }
    21 
    22 使用:
    23 MyRunnable my = new MyRunnable();
    24 // 创建Thread类的对象,并把C步骤的对象作为构造参数传递 Thread(Runnable target)
    25 Thread t1 = new Thread(my);
    26 Thread t2 = new Thread(my);
    27 t1.setName("林青霞");
    28 t2.setName("刘意");
    29 t1.start();
    30 t2.start();
    31 
    32 MyRunnable my = new MyRunnable();
    33 // Thread(Runnable target, String name)
    34 Thread t1 = new Thread(my, "林青霞");
    35 Thread t2 = new Thread(my, "刘意");
    36 t1.start();
    37 t2.start();

    2.1比较

    2.2 实现Runnable的最大好处就是可以实现共享同一资源

    案例2:经典的买票问题

    某电影院目前正在上映贺岁大片(红高粱,少林寺传奇藏经阁),

    共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。

     

    先用Thread实现

    public class SellTicket extends Thread {
    
        // 定义100张票
        // private int tickets = 100;
        // 为了让多个线程对象共享这100张票,我们其实应该用静态修饰
        private static int tickets = 100;
    
        public void run() {
    
           // 定义100张票
           // 每个线程进来都会走这里,这样的话,每个线程对象相当于买的是自己的那100张票,这不合理,所以应该定义到外面
           // int tickets = 100;
           // 是为了模拟一直有票
           while (true) {
               if (tickets > 0) {
                  System.out.println(getName() + "正在出售第" + (tickets--) + "张票");
               }
           }
        }
    }
    
    public class SellTicketDemo {
    
        public static void main(String[] args) {
    
           // 创建三个线程对象
           SellTicket st1 = new SellTicket();
           SellTicket st2 = new SellTicket();
           SellTicket st3 = new SellTicket();
    
           // 给线程对象起名字
           st1.setName("窗口1");
           st2.setName("窗口2");
           st3.setName("窗口3");
    
           // 启动线程
           st1.start();
           st2.start();
           st3.start();
        }
    }

     

    现在用Runable接口:

     1 public class SellTicket implements Runnable {
     2     // 定义100张票
     3     private int tickets = 100;
     4     public void run() {
     5        while (true) {
     6            if (tickets > 0) {
     7 
     8               try{Thread.sleep(100)}catch(Exception e){}
     9               
    10               System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
    11            }
    12        }
    13     }
    14 }

    new Thread(new SellTicket()).start();
    new Thread(new SellTicket()).start();
    new Thread(new SellTicket()).start();

     

    我们每次卖票都延迟100毫秒,此时你就会出现问题

    原因就是因为多个线程并发导致了共享数据的安全问题

     

    如何解决线程安全问题呢?

     要想解决问题,就要知道哪些原因会导致出问题:(而且这些原因也是以后我们判断一个程序是否会有线程安全问题的标准)

     A:是否是多线程环境

     B:是否有共享数据

     C:是否有多条语句操作共享数据

    我们来回想一下我们的程序有没有上面的问题呢?

    A:是否是多线程环境  是

    B:是否有共享数据   是

    C:是否有多条语句操作共享数据   是

     

    由此可见我们的程序出现问题是正常的,因为它满足出问题的条件。

    接下来才是我们要想想如何解决问题呢?

    A和B的问题我们改变不了,我们只能想办法去把C改变一下。

     

    Solution:

     基本上所有的并发模式在解决线程安全问题时,都采用“序列化访问临界资源”的方案,即在同一时刻,只能有一个线程访问临界资源,也称作同步互斥访问

     通常来说,是在访问临界资源前面加上一个锁,当访问完临界资源后释放锁,让其他线程继续访问。

       解决思就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程时不可以参与运算的。必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。 

     

    Java提供的解决方案

    思想:

    把多条语句操作共享数据的代码给包成一个整体,让某个线程在执行的时候,别人不能来执行。

    问题是我们不知道怎么包啊?Java给我们提供了:同步机制。

     

    解决思就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,

    其他线程时不可以参与运算的。必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。

    在java中,用同步代码块就可以解决这个问题。

     

    同步代码块的格式:

    synchronized(对象)

    {

        需要被同步的代码

    }

     

    A:对象是什么呢?

        我们可以随便创建一个对象试试。(对象其实可以是任意类型的,但必须是同一个对象)

           synchronized(new Object()){}  No

           synchronized(object){}         Yes  可以使用 class文件

     

    B:需要同步的代码是哪些呢?

      把多条语句操作共享数据的代码的部分给包起来

     

    注意:

    同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。

    多个线程必须是同一把锁。

     

    同步的好处:解决了线程的安全问题。

    同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。

    同步的前提:同步中必须有多个线程并使用同一个锁

     

    l  synchronized 代码块

     synchronized(...){
        .....
      }
     
    卖票问题的解决:
     1 public class ThreadDemo03 {
     2     public static void main(String[] args) 
     3     {
     4         Ticket t = new Ticket();//创建一个线程任务对象  此时只有一个Ticket对象  且只能有一个
     5 
     6         Thread t1 = new Thread(t);
     7         Thread t2 = new Thread(t);
     8         Thread t3 = new Thread(t);
     9         Thread t4 = new Thread(t);
    10 
    11         t1.start();
    12         t2.start();
    13         t3.start();
    14         t4.start();
    15     }
    16 }
    17 
    18 class Ticket implements Runnable{
    19     private  int num = 100;    //共享资源 必须放在这里 而不能在run()中
    20     Object obj = new Object();//对象锁只能有一个
    21 
    22     public void run()
    23     {
    24         while(true)
    25         {
    26             synchronized(obj)
    27             {
    28                 if(num>0)
    29                 {
    30                     try{Thread.sleep(1000);}catch (InterruptedException e){}
    31                     System.out.println(Thread.currentThread().getName()+".....sale...."+num--);
    32                 }
    33             }
    34         }
    35     }
    36 }

    l  synchronized 方法:

    public synchronized type function (... ...); 
     1 private static synchronized void sellTicket() {
     2         if (tickets > 0) {
     3         try {
     4                 Thread.sleep(100);
     5         } catch (InterruptedException e) {
     6                 e.printStackTrace();
     7         }
     8         System.out.println(Thread.currentThread().getName()
     9                     + "正在出售第" + (tickets--) + "张票 ");
    10         }
    11 }

     

    l  当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?

       对象的synchronized方法不能进入了,但它的其他非synchronized方法还是可以访问的

     1 public class TT implements Runnable {
     2 
     3      int b = 100;
     4 
     5      public synchronized void m1(){
     6             b = 10000;
     7             try {
     8                 Thread. sleep(5000);
     9                 System. out.println( "m1----b="+ b);
    10            } catch (InterruptedException e) {
    11                 e.printStackTrace();
    12            }
    13      }
    14 
    15      public void m2(){
    16            System. out.println( "m2====="+ b);
    17      }
    18 
    19      @Override
    20      public void run() {
    21            m1();
    22      }
    23 
    24      public static void main(String[] args) {
    25            TT tt = new TT();
    26            Thread t = new Thread(tt);
    27            t.start();
    28 
    29             try {
    30                 Thread. sleep(1000);
    31            } catch (InterruptedException e) {
    32                 e.printStackTrace();
    33            }
    34 
    35            tt.m2();
    36      }
    37 
    38 }

     

    锁对象是谁?

     * A:同步代码块的锁对象是谁呢?

     *     任意对象。

     

     * B:同步方法的格式及锁对象问题?

     *     把同步关键字加在方法上。

     *     同步方法是谁呢?

     *         this

     

     * C:静态方法及锁对象问题?

     *     静态方法的锁对象是谁呢?

     *     类的字节码文件对象。(反射会讲)

     

     

    线程安全的类

    StringBuffer sb = new StringBuffer();

    Vector<String> v = new Vector<String>();

    Hashtable<String, String> h = new Hashtable<String, String>();

    Vector是线程安全的时候才去考虑使用的,但是即使要安全,我也不用你  

    那么到底用谁呢?public static <T> List<T> synchronizedList(List<T> list)

    List<String> list1 = new ArrayList<String>();// 线程不安全

    List<String> list2 = Collections.synchronizedList(new ArrayList<String>()); // 线程安全

     

    synchornized

    synchronized是java中的一个关键字,也就是说是Java语言内置的特性。

    如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况:

      1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有;

      2)线程执行发生异常,此时JVM会让线程自动释放锁。

     

    原理示意图

    jdk1.5还提供了lock锁 后面再说

     

    转载于:https://www.cnblogs.com/wihainan/p/4760874.html

    展开全文
  • 程序运行时,内部可能包含了个顺序执行流,每个顺序执行流就是一个线程.一定的独立功能,进程是系统进行资源分配 和调度的一个独立单位. 进程的特点: 独立性: 进程是系统中独立存在的实体,它可以拥有自己独立的...

    一. 进程和线程

        几乎所有的操作系统都支持同时运行多个任务,一个任务通常就是一个程序,每个运行中的程序就是一个进程,当一个
     程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程.一定的独立功能,进程是系统进行资源分配
     和调度的一个独立单位.
    

    进程的特点:

    1. 独立性: 进程是系统中独立存在的实体,它可以拥有自己独立的资源,每一个进程都拥有自己私有的地址空间,在没有经过进程自己本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间.
    2. 动态性: 进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合,即进程中加入了时间概念.进程具有自己的声明周期和各种不同的状态,这些概念在程序中都是不具备的.
    3. 并发性: 多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响.

    线程的特点:

    1. 线程是进程的组成部分: 一个进程可以拥有多个线程,一个线程必须有一个父进程.线程可以拥有自己的堆栈,自己的程序计数器和自己的局部变量,单不拥有系统资源,它与父进程的其他线程共享该进程所拥有的全部资源
    2. 线程可以完成一定的任务: 线程可以与其他线程共享父进程中的共享变量及部分环境,相互之间协同来完成进程所要完成的任务.
    3. 一个线程可以创建和撤销另一个线程,同一个进程中的多个线程可以并发执行.

    多线程编程的几个优先:

    1. 进程之间不能共享内存,但同一个进程中的线程之间共享内存非常容易
    2. 系统创建进程时,需要为该进程重新分配系统资源,但创建线程则代价小的多,因此使用多线程来实现多任务并发比多进程的效率高
    3. Java语言内置了多线程功能支持,而不是单纯地作为地城操作系统的调度方式,从而简化了Java的多线程编程

    ###二. 并发编程
    并发编程使我们可以将程序划分为多个分离的,独立运行的任务.通过使用多线程机制,这些独立任务中的每一个都
    将由执行线程来驱动.而顺序编程,即程序中所有的事物在任意时刻都只能执行一个步骤.并发编程可以使程序执行的速
    度得到极大的提高,或者为设计某些类型的程序提供更易用的模型.

    • 并发的多面性: 并发编程令人困惑的一个主要原因是: 使用并发时需要解决的问题有多个,而实现并发方式也有多种,并且这两者之间没有明显的映射关系(而且通常只具有模糊的界限).因此,我们必须理解所有的这些问题和特例,以便有效的使用并发.

    • 用并解决的问题大体上可以分为"速度"和"设计可管理性"两种

    • 更快的执行: 如果你想要一个程序运行的更快,那么可以将其断开为多个片段,在单独的处理器上运行每个片段.并发是用于多处理器编程的基本工具.

    • 改进代码设计: 在单CPU机器上使用多任务的程序在任意时刻仍旧只在执行一项工作,因此从理论上讲,肯定可以不用任何任务而编写出相同的程序.但是,并发提供了一个重要的组织结构上的好处:你的程序设计可以极大的简化.某些类型的问题,例如仿真,没有并发的支持,是很难解决的.

    三. 线程的创建和启动

    通过继承Thread类来创建线程
    
    package com.laolang.thread;
    /**
     * 标题: Thread.java
     * 路径: com.laolang.thread
     * 描述: 通过继承Thread 类来创建线程类
     * 版本: @version V1.0
     */
    public class FirstThread extends Thread{
    	private int i;
    	//重写run()方法,run()方法的方法体就是线程执行体
    	public void run(){
    		for ( i = 0; i < 100; i++) {
    			//当线程类继承Thread时,直接时候this即可获取当前线程
    			//Thread对象的getName()返回当前线程的名字
    			//因此可以直接调用getName()方法返回当前线程的名字
    			System.out.println(getName()+"    "+i);
    		}
    	}
    	
    	public static void main(String[] args) {
    		for (int i = 0; i < 100; i++) {
    			//调用Thread的currentThread().getName()方法获取当前线程
    			System.out.println(Thread.currentThread().getName()+"  "+i);
    			//当i等于20时,创建并启动两个FirstThread线程
    			if(i==20){
    				//创建并启动第一个线程
    				new FirstThread().start();;
    				//创建并启动第二个线程
    				new FirstThread().start();
    			}
    		}
    	}
    }
    
    通过实现Runnable接口来创建线程,在这里我们可以把实现Runnable接口的来当做一个需要执行的任务,而new Thread()
    方法是将这个任务交个一个线程来管理.否则实现Runnable结构的这个类中的run()方法不会产生任何内在的线程的能力.想
    要实现线程行为,就必须显示的将它附着到线程上.
    
    package com.laolang.thread;
    /**
     * 标题: SecondThread.java
     * 路径: com.laolang.thread
     * 描述: 实现Runnable接口来创建并启动线程
     * 版本: @version V1.0
     */
    public class SecondThread implements Runnable {
    	private int i;
    	//run()方法同样是线程执行体
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		for (int i = 0; i < 100; i++) {
    			//当线程类事项Runnable接口时
    			//如果想获取当前线程,只能用Thread.currentThread()方法
    			System.out.println(Thread.currentThread().getName()+"  "+i);
    		}
    	}
    	
    	public static void main(String[] args) {
    		for (int i = 0; i < 100; i++) {
    			//调用Thread的currentThread().getName()方法获取当前线程
    			System.out.println(Thread.currentThread().getName()+"  "+i);
    			//当i等于20时,创建并启动两个FirstThread线程
    			if(i==20){
    				//创建并启动第一个线程
    				new Thread(new SecondThread(),"线程1").start();
    				//创建并启动第二个线程
    				new Thread(new SecondThread(),"线程2").start();
    			}
    		}
    	}
    
    }
    
    
    使用Executor执行器管理Thread对象,在下面示例中,CachedThreadPool将为每个任务都创建一个线程.注意,
    ExecutorService对象是使用静态的Executor方法创建的,这个方法可以确定其Executor的类型
    
    package com.laolang.thread;
    /**
     * 标题: ThirdThread.java
     * 路径: com.laolang.thread
     * 描述: 实现Runnable接口,并实现run()方法
     * 版本: @version V1.0
     */
    public class ThirdThread implements Runnable{
    	protected int countDown = 10;//默认
    	private static int taskCount = 0;
    	private final int id = taskCount++;
    	public ThirdThread() {
    		// TODO Auto-generated constructor stub
    	}
    	public ThirdThread(int countDown) {
    		// TODO Auto-generated constructor stub
    		this.countDown = countDown;
    	}
    	
    	public String status(){
    		return "#" + id + " ("+(countDown > 0 ? countDown : "ThirdThread") + "), ";
    	}
    	
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		while (countDown-- > 0 ) {
    			System.out.println(status());
    			/**
    			 * 对调度程序的提示,当前线程愿意放弃当前使用的处理器。调度器可以忽略这个提示。Yield是一种启发式的尝试,
    			 * 以改进在其他情况下会过度使用CPU的线程之间的相对进展。它的使用应该与详细的分析和基准测试相结合,以确保它实际上有预期的效果。
    			 * 使用这种方法是不合适的。它可能对调试或测试的目的很有用,在那里它可以帮助复制由于竞争条件而产生的错误。
    			 * 在设计类似java.util.concurrent中的并发控制结构时,它可能也很有用。锁方案。
    			 * 
    			 * 我们在这里使用,是为了看到线程之间相互切换的输出.
    			 */
    			Thread.yield();
    		}
    	}
    }
    
    
    package com.laolang.thread;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    
    /**
     * 标题: CachedThreadPool.java
     * 路径: com.laolang.thread
     * 描述: 使用Executor管理Thread
     * 		  使用Executor执行器管理Thread对象,在下面示例中,CachedThreadPool将为每个任务都创建一个线程.
     * 		  注意,ExecutorService对象是使用静态的Executor方法创建的,这个方法可以确定其Executor的类型
     * 版本: @version V1.0
     */
    public class CachedThreadPool {
    	public static void main(String[] args) {
    		ExecutorService exec = Executors.newCachedThreadPool();
    		for (int i = 0; i < 5; i++) {
    			exec.execute(new ThirdThread());
    		}
    		exec.shutdown();
    	}
    }
    
    
    Runnable是执行工作的独立任务,但是它不返回任何值,如果我们希望任务执行完之后可以返回一个值,那么我们可以实现
    Callable接口,Callable是一种具有类型参数的泛型,它的类型参数表示的从方法call()(而不是run())中返回的值,并
    且必须使用ExecutorService.submit()方法调用它
    
    package com.laolang.thread;
    
    import java.util.ArrayList;
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    
    public class FourthThread implements Callable{
    	private int id;
    	public  FourthThread(int id) {
    		// TODO Auto-generated constructor stub
    		this.id = id;
    	}
    	//相当于Runnable中的run()方法
    	@Override
    	public Object call() throws Exception {
    		// TODO Auto-generated method stub
    		return "result of FourthThread "+ id;
    	}
    	
    	public static void main(String[] args) {
    		//创建Executor对象来管理线程
    		ExecutorService exec = Executors.newCachedThreadPool();
    		//创建线程返回值的集合
    		ArrayList<Future<String>> results = new ArrayList<Future<String>>();
    		for (int i = 0; i < 10; i++) {
    			//submit()方法可以返回Future对象,exec.submit(new FourthThread(i))是将new FourthThread(i)对象中call()放到一个线程上执行
    			results.add( exec.submit(new FourthThread(i)));
    		}
    		//遍历results
    		for (Future<String> future : results) {
    			try {
    				System.out.println(future.get());//future.get()方法,得到线程执行的返回值
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			} catch (ExecutionException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}finally{
    				exec.shutdown();
    			}
    		}
    	}
    }
    
    
    展开全文
  • 设定一个 orderNum,每个线程执行结束之后,更新 orderNum,指明下一个要执行线程。并且唤醒所有的等待线程。 在每一个线程的开始,要 while 判断 orderNum 是否等于自己的要求值!!不是,...


    如何让10个线程按照顺序打印0123456789?

    答:

    1. 设定一个 orderNum,每个线程执行结束之后,更新 orderNum,指明下一个要执行的线程。并且唤醒所有的等待线程。

    2. 在每一个线程的开始,要 while 判断 orderNum 是否等于自己的要求值!!不是,则 wait,是则执行本线程。


    代码如下:

    1.创建一个锁对象类

    主要包括一个orderNum,用来指定此时输出到第几个数字了;MaxValue用来控制输出数字的最大值。

    /*
     * *   如何指定多个线程的执行顺序:  
     *     1、设置一个锁对象
     * */
    package com.threadDemo;
     
    public class LockObject {
    	int orderNum=0;
    	final static int MaxValue=9;
    	
    	public LockObject(int orderNum){
    		this.orderNum = orderNum;
    	}
     
    }
    

    2.创建一个线程类

    1. 传入一个锁对象;
    2. 定义每个线程自己打印的数字;
    3. 通过构造函数传入对象锁;
    4. run方法里面主要是通过四层判断来确定线程的执行顺序的。
    /*
     * 定义一个线程类
     * 思路:
     * 		1、通过公正琐object对10个线程进行管理,唤醒所有线程或者阻塞等待。
    		2、通过orderNum通知下一个线程需要输出的数字
    		就是通过线程内部定义一个变量:printNum来控制该线程打印属于自己的数字
     * */
    package com.threadDemo;
     
    public class MyThread1 extends Thread {
    	//1、定义需要传入的LockObject对象
    	private LockObject lobject;
    	//2、定义属于线程自己的打印数字
    	private int printNum =0;
    	
    	public MyThread1(LockObject lobject,int printNum){
    		this.lobject=lobject;
    		this.printNum = printNum;
    	}
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		//1、判断该资源是否被占用
    		synchronized(lobject){
    			//2、如果资源空闲,则判断是否已经打印完成
    			while(lobject.orderNum <= lobject.MaxValue){
    				//3、没有打印完则判断是否是自己需要打印的数字
    				if(lobject.orderNum == printNum){
    					System.out.print(printNum);
    					lobject.orderNum++;
    					if(lobject.orderNum==10){
    						System.out.println("线程打印完毕");
    					}
    					//打印完毕后,唤醒所有的线程
    					lobject.notifyAll();	
    				}else{
    					//4、不是该线程打印的数字,则继续等待
    					try {
    						lobject.wait();
    						//System.out.println("线程等待 "+printNum);
    					} catch (InterruptedException e) {
    						// TODO Auto-generated catch block
    						 System.out.println("线程" + printNum + "被打断了");
    						e.printStackTrace();
    					}
    				}
    			}
    		}
    	}
    }
    

    3.测试类

    创建一个对象锁以及10个线程对象,故意让线程从9开始执行,以测试线程是否从0开始顺序打印。

    package com.threadDemo;
    import java.util.ArrayList;
     
    public class ThreadTest {
    	public static void main(String[] args) {
    		//1、先创建一个LockObject对象
    		LockObject lobject = new LockObject(0);
    		//2、创建10个线程的数组
    		MyThread1[] mythread = new MyThread1[10];  
    		//初始化线程并且启动,
            //为了证明线程琐的作用,线程从后面开始启动
    		for(int i=0;i<10;i++){
    			mythread[i] = new MyThread1(lobject,9-i);
    			//启动线程  注意继承Thread类是调用start方法,实现Runable接口是调用run方法
    			mythread[i].start();
    		}
    	}
    }
    
    展开全文
  • 线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源表面上是多线程其实是cpu快速轮流切执行多线程(并行和并发)并行:两个任务同事进行,就是甲...
  • 提高响应速度:当任务到达时,任务可以不需要的等到线程创建就能立即执行。 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、...
  • 表面上是多线程其实是cpu快速轮流切执行 多线程(并行和并发) 并行:两个任务同事进行,就是甲任务执行的同时,乙任务也在执行(需要多核) 并发:两个任务都请求运行,而处理器只能接受一个任务,就把这两个任...
  • Java并发编程:如何创建线程  在前面一篇文章中已经讲述了在进程和线程的由来,今天就来讲一下在Java中如何创建线程,让线程执行一个子任务。下面先讲述一下Java中的应用程序和进程相关的概念知识,然后再阐述...
  • Java线程类也是一个object类,它的实例都继承自java.lang.Thread或其子类。可以用如下方式用java中创建一个线程: Tread thread = new Thread(); 执行线程可以调用该线程的start()方法: ...
  • 多线程并发

    2020-03-05 17:44:27
    在启动了一个线程创建了一个thread对象)之后,创建thread线程对象,传入线程函数和参数,线程直接启动运行。当这个线程结束的时候(std::terminate()),我们如何去回收线程所使用的资源呢?thread库给我们两种...
  • 并发指的是在一段时间内宏观上有个程序同时运行,这在单 CPU 系统中,每 一时刻只能有一道程序执行,即微观上这些程序是分时的交替运行,只不过是给人的感觉是同时运行,那是因为分时交替 运行的时间是非常短的...
  • 可以创建多线程,但是在这种模式下创建的多线程并不能将多核利用起来,所有由这种模式下创建的线程最多只能共享一个CPU核,所以在有些场景下,我们需要将一个作业分配给一个独立的线程,并且每个独立的线程可以使用...
  • Java并发编程:如何创建线程?  在前面一篇文章中已经讲述了在进程和线程的由来,今天就来讲一下在Java中如何创建线程,让线程执行一个子任务。下面先讲述一下Java中的应用程序和进程相关的概念知识,然后再阐述...
  • 在Java中,一个应用程序对应着一个JVM实例(也有地方称为JVM进程),一般来说名字默认为java.exe...但是要注意,虽然只有一个线程执行任务,不代表JVM中只有一个线程,JVM实例在创建的时候,同时会创建其他的线程
  • 在前两篇文章中,我们已经了解了关于线程创建与常用方法等相关知识。接下来就来了解下,当你运行线程时,线程如何调度的。关注我的公众号「Java面典」了解更 Java 相关知识点。任务系统往往需要同时执行多道...
  • 所有现代操作系统均支持并发执行任务。用户可以在听音乐或者浏览网页的同时阅读邮件。这种并发是进程级别的并发。在同一进程内,也可以有多种同时运行的子任务,我们将这些并发的子任务称为线程。与并发性有关的另一...
  • 多线程引用Wiki的解释—在计算机体系结构中,多线程是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程,进而提升整体处理性能。并发指的是可以实现...
  • 多线程并发基础

    2021-01-08 16:21:33
    多线程并发基础普通方法调用多线程三种创建方式继承thread类实现Runnable 接口实现Callable接口如何选择?初识并发问题静态代理模式多线程中的lambda表达式线程的状态停止线程sleep和wait的区别sleep()wait()yield...
  • Java 多线程并发

    2019-04-11 03:42:43
    线程并发执行提高程序的效率 CPU不会因为需要等待某个线程而空闲 线程分享内存 进程和线程之间有什么不同 进程是在系统内从中加载运行的程序,进程是一个进程内实际运作单元(线程属于进程) 进程内存独立,...
  • 如何创建一个线程

    千次阅读 2018-09-25 23:00:51
    我们都知道多线程可以并发执行任务,在某些情况下多线程或许不能提高执行效率,因为多线程虽然看上去是并发执行的,实际上还是CPU分配时间片,是有先后顺序的,只是时间片切换速度很快,以至于我们看上去是同时执行...
  • 多线程API - 小白如何快速上手并发编程 线程的基本概念 一.线程和进程的区别(linux环境下) 进程是资源管理的最小单位,进程是系统执行的最小单位 线程和进程都有对应的PCB和TCB,在linux内核中创建线程和进程...
  • 2、Java对多线程的基础操作:线程的状态扭转,线程的创建、终止、中断、等待和通知、挂起和执行、等待结束和谦让,synchronized实现原理及volatile和synchronized关键字在多线程环境下的作用及如何使用。 3、多线程...
  • 前面我们对并发有了一定的认识,并且知道如何创建线程,创建线程主要依靠的是Thread 的类来完成的,那么有什么缺陷呢?如何解决? 一、对比new Threadnew Thread的弊端 a. 每次new Thread新建对象性能差。 b. 线程...
  • 在上一章我们介绍了线程基础相关的内容,知道了什么是线程、线程存在的状态以及Java如何创建线程等内容,线程是程序执行的基本单元,任何程序都离不开线程,本章我们就来讲讲多线程的基础内容,什么是多线程呢?...
  • 日常开发中,我们面对的大多是多线程并发的场景,多线程的知识点也变得尤为重要。所以这一节我们对多线程的基本知识点做一个全面复习。 一、什么是线程,进程和线程的区别? 进程是资源分配的基本单位,是正在运行...
  • 所有现代操作系统均支持并发执行任务。用户可以在听音乐或者浏览网页的同时阅读邮件。这种并发是进程级别的并发。在同一进程内,也可以有多种同时运行的子任务,我们将这些并发的子任务称为线程。与并发性有关的另一...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 438
精华内容 175
关键字:

如何创建多线程并发执行