zoukankan      html  css  js  c++  java
  • JAVA基础多线程


    一、线程与进程   

        进程:正在计算机中运行的一个程序,当一个程序进入内存运行,即变成一个进程。一个软件至少有一个进程,
    有的软件在点击一个应用图标是可能会给你开多个进程,如:360全家桶。
        线程:软件在计算机中执行的一条路径。软件可以是多线程的。
        总结:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。而多线程就是一个程序中有多个线程
    在同时执行。而JAVA为我们提供了2种方式实现多线程:1、继承Thread类 2、实现Runnable接口。我们在编写程
    序时的主方法就是一个单独的线程,线程名叫 "main"。而JAVA虚拟机Jvm运行是多线程运行。

    二、Thread类

        JAVA实现多线程其中的一种方法就是继承Thread类,Thread是程序中的执行线程。Java 虚拟机允许应用程序并
    发地运行多个执行线程。Thread类常用的构造方法有:1、分配新的 Thread 对象 - Thread(),2、分配新的Thread
    对象,将指定的name作为线程名称 - Thread(String name)。其常用的方法有:
    1、改变线程名称,使之与参数 name 相同:setName(String name)
    2、该线程要执行的操作:run()
    3、在指定的毫秒数内让当前正在执行的线程休眠(暂停执行):sleep()
    4、使该线程开始执行;Java 虚拟机调用该线程的 run 方法:start()
    5、返回该线程的名称:getName()
    6、静态方法,返回值为Thread对象 - 返回对当前正在执行的线程对象的引用:currentThread()
        A:创建线程的步骤:
    1.定义一个类继承Thread。
    2.重写run方法。
    3.创建子类对象(创建线程对象)。
    4.调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法。
      举例:

     1 //定义类MyThread继承Thread类
     2 public class MyThread extends Thread {
     3 @Override
     4 // 重写Thread类里的run方法
     5 public void run() {
     6 // 想要多线程执行的代码
     7 for (int i = 0; i < 100; i++) {
     8 // 获取线程名称和循环的次数
     9 System.out.println(getName() + " " + i);
    10 }
    11 }
    12 }

    主函数:

     1 public static void main(String[] args) {
     2 // 创建线程一
     3 MyThread myThread01 = new MyThread();
     4 // 为线程一设置线程名
     5 myThread01.setName("线程一");
     6 // 开启线程一
     7 myThread01.start();
     8 // 创建线程二
     9 MyThread myThread02 = new MyThread();
    10 // 为线程二设置线程名
    11 myThread02.setName("线程二");
    12 // 开启线程二
    13 myThread02.start();
    14 }

    三、Runnable接口

        JAVA实现多线程的另一种方法就是实现Runnable接口,实现 run 方法。然后创建Runnable的子类对象,
    传入到某个线程的构造方法中,开启线程。Runnable接口其实就是用来指定每个线程要执行的任务,在由线
    程类Thread来执行这些任务。Runnable接口只有一个要实现的方法:run(),作用与Thread类的run方法类似,
    在启动该线程时将被调用,也就是说将由其实现类来实现run方法。Runnable接口的常用实现类是Thread类。
        B:创建线程的步骤:
    1、定义类实现Runnable接口。
    2、覆盖接口中的run方法。
    3、创建Thread类的对象
    4、将Runnable接口的子类对象作为参数传递给Thread类的构造函数。
    5、调用Thread类的start方法开启线程。
      举例:

     1 //创建MyRunable类实现Runnable接口
     2 public class MyRunable implements Runnable {
     3 @Override
     4 //重写run方法
     5 public void run() {
     6 /*循环
     7 Thread.currentThread().getName:
     8 通过Thread类的静态方法currentThread()方法,返回正在执行的线程对象,并获取线程名*/
     9 for (int i = 0; i < 100; i++) {
    10 System.out.println(Thread.currentThread().getName() + " :" + i);
    11 }
    12 }
    13 }

      *主方法:

     1 public static void main(String[] args) {
     2 // 获取MyRunable对象(线程任务)
     3 MyRunable r = new MyRunable();
     4 // 根据线程任务创建线程一
     5 Thread t1 = new Thread(r);
     6 // 命名
     7 t1.setName("线程一");
     8 // 开启线程
     9 t1.start();
    10 // 根据线程任务创建线程二
    11 Thread t2 = new Thread(r);
    12 t2.setName("线程二");
    13 t2.start();
    14 }

    四、多线程安全

        由于CUP对线程的处理具有随机性,这就导致了当线程1进入某一循环,还没有执行循环里的语句时,
    CUP突然切换到线程2,线程2也进入了循环语句。这样就会导致本应该只执行一次的条件执行了2次,导致
    线程不安全的问题出现。
        问题出现的原因:
    * 要有多个线程
    * 要有被多个线程所共享的数据
    * 多个线程并发的访问共享的数据。
      用实现Runnable接口的方式对同一数据进行

    操作可能就会出现这种重复的问题:

    (一)、多线程安全的解决

        问题出现了该如何解决呢?多线程处理的机制其实和我们在火车上厕所类似。在火车上有好多人都要去厕所,
    但是厕所只能供一个人使用,2个人如果一起挤进了厕所就会发生一些“事情”,那么火车是怎么解决这个问题的呢?
    我们可以在厕所的门上上一把锁,路人甲进厕所后把门锁起来,方便完之后再把锁打开,路人乙在进去把门锁上。
    而JAVA为了解决多线程的并发安全问题,也给我们提供了这么一把“锁” - 同步代码块和同步方法。

    1、同步代码块:

    synchronized(锁对象){

    }
      举例:

     1 public class MyRunable implements Runnable {
     2 // 设置一个被操作的数据
     3 int i = 100;
     4 
     5 @Override
     6 public void run() {
     7 while (true) {
     8 // 设置同步代码块
     9 synchronized (MyRunable.class) {
    10 try {
    11 // 为了不要太快,先睡一会
    12 Thread.sleep(200);
    13 } catch (InterruptedException e) {
    14 // TODO Auto-generated catch block
    15 e.printStackTrace();
    16 }
    17 // 对数据进行操作
    18 if (i > 0) {
    19 System.out.println(Thread.currentThread().getName() + " " + i--);
    20 }
    21 }
    22 }
    23 }
    24 }

    *入口方法:

     1 public static void main(String[] args) {
     2 MyRunable runable = new MyRunable();
     3 Thread t1 = new Thread(runable);
     4 t1.setName("线程A");
     5 Thread t2 = new Thread(runable);
     6 t2.setName("线程B");
     7 Thread t3 = new Thread(runable);
     8 t3.setName("线程C");
     9 t1.start();
    10 t2.start();
    11 t3.start();
    12 }

        锁对象:可以是java中的任意对象。多个同步代码块必须使用同一个对象,才能实现多个代码块之间的同步。一
    般使用当前类的.class对象

    2、同步方法:

    非静态方法:this
    静态方法:字节码对象(类名.class)
      举例:

     1 public class MyRunable implements Runnable {
     2 // 设置一个被操作的数据
     3 int i = 100;
     4 
     5 public void run() {
     6 while (true) {
     7 method();
     8 }
     9 }
    10 
    11 private synchronized void method() {
    12 
    13 try {
    14 Thread.sleep(150);
    15 } catch (InterruptedException e) {
    16 // TODO Auto-generated catch block
    17 e.printStackTrace();
    18 }
    19 if (i > 0) {
    20 System.out.println(Thread.currentThread().getName() + " :" + i);
    21 i--;
    22 }else {
    23 System.exit(0);
    24 }
    25 }

      *入口方法:

     1 public static void main(String[] args) {
     2 MyRunable runable = new MyRunable();
     3 Thread t1 = new Thread(runable);
     4 t1.setName("线程A");
     5 Thread t2 = new Thread(runable);
     6 t2.setName("线程B");
     7 Thread t3 = new Thread(runable);
     8 t3.setName("线程C");
     9 t1.start();
    10 t2.start();
    11 t3.start();
    12 }

    3、优缺点

    * 同步:安全性高,效率低。
    * 非同步:效率高,但是安全性低。

    五、使用多线程对同一数据进行不同的操作

      代码:

     1 public class Demo {
     2     public static class AddSub {
     3         //提供原子操作的Integer的类(无锁的线程安全整数 AtomicInteger)
     4         AtomicInteger at = new AtomicInteger(1);
     5         int j = 1;
     6 
     7         public static void main(String[] args) {
     8             AddSub ab = new AddSub();
     9             Add a = ab.new Add();
    10             Sub b = ab.new Sub();
    11             Thread t1 = new Thread(a);
    12             Thread t2 = new Thread(a);
    13             Thread t3 = new Thread(b);
    14             Thread t4 = new Thread(b);
    15             t1.start();
    16             t2.start();
    17             t3.start();
    18             t4.start();
    19         }
    20 
    21         public synchronized void add() {
    22             j++;
    23             System.out.println("add:" + j);
    24         }
    25 
    26         public synchronized void sub() {
    27             j--;
    28             System.out.println("sub:" + j);
    29         }
    30 
    31         class Add implements Runnable {
    32             @Override
    33             public void run() {
    34                 for (int i = 0; i < 20; i++) {
    35                     add();
    36                 }
    37 
    38             }
    39         }
    40 
    41         class Sub implements Runnable {
    42             @Override
    43             public void run() {
    44                 for (int i = 0; i < 20; i++) {
    45                     sub();
    46                 }
    47 
    48             }
    49         }
    50     }
    51 }
    我们在键盘上留下的余温,也将随时代传递到更远的将来~
  • 相关阅读:
    客户机(单线程 和多线程都可以用 主要是看服务器是单线程的还是多线程的)
    获得URl信息
    获取地址
    定时器的使用以及日期的学习
    生产者和消费者
    线程join
    线程的协作
    文件的解压与压缩
    文件的分割与合并
    以各种方式读写文件
  • 原文地址:https://www.cnblogs.com/0813lichenyu/p/7669017.html
Copyright © 2011-2022 走看看