一、Java的多线程有三种实现方式。

  1、继承创建.

    a、定义子类,重写run方法

    b、创建Thread子类的实例(即现成对象)

    c、调用start() 方法启动现成

    特征:不可以共享变量。

 public class FirstThreadByExtends extends Thread {
private int i; public FirstThreadByExtends(){
super();
}
public FirstThreadByExtends(String name){
super(name);
}
public void run() {
for (; i < 100; i++) {
System.out.println(getName() + " " + i);
}
} public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
System.out.println("当前线程的名字: " + Thread.currentThread().getName());
if (i == 20) {
new FirstThreadByExtends("线程" + i).start();
}
}
} }

    Note: 每次都是new出来的对象,没有共享变量。

  2、实现Runable接口.

    a、定义Runnable接口实现类,重写run()方法

    b、创建Runnable实现类实例,作为Thread实例的Target

    c、调用start() 方法启动现成

    特征:可以共享变量,因为可以用同一个Target创建多个线程。

 public class SecondThreadByImplementsRunnable implements Runnable {
private int i; public void run() {
for (; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println("当前线程的名字a: " + Thread.currentThread().getName());
if (i == 5) {
SecondThreadByImplementsRunnable st = new SecondThreadByImplementsRunnable();
new Thread(st,"新线程1").start();
new Thread(st,"新线程2").start();
}
}
}
}

SecondThreadByImplementsRunnable

  3、使用Callable和future创建线程

    a、创建Callable实现类,实现call()方法

    b、创建Callable实现类的实例,用FutureTask实现类来包装

    c、使用FutureTask作为Thread的Target来创建对象

    d、通过调用FT的get()方法来获取线程的返回值

    特征:可以有返回值,可以抛出异常

 public class ThirdThreadByImplementsCallable implements Callable<Long> {
public Long call() throws Exception {
int i = 0;
for (; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
return System.currentTimeMillis();
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
ThirdThreadByImplementsCallable tt = new ThirdThreadByImplementsCallable();
FutureTask<Long> task = new FutureTask<Long>(tt);
for (int i = 0; i < 100; i++) {
System.out.println("当前线程的名字: " + Thread.currentThread().getName() +" "+ i);
if (i == 5) {
new Thread(task,"有返回值的线程1").start();
}
if (i == 6) {
System.out.println("----------------------test---------------------------------");
new Thread(task,"有返回值的线程2").start();
}
}
System.out.println("子线程的返回值:" + task.get());
}
}

    Note: 第二个线程实例没有跑起来,情况未知,待解决!

三种方式的对比

  采用实现Ruannable或Callable接口的方式创建:

    a、线程类只是实现了Runnable或Callable接口,还可以继承其他类

    b、多个线程可以共享一个Target对象,适合多个相同线程处理统一份资源,从而将CPU,代码,数据分开,形成清晰的模型,较好的体现了面向对象的思想

    c、劣势是 编程较复杂,如需访问当前现成还要调用 Thread.CurrentThread()方法.

  继承Thread类的方式创建多线程:

    a、优势,编写简单,可直接调用this获得当前线程

    b、劣势,因继承了Thread,无法再继承其它类

二、线程生命周期

  线程的生命周期包括5个状态:新建(new),就绪(Runnable),运行(Running),阻塞(Blocked),死亡(Dead)。

  a、新建和就绪

    新建:当使用了new关键字创建了一个线程,该线程就处于新建状态。

    就绪:当线程调用了start()方法,该线程就处于就绪状态,JVM会为其创建方法调用栈和程序计数器。该状态表示可以运行,准备被JVM线程调度器调度。

    注意:启动线程的方法是调用线程实例的start(),如果直接调用run()方法,系统会把线程对象,当成一个普通对象,run()方法也会被当成普通方法,而不是线程执行体。

 package lifecycle;

 public class InvokeRun extends Thread {
private int i; public void run() {
for (; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
} public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 20) {
//直接调用线程对象的Run方法
//系统会把线程对象当作普通对象,把run方法当成普通方法,
//所以,一下两行代码不会启动两个线程,而是依次执行两个Run方法
new InvokeRun().run();
new InvokeRun().run();
}
}
}
}

InvokeRun

  b、运行和阻塞

    运行:就绪状态时,获得了CPU,该线程就处于运行状态,如果计算机只有一个CPU,那么在任何时刻只有一个线程处于运行状态。当然,在一个多处理器的机器上,将会有多个线程并行执行;但当线程数大于CPU数,也会出现轮换。具体线程调度细节取决于底层平台所采用的策略。

    运行==>阻塞:

          a、线程调用sleep()方法主动放弃所占用的资源

          b、线程调用了一个阻塞式IO,在该方法返回之前,该线程被阻塞

          c、线程试图获得一个同步监视器,但该同步监视器被其它线程所持有。关于同步监视器的知识、后面将有更深入的介绍

          d、线程在等待某个通知(notify)

          e、程序调用了线程的suspend()方法将该线程挂起。但这个方法容易导致死锁,所以应该尽量避免使用该方法

    阻塞==>运行:

          a、sleep()方法超过了指定的时间

          b、线程调用的阻塞式IO已经返回

          c、线程成功获得了试图获得的同步监视器

          d、线程正在等待某个通知时,其它线程发出了一个通知

          e、处于挂起状态的的线程被调用了resume()方法

  c、线程死亡

    a、run()或call方法执行完成,线程正常结束

    b、线程抛出一个未捕获的Exception或Error

    c、直接调用线程的Stop()方法结束线程,容易死锁,不推荐使用

    注意:不要试图对已经死亡的线程调用start()方法,会抛出不合法线程状态异常

 public class StartDead implements Callable<Long> {
private int i; public static void main(String[] args) throws InterruptedException, ExecutionException {
StartDead sd = new StartDead();
FutureTask<Long> ft = new FutureTask<Long>(sd);
Thread t1 = new Thread(ft, "马上会死的线程,哇哈哈哈哈");
//t1.setPriority(Thread.MAX_PRIORITY);
t1.start();
//get方法会阻塞main方法
System.out.println(ft.get());
for (int i = 0; i < 300; i++) {
if (i == 20) {
System.out.println(t1.isAlive() + "Love is forever!");
}
}
if (!t1.isAlive()) {
t1.start();
}
} public Long call() throws Exception {
for (; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
return 100L;
} }

StartDead

三、线程控制

  

  1、join()线程

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

    join()方法由使用线程的程序调用,将大问题划分成许多小问题,每一个小问题分配一个线程。当所有小问题都得到处理以后,再调用主线程进行进一步操作。

    join()方法有三种重载形式:

      join(),等待被join的线程执行完成。

      join(long millis),等待被join的线程的时间最长为millis毫秒。如果在millis毫秒之内被join的线程还没有执行结束,则不再等待。

      join(long millis,int nanos),比上面多了个微秒。

 public class JoinThread extends Thread {
public JoinThread(String name) {
super(name);
} public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(this.getName() + " " + i);
}
} public static void main(String[] args) throws InterruptedException {
new JoinThread("新线程").start();
for (int i = 0; i < 100; i++) {
if (i == 20) {
JoinThread jt = new JoinThread("被Join的线程");
jt.start();
jt.join(0);
}
System.out.println(Thread.currentThread().getName() + " " + i);
}
} }

JoinThread

  2、后台线程

    所谓的后台线程,是指在程序运行的时候在后台提供一种通用服务的线程,并且这种线程并不属于程序中不可或缺的部分。因此当所有的非后台线程结束时,程序也就终止了,同时会杀死所有后台线程。反过来说,只要有任何非后台线程(用户线程)还在运行,程序就不会终止。后台线程在不执行finally子句的情况下就会终止其run方法。后台线程创建的子线程也是后台线程。

    下面是一个后台线程的示例:

 public class Daemon extends Thread {
public Daemon(){ }
public Daemon(String name){
super(name);
} public void run(){
for (int i = 0; i < 1000; i++) {
System.out.println(this.getName() + " " + i);
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} public static void main(String[] args) throws InterruptedException {
Thread t = new Daemon("守护线程");
t.setDaemon(true);
t.start();
for (int i = 0; i < 10; i++) {
sleep(200);
System.out.println(Thread.currentThread().getName() + " " + i);
}
//Program end here
//and Daemon is end follow it.
System.out.println("啊哦,守护线程应该死了了吧!");
}
}

Daemon

  3、线程睡眠:Sleep()

    让线程暂停,进入阻塞状态。它是Thread的静态方法。

 public class SleepThread {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
Thread.sleep(300);
}
}
}

SleepThread

  4、线程让步:yield()

    让线程进入就绪状态,等待CPU重新调度。不过在此期间,只有 相同或更高优先级的进程能被调用,不然的话会继续执行。

  5、线程优先级

    范围 1 ~ 10,三个静态常量  MAX_PRIORITY:10, MAX_PRIORITY:1, MAX_PRIORITY:5.

    t.setPriority(MAX_PRIORITY);

四、线程同步

  1、线程安全

    银行取钱问题。

      a. 用户输入帐号、密码,系统验证是否通过

      b. 用户输入取款金额

      c. 系统判断账户余额是否大于取款金额

      d. 如果余额大于取款金额,成功;  如果余额小于取款金额,取款失败。

    以下是没有同步控制的代码,也许有时候会正确,但只要有一次错误,那整个代码就是错误的。

  2、同步代码快

    synchronized (obj) {},  obj就是同步监视器,线程执行之前必须获得同步监视器的锁定。

    流程:加锁==>操作==>释放锁。

         synchronized (account) {
// If balance > drawAmount, check out!
if (account.getBalance() > drawAmount) {
System.out.println(this.getName() + "取钱成功!吐出钞票:" + this.drawAmount);
try {
Thread.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
account.setBalance(account.getBalance() - this.drawAmount);
System.out.println("\t余额为:" + account.getBalance());
} else {
System.out.println(getName() + "余额不足,取款失败!");
}
}

  3、同步方法

    用同步方法可以非常方便的实现线程安全类。任意线程都可以安全访问。但无法显示指定同步监视器,同步监视器就是本身。

    代码如下:

      public synchronized void draw(double drawAmount) {
// If balance > drawAmount, check out!
if (this.getBalance() > drawAmount) {
System.out.println(Thread.currentThread().getName() + "取钱成功!吐出钞票:" +
drawAmount);
try {
Thread.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
this.setBalance(this.getBalance() - drawAmount);
System.out.println("\t余额为:" + this.getBalance());
} else {
System.out.println(Thread.currentThread().getName() + "余额不足,取款失败!");
}
}

  4、释放同步监视器的锁定

    a.同步方法、同步代码快执行结束,当前线程释放同步监视器。

    b.break、reutrn终止了该代码块,该方法的继续执行,当前线程会释放同步监视器。

    c. 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致了该代码块、该方法的异常结束时,当前线程将会释放同步监视器。

    d. 当前线程执行同步代码块或同步方法时,程序执行了同步监视器对象的wait方法,则当前线程暂停,并释放同步监视器。

    以下不会释放:

    a.sleep().yield();

    b.suspend(). resume();  容易弄成死锁,不推荐

  5、同步锁

    lock.lock(),  lock.unlock();

 lock.lock();
try {
// If balance > drawAmount, check out!
if (this.getBalance() > drawAmount) {
System.out.println(Thread.currentThread().getName() + "取钱成功!吐出钞票:" + drawAmount);
try {
Thread.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
this.setBalance(this.getBalance() - drawAmount);
System.out.println("\t余额为:" + this.getBalance());
} else {
System.out.println(Thread.currentThread().getName() + "余额不足,取款失败!");
}
} finally {
lock.unlock();
}

  6、死锁

    资源的相互依赖,你把我要用的给锁了,我把你要用的给锁了,我们各自拿着一块等待对方,谁都不肯释放。

    

Java Thread Basic的更多相关文章

  1. Java Thread 的 sleep() 和 wait() 的区别

    Java Thread 的使用 Java Thread 的 run() 与 start() 的区别 Java Thread 的 sleep() 和 wait() 的区别       1. sleep ...

  2. Java Thread 的 run() 与 start() 的区别

    Java Thread 的使用 Java Thread 的 run() 与 start() 的区别 Java Thread 的 sleep() 和 wait() 的区别             1. ...

  3. Java Thread wait, notify and notifyAll Example

    Java Thread wait, notify and notifyAll Example Java线程中的使用的wait,notify和nitifyAll方法示例. The Object clas ...

  4. java: Thread 和 runnable线程类

    java: Thread 和 runnable线程类 Java有2种实现线程的方法:Thread类,Runnable接口.(其实Thread本身就是Runnable的子类) Thread类,默认有ru ...

  5. Java Thread join() 的用法

    Java Thread中, join() 方法主要是让调用改方法的thread完成run方法里面的东西后, 在执行join()方法后面的代码.示例: class ThreadTesterA imple ...

  6. Java thread jargon

    In Java thread topic, the task to be executed and the thread to drive the task are two concepts shou ...

  7. 性能分析之-- JAVA Thread Dump 分析综述

    性能分析之-- JAVA Thread Dump 分析综述       一.Thread Dump介绍 1.1什么是Thread Dump? Thread Dump是非常有用的诊断Java应用问题的工 ...

  8. Java Thread线程控制

    一.线程和进程 进程是处于运行中的程序,具有一定的独立能力,进程是系统进行资源分配和调度的一个独立单位. 进程特征: A.独立性:进程是系统中独立存在的实体,可以拥有自己独立的资源,每个进程都拥有自己 ...

  9. [译]Java Thread wait, notify和notifyAll示例

    Java Thread wait, notify和notifyAll示例 Java上的Object类定义了三个final方法用于不同线程间关于某资源上的锁状态交互,这三个方法是:wait(), not ...

随机推荐

  1. 如何让Vim显示dos下的^M符号

    /*********************************************************************** * 如何让Vim显示dos下的^M符号 * 声明: * ...

  2. Java [Leetcode 58]Length of Last Word

    题目描述: Given a string s consists of upper/lower-case alphabets and empty space characters ' ', return ...

  3. Java [Leetcode 198]House Robber

    题目描述: You are a professional robber planning to rob houses along a street. Each house has a certain ...

  4. 【C#学习笔记】自我复制

    using System; using System.IO; using System.Diagnostics; namespace ConsoleApplication { class Progra ...

  5. android改变字体的颜色的三种方法

    写文字在Android模拟器中的方法 法一: main.xml配置文件: <TextView android:id="@+id/tv" android:layout_widt ...

  6. CentOS7 mariadb 修改编码

    CentOS7 mariadb 编码的修改: 网上看了不少的解决方案,要么是比较老的,要么是不正确,测试成功的方式,记录备查. 登录MySQL,使用SHOW VARIABLES LIKE 'chara ...

  7. PHP中的替代语法

    今天看了一下wordpress的代码,里面有些少见的php替代语法, <?php else : ?> <div class="entry-content"> ...

  8. Spark中的编程模型

    1. Spark中的基本概念 Application:基于Spark的用户程序,包含了一个driver program和集群中多个executor. Driver Program:运行Applicat ...

  9. HDFS分布式文件系统设计思想

    HDFS设计目标 1)硬件错误是常态,数据保存需要冗余. 2)数据批量读取,Hadoop擅长数据分析而不是事务处理. 3)大规模数据集. 4)简单一致醒模型,降低系统复杂度,文件一次写入多次读取, 5 ...

  10. Javascript原理

    1.javascript创建对象 创建新对象有两种不同的方法: 定义并创建对象的实例 person=new Object(); person.firstname="Bill"; p ...