线程的概念
线程也被称为轻型进程,而我觉得线程是一个带有入口的方法,必须来通过入口进行调用线程。(个人理解,不喜勿喷)
线程的创建
创建线程有两种方法,一是继承Thread,二是实现Runnable接口
首先介绍继承Thread 来创建线程,
Demo:
class add extends Thread{
public void run(){
int i;
for(i=0;i<2;i++)
System.out.println("Thread :"+i);
}
public static void main(String[]args){
int i;
add a=new add();
a.start();
}
}
}
第二种创建线程的方法:实现Runnable
Demo:
class add implements Runnable{
public void run(){
int i;
for(i=0;i<2;i++)
System.out.println("Thread :"+i);
}
public static void main(String[]args){
int i;
add a=new add();
new Thread(a).start();
}
}
线程的生命周期及调度
我查了网上好多的博客,各有各的说法,不过一般都是4个或5个,而且生命周期的说法不同,但英文是一样的,我就用书上看到的说法,分别是创建(new),就绪(runnable),运行(Running),阻塞(blocked),死亡(dead),5个生命状态。
1. 创建(new)
当用new 创建线程对象实例后,它作为一个实例存在,JVM并没有为其分配CPU时间片等资源
2. 就绪(runnable)
对创建(new)状态的线程调用start方法转换为就绪状态(runnable),线程获得除CPU时间片外的系统资源,等JVM的线程调度器按照线程优先级调度,从而使线程获得CPU时间片。运行状态的线程可以使用yield方法使线程进入就绪状态
3. 运行(Running)
线程获得CPU时间片后进入运行状态,运行run方法里的程序。
4. 阻塞(blocked)
阻塞指的是暂停一个线程来等待某个条件的发生(某资源的调度),调度机制不分给它任何CPU时间,直接跳过
5. 死亡(dead)
线程体运行结束或调用stop方法,线程终止,有JVM收回线程占用的资源。
(sleep,suspend,wait方法的区别
sleep ()使当前线程在指定的时间处于“非运行”(Not Runnable)状态。线程一直持有对象的监视器。比如一个线程当前在一个同步块或同步方法中,其它线程不能进入该块或方法中。如果另一线程调用了 interrupt ()方法,它将唤醒那个“睡眠的”线程。
注意:sleep ()是一个静态方法。这意味着只对当前线程有效,一个常见的错误是调用t.sleep (),(这里的t是一个不同于当前线程的线程)。即便是执行t.sleep (),也是当前线程进入睡眠,而不是t线程。
suspend ()是过时的方法,使用 suspend ()导致线程进入停滞状态,该线程会一直持有对象的监视器,suspend ()容易引起死锁问题。
wait ()使当前线程出于“不可运行”状态,和 sleep ()不同的是wait 是 object 的方法而不是 thread。调用 object.wait ()时,线程先要获取这个对象的对象锁,当前线程必须在锁对象保持同步,把当前线程添加到等待队列中,随后另一线程可以同步同一个对象锁来调用 object.notify (),这样将唤醒原来等待中的线程,然后释放该锁。基本上wait ()/notify ()与 sleep ()/interrupt ()类似,只是前者需要获取对象锁。
)
线程的调度
系统中只有1个CPU时以某种顺序在单CPU情况下执行多线程叫做调度。
JAVA采用了固定优先级调度的方法。该算法根据处于可运行态线程的优先级实行调度。
JAVA优先级分10个等级,分别用1到10数字表示
而设计优先级的方法为:thread.setPriority(int);
线程的互斥
假设银行总共有100元;而在2个窗口中同时有2个人来取100元;从道理来说,只能一个人取到,另一个人取不到,用线程来看:
Demo:
class add extends Thread{
static int count=100;
public void run(){
if(count>0){
try{
System.out.println("withdraw 100 success!");
count-=100;
System.out.println("count :"+count);
}catch(Exception e){
e.printStackTrace();
}
}
else {
System.out.println("failed to withdraw");
}
}
public static void main(String[]args){
add t1=new add();
add t2=new add();
t1.start();
t2.start();
}
}
然而结果却是:
withdraw100 success!
withdraw100 success!
count:0
count :-100
2个人都取出来了,银行总款为-100。线程t1在满足条件后,就被线程2打断,导致线程t2也进入判断的语句,从而导致2人都判定总额count大于0;这就是线程的互斥现象。
对于此问题,JAVA提供了一个互斥锁的机制,关键字synchronized;基本用法如下:
synchronized(互斥对象){
代码段
}
Demo:
class add extends Thread{
static int count=100;
public void run(){
synchronized (this)
{
if(count>0){
try{
System.out.println("withdraw 100 success!");
count-=100;
System.out.println("count :"+count);
}catch(Exception e){
e.printStackTrace();
}
}
else {
System.out.println("failed to withdraw");
}
}
}
public static void main(String[]args){
add t1=new add();
add t2=new add();
t1.start();
t2.start();
}
}
输出结果为:
withdraw100 success!
count:0
failed towithdraw
当线程t1检测synchronized代码段后,发现没有其他线程进入互斥锁中,线程t1获得该互斥锁,执行synchronized代码段,直到执行完毕释放互斥锁,当线程2发现其他线程拥有互斥锁执行代码时,会自动进入该互斥对象的等候队列,等待其他线程释放互斥锁。
synchronized 也可以用于方法前面,用法;public synchronized void withdraw()
它以该方法所在的对象为互斥对象,故不需要明确指明互斥对象
关于线程的同步,线程死锁以及线程池的内容,稍后更新。