zoukankan      html  css  js  c++  java
  • 15、线程

     一、线程(Thread)——进程(process)

      线程是程序类的控制流,线程和进程非常相似。

      多进程:在操作系统能同时运行多个任务(程序、软件)

      多线程:在同一个应用程序中有多个顺序流任务同时执行。

    二、Thread类和Runnable接口

      Thread实现了Runnable接口

      在A1类下继承Thread类,重写run方法之后的单线程串行运行:

    public class A {
        public static void main(String[] args) {
            new A1().run();
            new A1().run();
        }
    }
    class A1 extends Thread{
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                System.out.println(i);
            }
        }
    }

    多线程方法:

    1、使用Thread类下的start()方法可以实现简单的多线程(创建两个对象),Thread.currentThread()指当前线程:sleep()方法可以使当前线程睡眠指定毫秒数后唤醒。

    public class A {
    public static void main(String[] args) {
    A1 a1=new A1();
    a1.setName("A");
    a1.start();
    A1 a2=new A1();
    a2.setName("B");
    a2.start();
    }
    }
    class A1 extends Thread{
    @Override
    public void run() {
    for(int i=0;i<1000;i++){
    try {
    sleep(5000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName()+i);
    }
    }
    }

    2、使用Runnable接口,向Thread构造器传入实现了Runnable接口的对象:

    public class A {
        public static void main(String[] args) {
           A2 a1=new A2();
           Thread t1=new Thread(a1);
           t1.setName("A");//如果不设置名字,将按照JDK给定的Thread1和Thread2来命名
           t1.start();
    
            A2 a2=new A2();
            Thread t2=new Thread(a2);
            t2.setName("B");
            t2.start();
        }
    }
    
    class A2 implements Runnable{
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                System.out.println(Thread.currentThread().getName()+i);
            }
        }
    }

    3、也可以使用匿名内部类:(即用即调)

    public class B {
        public static void main(String[] args) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 100; i++) {
                        System.out.println(Thread.currentThread().getName() + i);
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 100; i++) {
                        System.out.println(Thread.currentThread().getName() + i);
                    }
                }
            }).start();
        }
    }

    4、使用FutureTask类,因为FutureTask类实现了Runnable接口,所以,间接地实现了Runnable接口。使用回调式接口,可以返回值。

      FutureTask类实现Runnable接口,而FutureTask构造器中需要的是Callable接口对应的对象,所以创建Callable接口对应的对象,传递给FutrueTask,创建一个FutrueTask对象,然后将这个实现了Runnable接口的对象传给Thread类的构造器,实现线程创建。

    public class C {
        public static void main(String[] args) {
            C1 c1=new C1();
            FutureTask<Integer> task=new FutureTask(c1);
            Thread t=new Thread(task);
            t.start();
            try {
                System.out.println("总计是"+task.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
    
            C1 c2=new C1();
            FutureTask<Integer> task2=new FutureTask(c2);
            Thread t2=new Thread(task2);
            t2.start();
            try {
                System.out.println("总计是"+task.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
    
    class C1 implements Callable<Integer>{
        @Override
        public Integer call() throws Exception {
            int sum=0;
            for(int i=0;i<=100;i++){
                sum+=i;
            }
            return sum;
        }
    }

    注意:1、start方法不要重复使用,一旦线程start,必须执行完或中止才会执行下一个start

       2、stop方法可以使线程急刹车。

    三、三种基本创建线程的区别

      --、使用Runable接口

        可以将线程代码和线程数据分开,形成清晰的模型。

        可以继承其他类。

      --、直接继承Thread类

        不能再从其他类继承;

        编写简单,可以直接操纵线程。

      1、使用Callable接口规定的方法是call(),而Runnable规定的方法是run()。

      2、Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。

      3、call()方法可抛出异常,而run()方法是不能抛出异常的。

      4、运行Callable任务可拿到一个Future对象,Future表示异步计算的结果。

        它提供了检查计算时候完成的方法,以等待计算的完成,并检索计算的结果。

        通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。

        Thread类构造器中填入的是Runnable接口实现类对象;FutureTask类构造器中填入的是Callable接口实现类对象。而FutureTask类实现了Runnable接口,这样就可以间接地使用Thread构造器调用FutureTask类的对象创建线程了。

        

      四、线程池创建线程

      通过Executor来启动线程比用Thread的start()要好。在新特性中,可以很容易控制线程的启动、执行和关闭过程,还可以很容易使用线程池的特性。

      1)、减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

      2)、可以根据系统的承受能力,调整线程池中工作线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开越多,消耗的内存越大,最后死机)

      Executors下的方法:newFixedThreadPool方法是创建n个线程的线程池

                newSingleThreadExecutor方法是创建单个线程

                newCacheThreadPool()方法是缓存线程池,以最快速度创建线程池,不管创建多少内存。

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    /**
     * Created by Administrator on 2018/2/10 0010.
     */
    public class D {
        public static void main(String[] args) {
            ExecutorService service= Executors.newFixedThreadPool(8);
            for(int i=0;i<100;i++){
                service.execute(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(Thread.currentThread().getName()+":"+"启动线程");
                    }
                });
            }
            service.shutdown();
            try {
                service.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
                System.out.println("线程结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    }

    未来式、Callable接口、线程池混合编写多线程

    线程池下的两个方法:

    submit方法形参是callable回调式接口对象,返回未来式(下面的例子)

    execute方法的形参是Runnable接口对象(上面的例子)

    import java.util.concurrent.*;
    /**
     * Created by Administrator on 2018/2/10 0010.
     */
    public class E {
        public static void main(String[] args) {
            ExecutorService service= Executors.newFixedThreadPool(10);
            for(int i=0;i<20;i++){
               Future<String> futrue= service.submit(new E1(i));
                try {
                    System.out.println(futrue.get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
            service.shutdown();
            try {
                service.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("结束");
        }
    }
    class E1 implements Callable<String>{
        public E1(){}
        private int id;
        public E1(int i){
            this.id=i;
        }
        @Override
        public String call() throws Exception {
            return  Thread.currentThread().getName()+"你好"+id;
        }
    }

    五、线程状态

                        状态图

      线程可以一直执行下去,直到下面的某件事情发生才停止。

      -明确地从目标run()方法返回,run方法结束。

      -遇到一个无法捕获的运行时异常。

      -调用了不推荐使用的stop()、Interrupt()方法。

      如果上面事情都没发生,run()方法就会一直运行下去,甚至在创建他的程序结束之后,线程还会一直运行。

      线程优先级设立:

      Runnable下对象使用setPriority方法,其中包含三个等级的默认优先级:MAX(10)、NORMAL、MIN(1)

      可以通过yield()方法明确地让出当前线程的控制权,通过完成他的目标方法或调用stop()方法终止线程。

     

    六、join线程

      join线程:Thread提供了让一个线程等待另一个线程完成的方法:join()方法,当在某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到join方法加入的join线程完成为止。

      该线程可以实现将一个大问题划分为若干小线程逐击破的想法。程序可以由三个线程组成:主线程、新线程、join线程,开始时候主线程和新线程交叉允许,当主线程的循环变量i等于20时,启动了join线程,该线程不会和主线交叉运行,而主线程必须等到该线程结束后才可以向下执行,但是由于没有控制新线程,所以新线程将还和join线程一同并发执行,而仅仅是主线程处于等待状态。

    package com.zxc.Q;
    
    /**
     * Created by Administrator on 2018/2/10 0010.
     */
    public class G {
        public static void main(String[] args) {
            G1 g1=new G1();
            g1.setName("G1:");
            g1.start();
        }
    }
    class G1 extends Thread{
        public void run() {
            for(int i=0;i<200;i++){
                if(i==50){
                    G2 g2=new G2();
                    g2.setName("G2:");
                    g2.start();
                    try {
                        g2.join();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName()+i);
            }
        }
    }
    class G2 extends Thread{
        public void run() {
            for(int i=0;i<200;i++){
                System.out.println(Thread.currentThread().getName()+i);
            }
        }
    }

    七、守护线程

      在多数情况下,实际上先创建的是一个能够在应用程序中做一些简单的、周期性任务的后台线程。可以调用setDaemon()方法标记一个线程是守护线程。

      下面的例子是h1对象为守护线程,守护h2,h3线程。

    public class H {
        public static void main(String[] args) {
            H1 h1=new H1();
            h1.setName("H1");
            h1.setDaemon(true);
            h1.start();
    
            H2 h2=new H2();
            h2.setName("H2");
            h2.start();
    
            H2 h3=new H2();
            h3.setName("H3");
            h3.start();
        }
    }
    
    class H1 extends Thread{
        @Override
        public void run() {
            for(int i=0;i<1000;i++){
                try {
                    sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
    class H2 extends Thread{
        @Override
        public void run() {
            for(int i=0;i<100;i++){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }

    七、总结方法

      Runnable  Thread  start stop interrupt   sleep  join   

      Executorservice   Exectors  submit   Callable  executor  shutdown

      awaitTermination

      newFixedThreadPool  newSingleThreadPool   newCacheThreadPool   FutureTask Future

      yield  setPriority  notify notifyall  wait

      中断异常的三个:wait sleep interrupt

    八、Sychronized同步(效率低)

    银行取钱问题:******用Sychronized来锁定方法并不安全,最好用来锁定对象线程。

    //Account账户类
    public class Account
    {
        private String accountNo;
        private double balance;
    
    
        public Account(){}
    
        public Account(String accountNo , double balance)
        {
            this.accountNo = accountNo;
            this.balance = balance;
        }
    
        public void setAccountNo(String accountNo)
        {
            this.accountNo = accountNo;
        }
        public String getAccountNo()
        {
            return this.accountNo;
        }
    
        public void setBalance(double balance)
        {
            this.balance = balance;
        }
        public double getBalance()
        {
            return this.balance;
        }
    }
    
    //DrawAccount 存款类
    public class DrawAccount extends Thread{
        private Account account;
        private  double drawAmount;
        public DrawAccount(String name,Account account,double drawAmount){
            super(name);
            this.account=account;
            this.drawAmount=drawAmount;
        }
    
        @Override
        public void run() {
            synchronized (this.account) {   //给this.account上锁
                if (account.getBalance() >= drawAmount) {
                    account.setBalance(account.getBalance() - this.drawAmount);
                    System.out.println(this.getName() + "取了" + this.drawAmount + "你的余额是:" + account.getBalance());
                } else {
                    System.out.println("余额不足");
                }
            }
        }
    }
    
    
    //TestDraw测试类
    public class TestDraw {
        public static void main(String[] args) {
            Account acct=new Account("NO123456789",800);
            new DrawAccount("甲",acct,800).start();
            new DrawAccount("乙",acct,800).start();
        }
    }

    九、synchronized和volatile和Lock区别

        volatile关键字:保证内存的可见性

                在每次访问变量时都会进行一次刷新,因此每次访问都是主内存中最新版本。所以volatile作用之一是保证变量修改的实时可见性。

               并不保证原子性

               防止指令重排

      sychronized比volatile更安全,而后者比前者效率高,后者是轻量级的同步机制,前者为重量级的同步机制。有了sychronized就不需要用voaltile了。前者可以保证原子性和可见性,而后者不能保障原子性,从而就不能保证完全锁定。用volatile修饰的变量不能再更改:count++、count+=1等都不能;

      论锁定功效:Lock>sychronized>volatile

      Lock:

    lock和Synchronized的区别:

  • 相关阅读:
    js中this应用
    易语言学习
    哈希表和字典List和Ilist和array和arraylist的应用
    ExtJs中decode与encode(转载)
    ajax几种请求几种类型
    关于Json
    简单属性margin和padding
    ==和===的区别
    maven实战读书笔记(三)
    maven实战读书笔记(二)
  • 原文地址:https://www.cnblogs.com/television/p/8438174.html
Copyright © 2011-2022 走看看