------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------
一个进程中可以运行多个线程。线程,有时被称为轻量级进程,是程序执行流的最小单元。
使用Thread类或者Runnable接口编写代码来定义、实例化和启动新线程。二、实例化线程
1、如果是扩展java.lang.Thread类的线程,则直接new即可。
2、如果是实现了java.lang.Runnable接口的类,则用Thread的构造方法:
Thread(Runnable target)
Thread(Runnable target, String name)
Thread(ThreadGroup group, Runnable target, String name)
Thread(ThreadGroup group, Runnable target, String name, long stackSize)
三、启动线程
在线程的Thread对象上调用start()方法,而不是run()或者别的方法。
在调用start()方法之前:线程处于新状态中,新状态指有一个Thread对象,但还没有一个真正的线程。
在调用start()方法之后:发生了一系列复杂的事情
启动新的执行线程(具有新的调用栈);
该线程从新状态转移到可运行状态;
当该线程获得机会执行时,其目标run()方法将运行。
注意:对Java来说,run()方法没有任何特别之处。像main()方法一样,它只是新线程知道调用的方法名称(和签名)。因此,在Runnable上或者Thread上调用run方法 是合法的。但并不启动新的线程。
Java线程:线程状态的转换
四、线程状态
线程的状态转换是线程控制的基础。线程状态总的可分为五大状态:分别是生、死、可运行、运行、等待/阻塞。
1、新状态:线程对象已经创建,还没有在其上调用start()方法。
2、可运行状态:当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。当start()方法调用时,线程首先进入可运行状态。在线程运行之后或 者从阻塞、等待或睡眠状态回来后,也返回到可运行状态。
3、运行状态:线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。
4、等待/阻塞/睡眠状态:这是线程有资格运行时它所处的状态。实际上这个三状态组合为一种,其共同点是:线程仍旧是活的,但是当前没有条件运行。换句话说,它 是可运行的,但是如果某件事件出现,他可能返回到可运行状态。
5、死亡态:当线程的run()方法完成时就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。如果在一个死 去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
五、阻止线程执行
对于线程的阻止,考虑一下三个方面,不考虑IO阻塞的情况:睡眠,等待;
1、睡眠
Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)静态方法强制当前正在执行的线程休眠(暂停执行),以“减慢线程”。当线程睡眠时,它入睡在某个 地 方,在苏醒之前不会返回到可运行状态。当睡眠时间到期,则返回到就绪状态。
注意:
1、线程睡眠是帮助所有线程获得运行机会的最好方法。
2、线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就 开始执行。
3、sleep()是静态方法,只能控制当前正在运行的线程。
4、线程睡眠不丢失任何监视器的所属权。
2、中断线程
1、interrupt()方法只是设置了线程的中断状态为true,并没有真正中断线程。要在catch里再次中断。Thread.currentThread.interrupt()
2、自定义flag=true
如果当前线程没有中断自己,该线程checkAccess方法会被调用,这可能抛出SecurityException。
3、设置优先级
t.setPriority(Thread.MAX_PRIORITY);
t.setPriority(Thread.MIN_PRIORITY);
t.setPriority(Thread.NORM_PRIORITY);
4、当程序中没有用户线程的时候所以守护线程都会自动终止!
t.setDaemon(true/false)
5、static void yield()暂停当前正在执行的线程对象,并执行其他线程。
六、同步
解决数据共享问题,必须使用同步。
synchronized(要同步的对象/this){要同步的操作}
public synchronized void method(){要同步的操作},同步当前对象(this)
准则:1使代码块保持简短。2不要阻塞。3在持有锁的时候,不要对其他对象调用方法。
七、同步和锁定
1、锁的原理
Java中每个对象都有一个内置锁
当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行代码类的当前实例(this实例)有关的锁。获得一个对象的锁也称为获取锁、锁定对象、在对 象上锁定或在对象上同步。
当程序运行到synchronized同步方法或代码块时才该对象锁才起作用。
一个对象只有一个锁。所以,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。
释放锁是指持锁线程退出了synchronized同步方法或代码块。
关于锁和同步,有一下几个要点:
1)、只能同步方法,而不能同步变量和类;
2)、每个对象只有一个锁;当提到同步时,应该清楚在什么上同步?也就是说,在哪个对象上同步?
3)、不必同步类中所有的方法,类可以同时拥有同步和非同步方法。
4)、如果两个线程要执行一个类中的synchronized方法,并且两个线程使用相同的实例来调用方法,那么一次只能有一个线程能够执行方法,另一个需要等待,直到锁被 释放。也就是说:如果一个线程在对象上获得一个锁,就没有任何其他线程可以进入(该对象的)类中的任何一个同步方法。
5)、如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。
6)、线程睡眠时,它所持的任何锁都不会释放。
7)、线程可以获得多个锁。比如,在一个对象的同步方法里面调用另外一个对象的同步方法,则获取了两个对象的同步锁。
8)、同步损害并发性,应该尽可能缩小同步范围。同步不但可以同步整个方法,还可以同步方法中一部分代码块。
9)、在使用同步代码块时候,应该指定在哪个对象上同步,也就是说要获取哪个对象的锁。
八、经典案例--生产者与消费者
package com.vince.xiancheng;
public class DemoJin {
public static void main(String[] args){
Food f=new Food();
Productor p=new Productor(f);
Consumer c=new Consumer(f);
new Thread(p).start();
new Thread(c).start();
}
}
class Productor implements Runnable{
private Food food;
public Productor(Food f){
this.food=f;
}
public void run() {
for (int i = 0; i < 500; i++) {
if(i%2==0){
food.set("A", 100.0);
}else{
food.set("B", 001.0);
}
}
}
}
class Consumer implements Runnable{
private Food food;
public Consumer(Food f){
this.food=f;
}
public void run() {
for (int i = 0; i < 100; i++) {
food.get();
}
}
}
class Food{
private String name;
private double price;
private boolean flag=true;//t表示可以生产,f表示可以消费
public synchronized void set(String name,double price){
if(!flag){
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
this.setName(name);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setPrice(price);
flag=false;
this.notify();
}
public synchronized void get(){
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName()+":"+this.getPrice());
flag=true;
this.notify();
}
public Food() {
super();
}
public Food(String name, double price) {
super();
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String toString() {
return "food [name=" + name + ", price=" + price + "]";
}
}
九、线程池
package com.vince.xiancheng;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class xianchengchi {
public static void main(String[] args){
// ExecutorService es=Executors.newSingleThreadExecutor();
// 创建一个单线程的线程池
// ExecutorService es=Executors.newFixedThreadPool(2);
// 创建一个多线程的线程池
// ExecutorService es=Executors.newCachedThreadPool();
// 创建一个可缓存的线程池
ExecutorService es=Executors.newScheduledThreadPool(2);
// 创建一个大小无限的线程池
T1 t1=new T1();
T1 t2=new T1();
es.execute(t1);
es.execute(t2);
}
}
class T1 implements Runnable{
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--"+i);
}
}
}
class T2 implements Runnable{
public void run() {
}
}