• java多线程基本概述(一)——线程的基本认知


    1.1、概念:

        进程:进程是操作系统结构的基础,是一次程序的执行;是一个程序及其数据再处理器上顺序执行时所发生的活动;是程序再一个数据集合上运行的过程,它是系统进行系统资源分配和调度的最小单元。

        线程:可以理解为一个程序的不同执行路径,是程序执行流的最小单元。线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪阻塞运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。

    1.2、使用多线程

        

     1 package soarhu;
     2 
     3 /**
     4  * Created by huaox on 2017/4/17.
     5  *
     6  */
     7 public class Test {
     8     public static void main(String[] args) {
     9         System.out.println(Thread.currentThread().getName());
    10     }
    11 }

    输出结果:main

    可以知道,在main方法所在的线程即是一个名叫main的线程。

    创建线程主要右三种方式:分别为继承Thread类,实现Runnable和Callable接口。

    1.2.1:继承Thread

        Thread在jdk文档中定义为: public class Thread implements Runnable 

       使用从Thread继承的最大局限性即是无法实现多继承,所以可以实现Runnable接口或者Callable接口。

     1 package soarhu;
     2 
     3 /**
     4  * Created by huaox on 2017/4/17.
     5  *
     6  */
     7 
     8 class ThreadTest extends Thread{
     9 
    10     @Override
    11     public void run() {
    12         System.out.println("ThreadTest run method: "+Thread.currentThread().getName());
    13     }
    14 }
    15 
    16 public class Test {
    17     public static void main(String[] args) {
    18         System.out.println(" main method : "+Thread.currentThread().getName());
    19         Thread thread = new ThreadTest();
    20         thread.start();
    21         System.out.println("end!!!");
    22     }
    23 }

       输出结果:

     main method : main

     end!!!
      ThreadTest run method: Thread-0

     从输出结果可以知道:线程之间的代码执行顺序是不可控制的或者说调用顺序是无关的。

    1.2.2:实现Runnaball接口

      

    package soarhu;
    
    /**
     * Created by huaox on 2017/4/17.
     *
     */
    
    class ThreadTest implements Runnable{
    
        @Override
        public void run() {
            System.out.println("ThreadTest run method: "+Thread.currentThread().getName());
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            System.out.println(" main method : "+Thread.currentThread().getName());
            Thread thread = new Thread(new ThreadTest());
            thread.start();
            System.out.println("end!!!");
        }
    }

    输出结果与上例一致。

    1.2.3:实现Callble接口

     Callable接口类似于Runnable,但是Runnable不会返回结果,并且无法抛出返回结果的异常,而Callable可以,可以返回值,这个返回值可以被Future拿到。例子如下

    package soarhu;
    
    import java.util.concurrent.*;
    
    /**
     * Created by huaox on 2017/4/17.
     *
     */
    
    class ThreadTest implements Callable<String>{
    
        @Override
        public String call() throws Exception {
            System.out.println("ThreadTest run method: "+Thread.currentThread().getName());
            try {
                TimeUnit.MILLISECONDS.sleep(3000);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            return "hello";
        }
    
    }
    
    public class Test {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
    
            // 第一种方式:
          /*
            ExecutorService executor = Executors.newCachedThreadPool();
            Future<String> future = executor.submit(new ThreadTest());
            System.out.println("result:" + future.get());
            executor.shutdown();*/
    
          //第二种方式:
            FutureTask<String> futureTask = new FutureTask<String>(new ThreadTest());
            Thread thread = new Thread(futureTask);
            thread.start();
            System.out.println("result:" + futureTask.get());
        }
    }

      输出结果:hello

    1.2.4、线程安全

        

    package soarhu;
    
    /**
     * Created by huaox on 2017/4/17.
     *
     */
    
    class ThreadTest implements Runnable{
    
        private int count = 5;
    
    
        @Override
        public void run() {
            while (count-->0)
                System.out.println(Thread.currentThread().getName()+" count:-> "+count);
        }
    
    
    }
    
    public class Test {
        public static void main(String[] args) {
            for (int i = 0; i < 3; i++) {
                new Thread(new ThreadTest(),i+"").start();
            }
        }
    }

    输出结果:

    0 count:-> 4
    2 count:-> 4
    1 count:-> 4
    1 count:-> 3
    1 count:-> 2
    1 count:-> 1
    1 count:-> 0
    2 count:-> 3
    2 count:-> 2
    2 count:-> 1
    2 count:-> 0
    0 count:-> 3
    0 count:-> 2
    0 count:-> 1
    0 count:-> 0

    程序中的count变量是一个成员变量,如果多个线程同时访问则会出现并发问题,但现在这个程序没有出现安全问题是因为:每个线程都访问自己的变量,互补相关。类似于每个人都卖自己的票。

    下面修改为多个线程共卖一张票的情况:

      

    package soarhu;
    
    /**
     * Created by huaox on 2017/4/17.
     *
     */
    
    class ThreadTest implements Runnable{
    
        private int count = 5;
    
    
        @Override
        public void run() {
            while (count-->0){
                Thread.yield();//让给调度器给其他线程执行的机会。此以方法一般用于debug
                System.out.println(Thread.currentThread().getName()+" count:-> "+count);
            }
        }
    
    
    }
    
    public class Test {
        public static void main(String[] args) {
            ThreadTest thread = new ThreadTest();
            for (int i = 0; i < 3; i++) {
                new Thread(thread,i+"").start();
            }
        }
    }

    输出结果:

    0 count:-> 4
    2 count:-> 2
    1 count:-> 2
    2 count:-> 0
    0 count:-> 1

    可见两个线程同时出现了count数为2的情况,也就是两个售货员分别卖出票后,所剩的余票是一样的,这是不允许的,即线程不安全的状况。之所以出现这种情况,是因为某些情况下,i++类似这种操作为非原子操作。可能分为3部分进行。1:获取i的值,2:i+1,3:赋值给i。在这三种情况中。如果有多个线程进行同时访问,那么就会出现安全问题。修改的方式有很多。例如设置为同步块,或者同步方法,显示用Lock锁对象等方式都可以避免。例如:

    package soarhu;
    
    /**
     * Created by huaox on 2017/4/17.
     *
     */
    
    class ThreadTest extends Thread{
    
        private int count = 5;
    
    
        @Override
        public  void run() {
            synchronized (ThreadTest.class) {
                if (count-->0) {    //此处应该改为if块,如果为while那么其他线程将得不到执行
                    Thread.yield();//让给调度器给其他线程执行的机会。此以方法一般用于debug
                    System.out.println(Thread.currentThread().getName() + " count:-> " + count);
                }
            }
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            ThreadTest thread = new ThreadTest();
            for (int i = 0; i < 5; i++) {
                new Thread(thread,i+"").start();
            }
        }
    }

     输出结果:

    0 count:-> 4
    1 count:-> 3
    4 count:-> 2
    3 count:-> 1
    2 count:-> 0

    可知现在结果输出一切正常!

    1.2.5、关于Thread.currentThread.getName()

    package soarhu;
    
    /**
     * Created by huaox on 2017/4/17.
     *
     */
    
    class ThreadTest extends Thread{
    
        private int count = 5;
    
        ThreadTest() {
            System.out.println("constructor method start");
            System.out.println("-----ThreadCurrent.getName(): "+Thread.currentThread().getName());
            System.out.println("------this.getName(): "+this.getName());
            System.out.println("constructor method end");
            System.out.println("==============================");
        }
    
        @Override
        public  void run() {
            System.out.println("run method start");
            System.out.println("-----ThreadCurrent.getName(): "+Thread.currentThread().getName());
            System.out.println("------this.getName(): "+this.getName());
            System.out.println("run method end");
            System.out.println("==============================");
    
        }
        
    }
    
    public class Test {
        public static void main(String[] args) {
            ThreadTest thread = new ThreadTest();
            thread.setName("a");
            thread.start();
        }
    }

    输出结果:

    constructor method start
    -----ThreadCurrent.getName(): main
    ------this.getName(): Thread-0
    constructor method end
    ==============================
    run method start
    -----ThreadCurrent.getName(): a
    ------this.getName(): a
    run method end
    ==============================

  • 相关阅读:
    Java 垃圾回收机制
    Android 图片旋转(使用Matrix.setRotate方法)
    antd design vue 设置 v-decorator 的初始值
    校验 url 是否以http 或者https 开头
    有效的括号
    堆排序
    归并排序
    插入排序
    选择排序
    冒泡排序
  • 原文地址:https://www.cnblogs.com/soar-hu/p/6721133.html
Copyright © 2020-2023  润新知