1.两种方式的差异
2.线程的生命周期
3.线程控制(线程的方法)
4.线程同步
5.线程同步锁
一、 两种方式的差异
A extends Thread :简单
不能再继承其他类了(Java单继承)同份资源不共享
B implements Runnable:( 推荐) )多个线程共享一个目标资源,适合多线程处理同一份资源。
该类还可以继承其他类,也可以实现其他接口。
二、 线程的生命周期
新建:当程序使用new创建一个线程后,该线程处于新建状态,此时他和其他java对象一样,仅仅由Java虚拟机为其分配内存并初始化成员变量值。【 Thread r = new Thread()】
就绪:当线程对象调用start()方法后,该线程处于就绪状态,线程计入线程队列排队,此时该状态线程并未开始执行,它仅表示可以运行了。至于该线程何时运行,取决于JVM线程调度器的调度。【 r.start() 】
运行:若处于就绪状态的线程获得了CPU,开始执行run()线程执行体,该线程处于执行状态。
阻塞:线程运行过程中需要被中断,目的是是其他的线程获得执行的机会。该状态就会进入阻塞状态。
注意:阻塞状态不能直接转成运行状态,阻塞状态只能重新进入就绪状态。
死亡:run()执行完成,线程正常结束;
线程抛出未捕获的Exception或Error;
调用线程的stop()。(易导致死锁,不推荐)
注意:
主线程结束后,其他线程不受其影响,不会随之结束;一旦子线程启动起来后,就拥有和主线程相等地位,不受主线程影响。
例子:
class MyThread implements Runnable {
  public void run() {
    for (int i = 0; i < 200;i++) {
      System.out.println(i);
    }
  }
}
public class MyThreadDemo {
  public static void main(String[] args) {
    for (int i = 0; i < 10;i++) {
      if(i == 5){
        MyThread t = new MyThread();
        new Thread(t).start();
      }
      System.out.println("main" + i);
    }
  }
}
测试线程是否活着,可用线程对象的isAlive()方法。当线程处于就绪,运行,阻塞状态返回true。
当线程处于新建和死亡状态,返回false。
已死亡的线程是不可能通过start()方法唤醒线程的。
否则引发IllegalThreadStateException异常;
public class DieThreadDemo {
  public static void main(String[]args) {
    DeadThread dt = new DeadThread();
    for (int i = 0; i < 100; i++) {
      Thread mainThread =Thread.currentThread();//获得当前线程(Main线程)
      System.out.println(mainThread.getName()+ "--" + i);
      System.out.println("------>"+mainThread.isAlive());
      if (i == 10) {
        dt.start();
      }
    }
    if (!dt.isAlive()) {
      dt.start();
    }
  }
}
class DeadThread extends Thread {
  public void run() {
    for (int i = 0; i < 50; i++) {
      System.out.println(getName() +"--" + i);
    }
  }
}
三、线程控制
Join()方法:等待该线程终止。
join 方法:调用join方法的线程对象强制运行,该线程强制运行期间,其他线程无法运行,必须等到该线程结束后其他线程才可以运行。
有人也把这种方式成为联合线程
join方法的重载方法:
join(long millis):
join(long millis,int nanos):
通常很少使用第三个方法:
程序无须精确到一纳秒;
计算机硬件和操作系统也无法精确到一纳秒;
public class JoinDemo {
  public static void main(String[]args) throws Exception {
    Thread join = new Thread(newJoin(),"Join线程");
    join.start();
    int i = 0;
    while(i < 500){
      if(i == 100){
        join.join();
      }
      System.out.println("Main -- > "+ i ++);
    }
  }
}
class Join implements Runnable {
  public void run() {
    int i = 0;
    while(i< 200){
      System.out.println(Thread.currentThread().getName() + "--" + i ++ );
    }
  }
}
setDaemon()方法
后台线程:处于后台运行,任务是为其他线程提供服务。也称为“守护线程”或“精灵线程”。
JVM的垃圾回收就是典型的后台线程。
特点:若所有的前台线程都死亡,后台线程自动死亡。
设置后台线程:Thread对象setDaemon(true);
setDaemon(true)必须在start()调用前。
否则出现IllegalThreadStateException异常;
前台线程创建的线程默认是前台线程;
判断是否是后台线程:使用Thread对象的isDaemon()方法;
并且当且仅当创建线程是后台线程时,新线程才是后台线程。
class Daemon extends Thread {
  public void run() {
    for (int i = 0; i < 10000; i++) {
      System.out.println(getName() +"-" + i);
    }
  }
}
public class DaemonDemo {
  public static void main(String[]args) {
    Daemon d = new Daemon();
    d.setDaemon(true);//把 d 线程设置成后台线程
    d.start();
    for (int i = 0; i < 5; i++) {
      System.out.println(Thread.currentThread().getName()+"--" + i);
    }
  }
}
Sleep()
线程休眠:
让执行的线程暂停一段时间,进入阻塞状态。
sleep(long milllis) throws InterruptedException:毫秒
sleep(long millis,int nanos) throws InterruptedException:毫秒,纳秒
调用sleep()后,在指定时间段之内,该线程不会获得执行的机会。
public class SleepDemo {
  public static void main(String[]args) {
    for (int i = 10; i > 0; i--) {
      System.out.println("还剩" + i);
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e){
        e.printStackTrace();
      }
    }
  }
}
四、线程优先级
每个线程都有优先级,优先级的高低只和线程获得执行机会的次数多少有关。
并非线程优先级越高的就一定先执行,哪个线程的先运行取决于CPU的调度;
默认情况下main线程具有普通的优先级,而它创建的线程也具有普通优先级。
Thread对象的setPriority(int x)和getPriority()来设置和获得优先级。
MAX_PRIORITY : 值是10
MIN_PRIORITY : 值是1
NORM_PRIORITY : 值是5(主方法默认优先级)
class Priority extends Thread{
  public void run() {
    for (int i = 0; i < 100; i++) {
      System.out.println(getName()+",优先级=" + getPriority() +"--"+ i);
    }
  }
}
public class PriorityDemo {
  public static void main(String[]args) {
    Thread.currentThread().setPriority(7);
    for (int i = 0; i < 100; i++) {
      if(i == 10){
        Priority p1 = new Priority();
        p1.setPriority(Thread.MAX_PRIORITY);
        p1.start();
      }
      if(i == 15){
        Priority p2 = new Priority();
        p2.setPriority(Thread.MIN_PRIORITY);
        p2.start();
      }
      System.out.println("main" + i);
    }
  }
}
Yield()方法
线程礼让:
暂停当前正在执行的线程对象,并执行其他线程;
Thread的静态方法,可以是当前线程暂停,但是不会阻塞该线程,而是进入就绪状态。
所以完全有可能:某个线程调用了yield()之后,线程调度器又把他调度出来重新执行。
class Yield implements Runnable {
  public void run() {
    for (int i = 0; i < 100; i++) {
      System.out.println(Thread.currentThread().getName() + "--> " + i);
      if (i % 2 == 0) {
        Thread.yield();// 礼让
      }
    }
  }
}
public class YieldDemo {
  public static void main(String[]args) {
    Yield y = new Yield();
    new Thread(y, "A").start();
    new Thread(y, "C").start();
  }
}
API 过时方法 -- 易死锁, , 不推荐使用
stop:终止线程
马上让线程停止运行,并释放该线程所持有的锁,该操作无法保证对象的内部状态正确;
suspend:挂起线程
使线程进入“阻塞”状态,该状态下CPU不会分给线程时间片,进入这个状态可以用来暂停一个线程的运行,在被resume方法调用前,不可用.
如果要suspend的目标线程对一个重要的系统资源持有锁,那么没任何线程可以使用这个资源直到要suspend的目标线程被resumed。
resume:恢复线程
恢复被suspend方法挂起的线程Java2开始已经废弃了suspend()和resume()方法,因为使用这两个方法可能会产生死锁,所以应该使用同步对象调用wait()和notify()的机制来代替suspend()和resume()进行线程控制。
五、线程的安全性问题
导致安全问题的出现的原因:
多个线程访问出现延迟。
线程随机性。
注:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。
我们可以通过Thread.sleep(long time)方法来简单模拟延迟情况。
六、线程同步
当多个线程访问同一份数据的时候,很容易出现线程安全的问题。
让多个操作在同一个时间段内只能有一个线程进行,其它线程要等到该线程完成后才可以继续执行。
经典的事例: 银行取钱
用户输入取款金额;
系统判断余额是否大于取款金额
若余额大于取款金额,取款成功;
若余额小于取款金额,取款失败。
例子分析:
账户类:Account
字段balance表示余额
取钱类:DrawMoney 线程类
字段a表示账户对象,money表示需要取的金额;
把取钱行为放在线程体里面;
提示:
public void run(){
  if(c.getBlance() >= money){
    System.out.println(getName()+",取走" + money);
    Thread.sleep(100);
    c.setBlance(c.getBlance()- money);
    System.out.println("余额= " +c.getBlance());
  }else{
    System.out.println(getName()+ "余额不足,余额"+ c.getBlance() +",需要取走" +money );
  }
}
解决问题:
方法一:
当发生以上情况时,java给我们提供了同步代码块
synchronized(obj){
//obj表示同步监视器,是同一个同步对象
/**.....
TODO SOMETHING
*/
}
方法二:
synchronized 返回值类型 方法名(参数列表){
/**.....
TODO SOMETHING
*/
}
同步方法的同步监听器其实的是 this
/**
* 账号
*
*/
public class Account {
/**
* 余额
*/
  private double blance;
  public Account(double blance) {
    this.blance = blance;
  }
  public double getBlance() {
    return blance;
  }
  public void setBlance(double blance){
    this.blance = blance;
  }
  public synchronized void draw(doubledrawMoney){
    if(getBlance() >= drawMoney) {
      System.out.println(Thread.currentThread().getName()+",取出" + drawMoney);
      try {
        Thread.sleep(1);
      } catch (InterruptedException e){
        e.printStackTrace();
      }
//修改余额
      setBlance(getBlance() -drawMoney);
      System.out.println("余额= " +getBlance());
    }else{
      System.out.println("对不起余额不足");
    }
  }
}
/**
* 取钱操作
*/
public class DrawThread extendsThread{
/**
* 账号
*/
  private Account a;
/**
* 取的钱
*/
  public double drawMoney;
  public DrawThread(String name,Account a,double drawMoney){
    super(name);
    this.a = a;
    this.drawMoney = drawMoney;
  }
/**
* 当多条线程修改同一个共享资源数据的时候,将会导致数据安全问题
*/
  public void run(){
/*   synchronized (a) {
      if(a.getBlance() >= drawMoney) {
        System.out.println(getName()+",取出" +drawMoney);
        try {
          Thread.sleep(1);
        } catch (InterruptedExceptione) {
          e.printStackTrace();
        }
//修改余额
        a.setBlance(a.getBlance() -drawMoney);
        System.out.println("余额= " +a.getBlance());
      }else{
        System.out.println("对不起余额不足");
      }
    }*/
    a.draw(drawMoney);
  }
}
以上内容是课堂笔记之后是今天作业:做一个售票的程序
要求三个窗口,一共50张票,随机售票,售完后求小明在每个窗口购票的几率:

public class Dome {                         //主类
  public static void main(String[] args) {             //主方法
    Chuangkou c1 = new Chuangkou("窗口一");
    Chuangkou c2 = new Chuangkou("窗口二");
    Chuangkou c3 = new Chuangkou("窗口三");
    Thread t1 = new Thread(c1);               //新建三个线程
    Thread t2 = new Thread(c2);
    Thread t3 = new Thread(c3);
    t1.start();                         //三个线程同时运行
    t2.start();
    t3.start();
  }
}

public class Piao{                        //用来统计票数  
  public static String name = "目前只有这一种票";
  public static int piao = 1;
}

public class Chuangkou implements Runnable{           //窗口类
  public String name;
  static int a = 0;                        //定义三个静态变量用来统计每个窗口一共卖的票
  static int b = 0;
  static int c = 0;
  public Chuangkou(String name) {               //有参构造器
    this.name = name;
  }
  public void run(){                        //run方法
    while(Piao.piao<49){                    //如果票数等于49就停止
      try {
        Thread.sleep(1/10);                //经测试睡眠1/10的时间最有利于每个窗口的售票率
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      synchronized(Piao.name){                //同步,防止多个线程同时运行
        System.out.println(name+"卖出了第 "+Piao.piao+++"票");
      }
      switch(name){                      //统计窗口票数
        case "窗口一":a+=1;break;
        case "窗口二":b+=1;break;
        case "窗口三":c+=1;break;
      }
    }
    System.out.println(name+"售罄");
    if(Piao.piao == 50){                      //计算出小明购票几率
      System.out.println("小明买窗口一的票的概率为"+(a/50.0));
      System.out.println("小明买窗口二的票的概率为"+(b/50.0));
      System.out.println("小明买窗口三的票的概率为"+(c/50.0));
    }

  }
}

目前这个作业还有问题,不知为什么小明的效率有时会出现两次,有时不出现,希望大神指导一下。

线程(java课堂笔记)的更多相关文章

  1. Java课堂笔记(零):内容索引

    回想自己学习和使用Java的时间也是很长了.本科期间课堂上浅尝辄止地学习了点皮毛,后来也是搁置不用,未曾深入研究.研究生期间因为项目和实习的原因,基本算是重新拾起Java这门语言,并且接触到了Spri ...

  2. 我做的第一个程序(菜鸟的java课堂笔记)

    内存: 堆区,栈区,常量区,计数区,代码区: 数据类型: 基本数据类型:整形,浮点型,布尔型 boolean 只能返回真假值,字符型: 引用数据类型:类,数组: 基本数据类型-->直接存放到栈区 ...

  3. Java课堂笔记(一):Java基础

    本篇博客将对Java中的数据类型.操作符,常量与变量和数组进行介绍.这些内容都是Java中最基本的知识,也是初学Java时最开始就需要了解的东西. Java数据类型 Java是一种强类型的语言,这就意 ...

  4. Java课堂笔记1

    1.  Java严格区分大小写 2.  一个源文件public主类名必须和文件名完全一致 3. 命名规则严格要求,字母.数字.下划线.美元符号$.下划线_组成,其中不能以数字开头,也不能使用Java的 ...

  5. Java课堂笔记(三):抽象类和接口

    在面向对象一文中,我们说了多态的一个功能是将“做什么”和“怎么做”分离开来,所采用的方法是将不同的具体实现放在不同的子类中,然后向接口中传入一个父类对象的引用.而本篇博客要说的内容则为接口(此处&qu ...

  6. Java课堂笔记(二):面向对象

    几乎每一本介绍Java语言的书中都会提到“面向对象”的这个概念,然而博主初学Java时看到这方面的内容一般都是草草地看一看,甚至是直接略过.原因很简单:考试基本不考,而且初学阶段写代码也很少用上.但事 ...

  7. 第九次java课堂笔记

  8. java课堂笔记

  9. java课堂笔记4

随机推荐

  1. vue-router2 使用

    VUE-ROUTER2  API http://router.vuejs.org/zh-cn/api/router-link.html   1,安装vue-router npm install vue ...

  2. 深入tornado中的Configurable

    Configurable十分重要! 位于tornado.util文件中,它是一个工厂类. 我们暂且称这个类为 配置类 . 我们暂且约定:该类的子类称之为 直属配置子类 , 该类的孙类.重孙类……称之为 ...

  3. 请为main函数提供返回值

    很多人甚至市面上的一些书籍,都使用了void main( ) ,其实这是错误的.C/C++ 中从来没有定义过void main( ).C++ 之父 Bjarne Stroustrup 在他的主页上的 ...

  4. JS的Dom树小结

    一[DOM树节点]  DOM节点分为三大类:元素节点.文本节点.属性节点 文本节点.属性节点,为元素节点的两个子节点:  通过getElement系列方法,可以去到元素节点.     二[查看节点] ...

  5. css grid学习材料整理

    2017-04-18 19:59:02 由浅入深: 什么是网格布局:http://www.w3cplus.com/css3/what-is-css-grid-layout.html 浏览器如何开启网格 ...

  6. Expression表达式树动态查询

    在进行数据列表的查询中,我们通常会使用两种方式进行查询: linq查询 数据库sql语句查询 这样固然可以实现查询,本人之前也都是这么做的,因为查询的条件很少.使用linq,可以将所有的查询条件的属性 ...

  7. 观察者模式(Observer)发布、订阅模式

    观察者模式定义了对象之间一对多的依赖关系,这样一来,当一个对象改变时,他的所有依赖者都会收到通知并自动更新.   模式中的角色 1.抽象主题(Subject):它把所有观察者对象的引用保存到一个聚集里 ...

  8. 设置共享目录(主机win7,虚拟机Ubuntu)

    1.安装增强功能包 启动虚拟机后,在 设备 -> 分配光驱 选择VBoxGuestAdditions.iso增强包镜像(在virtualbox安装目录下) 在虚拟机中挂载光驱镜像: #mkdir ...

  9. 浅析如何在Nancy中使用Swagger生成API文档

    前言 上一篇博客介绍了使用Nancy框架内部的方法来创建了一个简单到不能再简单的Document.但是还有许许多多的不足. 为了能稍微完善一下这个Document,这篇引用了当前流行的Swagger, ...

  10. Web 版 powerdesigner (Canvas) 技术分享

    演示地址:http://www.netuml.com:8088  <canvas></canvas>是HTML5出现的新标签,像所有的dom对象一样它有自己本身的属性.方法和事 ...