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的区别:

 

posted @ 2018-02-10 10:11  彩电  阅读(185)  评论(0编辑  收藏  举报