1 创建线程的方式
- 扩展java.lang.Thread类
- 实现Runnable接口
2 扩展java.lang.Thread类
用户的线程只需要继承Thread类,覆盖Thread类的run()方法即可。
Thread类代表线程类,最主要的两个方法
run() --- 包含线程运行时候所执行的代码 (没有任何异常抛出): public void run()
start() --- 用于启动线程
例子:
package com.cici.extends; public class Machine extends Thread{ public void run(){ for(int a=0;a<50;a++){ System.out.println(a); } } public static void main(String[] args){ Machine machine = new Machine(); machine.start(); } }
2.1 用户自定义线程各自运行
public class Machine extends Thread{ private int a; @Override public void run() { for( a=0;a<10;a++){ System.out.println(currentThread().getName()+"----"+a); try { sleep(2000);//给其他线程运行机会 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void main(String[] args) { Machine th1 = new Machine(); Machine th2 = new Machine(); th1.start();//th1线程启动 th2.start();//th2线程启动 th1.run();//主线程调用th1线程 } }
备注:从面向对象的角度来看,Thread类的run()方法是准们被自身线程执行的。主线程调用Thread类的run(),违背了Thread类提供的run()的初衷。
输出: Thread-0----0 main----0 Thread-1----0 Thread-0----1 main----2 Thread-1----1 Thread-0----3 Thread-1----2 main----4 Thread-0----5 main----6 Thread-1----3 Thread-0----7 main----8 Thread-1----4
2.2 线程拥有各自的实例变量
public class Machine extends Thread{ private int a; @Override public void run() { for( a=0;a<10;a++){ System.out.println(currentThread().getName()+"----"+a); try { sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void main(String[] args) { Machine th1 = new Machine(); Machine th2 = new Machine(); th1.start(); th2.start(); // th1.run(); } }
输出结果
Thread-0----0 Thread-1----0 Thread-0----1 Thread-1----1 Thread-0----2 Thread-1----2 .... .... ....
解释:
线程th1和线程th2用有各自的实例变量,运行各自的run()方法.
2.3 主线程与用户自定义线程并发运行
public class Machine_SHARE_ONE_INSTANCE extends Thread{ private int a=0; @Override public void run() { for( a=0;a<10;a++){ System.out.println(currentThread().getName()+"----"+a); try { sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Machine_SHARE_ONE_INSTANCE m1 = newMachine_SHARE_ONE_INSTANCE(); m1.start(); m1.run(); } } //运行结果 main----0 Thread-0----0 Thread-0----1 main----2 main----3 Thread-0----4 Thread-0----6 main----6 Thread-0----7 main----8 main----9
解释:
主线程和Machine线程都会执行Machine对象的run()方法。操纵同一个实例变量a。
3 实现Runnable接口
java.lang.Runnable接口,最主要的两个方法
run() --- 包含线程运行时候所执行的代码 (没有任何异常抛出): public void run()
start() --- 用于启动线程
Thread 线程类也实现了 Runnable接口,并有如下的构造方法:public Thread(Runnable target)
public class Machine_Runnable implements Runnable{ private int a=0; public void run() { for(a=0;a<10;a++){ System.out.println(Thread.currentThread().getName()+":"+a); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Machine m = new Machine(); Thread t1 = new Thread(m); t1.start(); } }
4 Exercise:
4.1 以下代码能否编译通过?如果通过将得到什么打印结果?
1 public class WhatHappens implements Runnable{ 2 public static void main(String[] args) { 3 Thread t = new Thread(this); 4 t.start(); 5 } 6 public void run() { 7 System.out.println("HI"); 8 } 9 }
不能line3 出现错误:原因不能再static静态方法内使用 this关键字
4.2 能否编译通过?打印什么内容?
OK
public class WhatHappens implements Runnable{ public static void main(String[] args) { WhatHappens wh = new WhatHappens(); wh.run(); } public void run() { for(int i=0;i<10;i++){ System.out.println("Value of i : "+i); } } }
Value of i : 0 Value of i : 1 Value of i : 2 Value of i : 3 Value of i : 4 Value of i : 5 Value of i : 6 Value of i : 7 Value of i : 8 Value of i : 9
4.3 Line 5 出现什么代码会 打印出RUNING ?
1 public class RunTest implements Runnable{ 2 public static void main(String[] args) { 3 RunTest rt = new RunTest(); 4 Thread th = new Thread(rt); 5 //?? 6 } 7 public void run() { 8 System.out.println("RUNING"); 9 } 10 void go(){ 11 start(1); 12 } 13 private void start(int i) {} 14 }
th.start();
th.run();
rt.run();
4.4 下列代码能否编译通过?
public class Test { public static void main(String[] args) { Thread th = new Thread(new RunHandler()); th.start(); } } class RunHandler{ public void run(){ System.out.println("RUNING"); } }
不能RunHander没有实现Runable接口
4.5 假设某银行服务器内存储了1000块钱的数据信息,现有两台ATM同时取款,一个取800,一个取700,用多线程解决同时取款的问题
参考出自http://sd6292766.iteye.com/blog/695814
谢谢sd6292766
1 public class TestAccount { 2 public static void main(String[] args) { 3 /*临界资源 */ 4 Account account = new Account(); 5 Withdraw w1= new Withdraw (700); 6 w1.account=account; 7 Withdraw w2= new Withdraw (800); 8 w2.account=account; 9 w1.start(); 10 w2.start(); 11 } 12 } 13 class Withdraw extends Thread{ 14 public Account account; 15 private int withdrawMoney; 16 public Withdraw (int money){ 17 this.withdrawMoney = money; 18 } 19 public void run(){ 20 System.out.println(this.getName()+" 来取"+ withdrawMoney+"块钱"); 21 account.withdraw (withdrawMoney); 22 System.out.println("余额为:"+account.getBalance()+" 元!"); 23 } 24 } 25 class Account { 26 private float balance =1000; 27 public float getBalance() { 28 return balance; 29 } 30 31 public void setBalance(float balance) { 32 this.balance = balance; 33 } 34 /*取款 同步*/ 35 public synchronized void withdraw(float money){ 36 if(balance>=money) { 37 System.out.println("被取走"+money+"元"); 38 try { 39 Thread.sleep(1000); 40 } catch (InterruptedException e) { 41 e.printStackTrace(); 42 } 43 balance -=money; 44 }else{ 45 System.out.println("对不起 余额不足"); 46 } 47 } 48 49 }
5 线程状态
5.1 新建状态
new语句创建线程对象处于新建状态
5.2 就绪状态
当一个线程对象创建之后 其他线程调用它的start()方法 该线程进入就绪状态
5.3 运行状态
处于这个状态的线程占用CPU 执行程序代码
5.4 阻塞状态
1)位于对象等待池中的阻塞状态:线程位于运行状态时,如果执行了某个对象的wait().JVM就会把线程放入这个对象的等待池当中。
wait():执行该方法的线程释放对象的锁,JVM就会把线程放入这该对象的等待池当中,等待其他线程将其唤醒。
2)位于对象琐池中的阻塞状态:当前线程处于运行状态时,试图获得某个对象的同步锁时,如果该对象的该对象的同步锁已经被其他线程占用,
JVM就把这个线程放到这个对象的锁池当中。
*解释每个java对象只有一个同步锁,在任何时刻,最多允许一个线程拥有这把锁。当消费者线程试图执行以上带有synchronized(this)
标记的代码块时,消费者线程必须先获得this关键字引用的Stack对象的锁。
--1 假如这个锁已经被其他线程占用,JVM把消费者线程放到Stack对象的锁池中,消费者线程进入阻塞状态。在Stack对象的锁池中
可能会有许多等待锁得线程,等其他线程释放了锁,JVM从锁池中随机取出一个线程,是这个线程拥有锁。该线程到就绪状态。
--2 假如这个锁没有被其他线程占用,消费者就会获得这把锁,并执行同步代码块。一般消费者线程只有执行完同步代码块,才释放锁。
3)其他阻塞状态:当执行了sleep()方法,或者调用了其他线程的join() 或者发出IO请求时 就会进入这个状态
5.5 死亡状态 Dead
线程退出run() 就进入死亡状态 该线程生命周期结束
6 什么时候持有锁的线程会释放锁
1 执行完同步代码块
2 执行同步代码块过程中 遇到异常导致线程终止 锁会被释放
3 执行同步代码块过程中 执行了锁所属对象的wait()方法 线程释放锁 进入对象等待池中
下列情况不会释放锁
1 执行同步代码块过程中 执行了Thread.sleep()方法
当前线程放弃CPU 开始睡眠 在睡眠中不释放锁
2 执行同步代码块过程中 执行了Thread.yield()方法
当前线程放弃CPU 开始睡眠 在睡眠中不释放锁
7 Thread类静态方法比较
wait() 执行该方法的线程 释放对象锁 JVM把该线程放到该对象的等待池中。该线程等待其他线程将它唤醒。
notify()执行该方法的线程 唤醒在对象的等待池中等待的一个线程。
JVM随机从对象的等待池中选择一个线程 把他转到对象的锁池中。
sleep()
yield()
相同
都是Thread类的静态方法 会使当前处于运行状态的线程
放弃CPU 把运行机会让给其他线程
差异 1
sleep() 给其他线程机会 而不考虑线程优先级
yield() 只会给同等优先级 或者更高优先级的线程一个机会
差异 2
线程执行了sleep(long millis)方法后,转到阻塞状态
执行yield()方法后转到就续状态
差异 3
sleep() --- 抛出InterruptedException异常
yield() --- 不抛出任何异常
差异 4
sleep()方法移植性能好
yield()方法用途:测试期间人为提高程序并发性能 帮助查询
隐含错误