复习 J2SE基本内容 线程 Thread

目录:

  • 线程的四种开法 (三种方法的异同)

  • 线程状态

  • join线程 

  • 守护线程

  • 线程同步sychronized 

  • volatile关键字

  •  sychronized与volatile的区别

  • 线程的通讯Communication

  • Lock

  • sychronized与lock的区别

  • 线程的方法


线程的四种开法

  Thread类  Runnable类  Callable类  线程池


 Thread类

  • 不能再从其他类继承
  •  编写简单可直接操控线程
class  A0 extends  Thread
{
    @Override
            public  void run()
        {
            for(int  i = 0 ;  i <= 10 ; i++)
            {
                System.out.println(Thread.currentThread().getName()+"  " + i);
        }
        }
 public static void main(String[] args) {
        A0 a0 = new A0() ;
        a0.start();

Runnable类 (更有优势)

  •  可以将线程代码与线程数据分开形成清晰的模型
  •  可以继承其他类
class  B0 implements Runnable
{

    @Override
    public void run() {
        for (int i = 1; i <=10 ; i++ )
        {
            System.out.println(Thread.currentThread().getName()+"   " + i);
        }

    }
}
  public static void main(String[] args) {
        B0 b0 = new B0();
       Thread t = new Thread(b0);
       t.start();

Callable类

 

class  C0 implements Callable<Integer>
{

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int  i = 0 ; i <= 100 ; i ++ )
        {
            sum = sum + i ;
            System.out.println(Thread.currentThread().getName()+"   " + i);
        }
        return sum;
    }
}

 

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        C0 c0 = new C0() ;
        FutureTask<Integer> futureTask = new FutureTask(c0);
        new Thread(futureTask).start();

        C0 c1 = new C0() ;
        FutureTask<Integer> futureTask1 = new FutureTask(c1);
        new Thread(futureTask1).start();


        System.out.println(futureTask.get());
        System.out.println(futureTask1.get());
    }
}

三种方法的异同:

  • Callable()规定的方法是call() Runnable()规定的方法是run()
  • Callable()执行方法后可返回值   Runnable()执行方法后不能返回值
  •  call()方法可以抛出异常   run()方法不能抛出异常
  • 实现Callable() 或者Runnable()的类都是可以被其他其他线程执行的任务
  • 运行Callable任务可以得到一个future对象 future表示异步计算的结果 提供了检查计算是否完成的方法 以等待计算完成 并检索计算结果

 

总结:

  1. 新的线程都保持空闲状态直到start()将其唤醒 在一个线程的生命周期中 start()只能被调用一次

  2. 可以使用stop()来永久结束线程 但是不推荐

  3. JDK推荐使用Interrupt来中断线程 但是是有条件的 对线程的Interrupt是在sleep ,wait ,join ,这些状态时才起作用 这些状态下会一直检查是否中断 如果中断会抛出异常来结束进程 

  4. InterruptException 不是Interrupt ()抛出的

 

线程池创建线程

更容易执行线程的启动 执行 关闭  也可以很容易的使用线程池的特性

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

可以根据系统的承受能力 来调整线程池内的线程 防止因为消耗过多内存导致系统瘫痪(每个线程大约需要4MB)

public class D {
    public static void main(String[] args) {
        ExecutorService ex = Executors.newSingleThreadExecutor();
     //   ExecutorService ex = Executors.newCachedThreadPool(); 最大化 自动开
      //  ExecutorService ex = Executors.newFixedThreadPool(5); 限制大小
        for (int i = 0 ; i <= 10 ; i++ )
        {
            ex.execute(new D0());
        }

    }

}
class D0 implements  Runnable {


    @Override
    public void run() {
        for (int i = 0 ; i <= 100 ; i++ )
        {
            System.out.println(Thread.currentThread().getName()+"  "+i);
        }
    }
}

 


线程状态

线程的生命周期与状态

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

  • 明确的从目标run()方法返回 run方法结束了
  • 遇到一个无法捕获的运行时异常
  • 调用了不推荐使用的stop()Interrupt()方法

    如果上述内容没有发生 run()就会一直执行下去

调度与优先级

协同式线程调度
协同式线程调度,**线程的执行时间由线程本身控制。**协同式线程调度,线程执行时间由线程本身来控制,线程把自己的工作执行完之后,要主动通知系统切换到另外一个线程上。

优点: 实现简单,且切换操作对线程自己是可知的,没啥线程同步问题。
缺点: 线程执行时间不可控制,如果一个线程有问题,可能一直阻塞在那里。

 抢占式线程调度

抢占式调度,每个线程将由系统来分配执行时间,线程的切换不由线程本身来决定

Java中,Thread.yield()可以让出执行时间,但无法获取执行时间。

优点: 线程执行时间系统可控,也不会有一个线程导致整个进程阻塞。

 

       Java提供一个线程调度器来监视和控制Runnable状态的线程。线程的调度策略采用抢占式,优先级高的线程比优先级低的线程优先执行。在优先级相同的情况下,按照“先到先服务”的原则。

      每个Java程序都有一个默认的主线程,就是通过JVM启动的第一个线程。对于应用程序,主线程执行的是main()方法。对于Applet主线程是指浏览器加载并执行小应用程序的那一个线程。数值越大优先级越高


join线程

         join线程执行后把后面所有线程和唤醒他的线程都阻塞 直到join线程完成后 自动唤醒其他线程 其他线程自动争抢

 F0 f0 = new F0();
                f0.setName("A");
                f0.start();
                try {
                    f0.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+"  " + i);

守护线程

守护其他所有线程 直到其他线程结束 自己默默执行完毕

创建一个能在应用程序中做一些简单的 周期性任务的后台程序 可以调用setDaemon()标记一个线程是守护线程

用户线程和守护线程的区别:

     用户线程:java虚拟机在他所有的用户线程都离开后 java虚拟机才离开

     守护线程:守护线程是用来服务用户线程的 如果没有其他用户线程在运行 那么就没有客服务对象  也就没有理由继续下去 守护线程会自动离开  一旦系统中都剩下守护线程 并不影响JVM的自动离开


线程同步

     线程安全问题 银行取钱问题

   private  String name ;
    private  volatile Account account ;
    private  int money ;
    public GetMoney(String name ,Account account ,int money)
    {
        this.name = name ;
        this.account = account ;
        this.money = money ;
    }
    @Override
    public void run()
    {
        synchronized(this.account){
        if (this.money <= this.account.getFund() )
        {
            this.account.setFund(this.account.getFund()-this.money);
            System.out.println(this.name +"取出"+ "  "+ this.money);
        }else
            {
                System.out.println("账户余额不足");
            }
    }

public class Account {
    private int fund;
    private String no;

    public Account(int fund, String no) {
        this.fund = fund;
        this.no = no;
    }
    public  Account(){}

    public int getFund() {
        return fund;
    }

    public String getNo() {
        return no;
    }

    public void setFund(int fund) {
        this.fund = fund;
    }

    public void setNo(String no) {
        this.no = no;
    }
}




    public static void main(String[] args) {
        Account a = new Account(800,"1");
        GetMoney g1 = new GetMoney("A",a,800);
        GetMoney g2 = new GetMoney("B",a,800);
        GetMoney g3 = new GetMoney("C",a,800);
        g1.start();
        g2.start();
        g3.start();

volatile关键字

  保证内存的可见性  每次访问变量时都会进行一次刷新 因此每次访问都是主存中的最新版本

  防止指令重排  不保证原子性

当满足以下条件后才使用volatile:

该变量没有包含在具有其他变量的不变式中

对变量的写入操作不依赖变量当前值

由于使用volatile屏蔽掉了JVM中必要的代码优化 所以在效率上比较低

在两个或者更多线程访问成员变量上使用volatile 当要访问的变量已经在sychronized代码块中的时候 或者是为常量时 没必要再写volatile

sychronized与volatile的区别:

  • volatile仅能使用在变量级别      sychronized可以使用在变量 方法级别
  • volatile仅能实现变量的修改可见性  sychronized 则可以保证变量修改的可见性和原子性
  • volatile本质是告诉JVM当前变量在寄存器中的值是不确定的  需要从主存中读取   sychronized则是锁定当前变量 只有当前线程可以访问当前变量 其他线程被阻塞住
  • volatile不会造成线程阻塞   sychronized可能造成线程阻塞
  • 当一个域的值依赖于之前的值 volatile就无法工作 如i++等 如果某个域的值被其他域值限制那么volatile也无法工作
  • 使用volatile而不使用 sychronized的唯一安全情况就是类中只有一个可变的域
  • volatile不会进行加锁
  • sychronized比volatile更安全
  • volatile无法同时保证内存可见性和原则性

线程的通讯Communication

方法涉及到wait() notify()notifyAll()

  public  void saveMoney(int money){
        synchronized (this){
        if(flag)//true:不能存,只能取
        {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }else
            {
                this.fund += money ;
                System.out.println("存"+ money+ "余额"+this.fund);
                flag = true ;
                notifyAll();
            }
    }

 

 


Lock

    ReentrantLock有嗅探锁定 多路分支等功能

嗅探锁定  是指在获取锁得时候如果被其他线程获取到 ReentrantLock可以进行指定等待时间获取锁

多路分支通知 是指线程发生wait时 线程可以选择注册在不同的监测器Condition对象上  在适当的时候可以指定选择监测器Condition对象上的线程进行signal通知 

sychronized与lock的区别

  • sychronized(隐式锁):在需要同步的对象中加入此控制  sychronized可以加在方法上 也可以加在特定代码块中 括号中表示需要锁的对象  
  • lock(显示锁):一般使用ReentrantLock类作为锁  需要显示锁定起始位置和终止位置  多个线程中必须使用 ReentrantLock 类作为对象才能保证锁的生效   在加锁和解锁处要通过lock()和unlock()指出   一般会在finally()块中写unlock()防止死锁
  • sychronized是托管给JVM执行 而lock是java写的控制锁的代码
  • sychronized采用的是CPU悲观锁机制 即为线程获得独占锁 独占锁意味着其他线程只能依靠阻塞来等待线程释放锁
  • lock采用的是乐观锁   乐观锁是每次不加锁  而是假设每次没有冲突去完成某项操作 如果因为冲突失败就重试 直到成功为止 乐观锁的实现机制是CAS操作

 

 

 

 

 

 

 

 


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值