一、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. VB VS2003获取当前进程用户登录

    Page.User.Identity.Name获取当前进程用户名称,VS03才可以用

  2. MVC的项目使用html编辑器UEditorMINI

    一个MVC的项目中有个发布新闻的页面需要用到一个html的编辑器,网上看到UEditor评价貌似还不错, 因为我用到的功能比较简单,就下载了MINI版本的, 使用的过程在这里总结一下. 关于UEdit ...

  3. [反汇编练习] 160个CrackMe之002

    [反汇编练习] 160个CrackMe之002. 本系列文章的目的是从一个没有任何经验的新手的角度(其实就是我自己),一步步尝试将160个CrackMe全部破解,如果可以,通过任何方式写出一个类似于注 ...

  4. python - 简述list. extend() 和 append() 区别

    >>> a = 'hello' >>> b = [1, 2, 3] >>> b.append(a) >>> b [1, 2, 3 ...

  5. css的框架——base.css

    一.常用的base.css文件(也是比较简略的,但按需增加) body,ul,li,ol,dl,dd,h1,h2,h3,h4,h5,h6,input,p{ margin:;} ul,ol { padd ...

  6. context.Response.End()的用法和本质

    using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace Web_C ...

  7. redis错误汇总

    1.redis因为内存不够而启动失败 Microsoft Open Tech group 在 GitHub上开发了一个REDIS Win64的版本,项目地址是:https://github.com/M ...

  8. HDU5777 domino (BestCoder Round #85 B) 思路题+排序

    分析:最终的结果肯定会分成若干个区间独立,这些若干个区间肯定是独立的(而且肯定是一边倒,左右都一样) 这样想的话,就是如何把这n-1个值分成 k份,使得和最小,那么就是简单的排序,去掉前k大的(注意l ...

  9. bjfu1211 推公式,筛素数

    题目是求fun(n)的值 fun(n)= Gcd(3)+Gcd(4)+…+Gcd(i)+…+Gcd(n).Gcd(n)=gcd(C[n][1],C[n][2],……,C[n][n-1])C[n][k] ...

  10. MFC DLL 资源模块句柄切换[转]

    以前写MFC的DLL的时候,总会在自动生成的代码框架里看到提示,需要在每一个输出的函数开始添加上 AFX_MANAGE_STATE(AfxGetStaticModuleState()).一直不明白这样 ...