一、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. SJ9012: IE6 IE7 不支持 JSON 对象

    标准参考 JSON 是一种数据交换格式,RFC 4627 对 JSON 进行了详细描述. 根据 ECMA-262(ECMAScript)第 5 版中描述,JSON 是一个包含了函数 parse 和 s ...

  2. 一个java高级工程师的进阶之路

    宏观方面 一. JAVA.要想成为JAVA(高级)工程师肯定要学习JAVA.一般的程序员或许只需知道一些JAVA的语法结构就可以应付了.但要成为JAVA(高级) 工程师,您要对JAVA做比较深入的研究 ...

  3. 【JS】js获得下拉列表选中项的值和id

    function tijiao(){ var elem = document.getElementById("dish_sort"); var index=elem.selecte ...

  4. 【转】个人常用iOS第三方库以及XCode插件介绍 -- 不错

    原文网址:http://adad184.com/2015/07/08/my-favorite-libraries-and-plugins/ 第三方库是现在的程序员离不开的东西 不光是APP开发 基本上 ...

  5. 【转】iOS中定时器NSTimer的使用

    原文网址:http://www.cnblogs.com/zhulin/archive/2012/02/02/2335866.html 1.初始化 + (NSTimer *)timerWithTimeI ...

  6. 如何打开和关闭Oracle Flashback

    1.打开flashback: 关闭数据库 SQL>shutdown immediate; 启动到mount方式 SQL>startup mount; 如果归档没有打开,打开归档[因为fla ...

  7. Shell获取Aix/linux/unix机器上db2和os的信息并上传到指定服务器

    (之前写过一篇类似的文章,当时传输文件用的是ftp,因为项目觉得ftp不够安全所以这次换成了scp,同时对脚本的一些地方也做了一些调整) 其实做这个东西还是因为项目的需求,需要获取某些机器(目前主要是 ...

  8. windows各种程序中文显示乱码又找不到原因时

    我电脑上的各种程序,如xshell,Navicat for MySQL都不正常显示中文,该软件的编码,utf-8,gbk,gb2312来回切换好几回,没一次正常,最好解决办法如下       进入控制 ...

  9. Extjs Ext.onReady的用法

    最近在学习Extjs,当然首先学习的肯定是从官网下载下来的example. 从学习上而言对于Extjs,我个人认同的是李林峰老师所言先从视图开始,搞一些所见即所得的东西. 废话有点多,对于Extjs而 ...

  10. linux常用命令之--磁盘管理命令

    linux的磁盘管理命令 1.查看磁盘空间 df:用于显示磁盘空间的使用情况 其命令格式如下: df [-option] 常用参数: -i:使用inodes显示结果 -k:使用KBytes显示结果 - ...