一、线程与多线程
主线程的特点:是产生其它子线程的线程,不一定是最后完成执行的线程。
在主线程中未创建其它线程---单线程
创建了其它线程---多线程(分解大任务)
java中有两种创建线程的方式
1、继承Thread类
重写run()方法
new一个线程对象
调用对象的start()启动线程
class Handler extends Thread{
public void run(){
//重写
}
public static void main(String[] args){
Thread thread = new Handler();
//创建线程对象
thread.start();//启动线程
}
}
编写简单,但是不可以再继承其它类
2、实现runnable接口
实现run()方法
创建一个runnable类的对象r,new MyRunnable()
创建Thread类对象并将Runnable对象作为参数,new Thread(r)
调用Thread对象start()启动线程
class Handler implements Runnable{
public void run(){
//方法实现
}
public static void main(String[] args){
Handler handler = new Handler();
Thread thread = new Thread(handler);
//创建线程对象
thread.start();//启动线程
}
}
只实现了runnable接口,还可以继承其它类,适合多个线程处理同一份资料
,需要访问当前线程,必须使用Thread.currentThread()
方法
•线程的名字,一个运行的线程总有一个名字,JVM给的名
字或者我们自定义的名字,通过setName方法设置
•获取当前线程对象的方法:Thread.currentThread()
•在一个程序里多个线程只能保证其开始时间,而无法保证
其结束时间,执行顺序也无法确定
•一个线程的run方法执行结束后,该线程结束
•一个线程只能被启动一次
•线程的调度是JVM的一部分,在一个CPU的机器上,一次
只能运行一个线程。JVM线程调度程序决定实际运行哪个
处于可运行状态的线程。采用队列形式
•start():启动线程,让线程从新建状态进入就绪队列排队
•run():线程对象被调度之后所执行的操作
•sleep():暂停线程的执行,让当前线程休眠若干毫秒
•currentThread():返回对当前正在执行的线程对象的引用
•isAlive():测试线程的状态,新建、死亡状态的线程返回
false
•interrupt():“吵醒”休眠的线程,唤醒“自己”
•yield():暂停正在执行的线程,让同等优先级的线程运行,
进入就绪状态
•join():当前线程等待调用该方法的线程结束后,再排队等待
CPU资源,进入阻塞状态
•stop():终止线程
线程睡眠,线程等待,线程阻塞都可以阻止线程执行。
•线程睡眠是帮助其他线程获得运行机会的最好方法
•线程睡眠到期自动苏醒,并返回到可运行状态,不是运行
状态
•sleep()中指定的时间是线程不会运行的最短时间。因此,
sleep()方法不能保证该线程睡眠到期后就开始执行
•sleep()是静态方法,只能控制当前正在运行的线程
//计数器,每隔一秒计数一次
public class MyThread extends Thread {
public void run() {
for (int i = 0; i < 100; i++) {
System.out.print(i);
try {
Thread.sleep(1000);
System.out.print(" 线程睡眠1000毫秒!\n");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
new MyThread().start();
}
线程的优先级:
多线程运行时,JVM的调度策略为按优先级调度,级别相
同时由操作系统按时间片来分配
–线程优先级通常表示为1~10的数字
•设置线程优先级
–线程默认优先级是创建它的执行线程的优先级
–通过Thread实例调用setPriority()方法设置线程优先级
•Thread.MIN_PRIORITY (1)
•Thread.NORM_PRIORITY (5)
•Thread.MAX_PRIORITY (10)
–通过Thread示例调用getPriority()方法得到线程多线程运行时,JVM的调度策略为按优先级调度,级别相
同时由操作系统按时间片来分配
多线程运行时,JVM的调度策略为按优先级调度,级别相
同时由操作系统按时间片来分配
–线程优先级通常表示为1~10的数字
•当线程池中线程都具有相同的优先级,调度程序的操作有
两种可能:
–一是选择一个线程运行,直到它阻塞或者运行完成为止
–二是时间分片,为池内的每个线程提供均等的运行机会
•线程礼让通过yield方法来实现,暂停当前正在执行的线程
对象,并执行同等优先级的其他线程Thread.yield();
–yield()将导致线程从运行状态转到就绪状态,有可能没有效果无法保证
yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中
线程离开运行状态的方法:
–调用Thread.sleep():使当前线程睡眠至少多少毫秒(尽管它可能在指定的
时间之前被中断)
–调用Thread.yield():不能保障太多事情,尽管通常它会让当前运行线程回
到就绪可运行性状态,使得有相同优先级的线程有机会执行
–调用join()方法:保证当前线程停止执行,直到调用join方法的线程完成为
止。然而,如果调用join的线程没有存活,则当前线程不需要停止
–线程的run()方法完成
进程同步
多线程程序在设计上最大的困难在于各个线程的控制流彼
此独立,使得各个线程之间的代码是乱序执行的,而且各
个线程共享资源,所以多线程会带来线程调度、同步、死
锁等一系列的问题
•线程同步 :当两个或两个以上线程访问同一资源时,需要
某种方式来确保资源在某一时刻只被一个线程使用
引入了对象互斥锁(类似操作系统的pv原语和信号量集)
–synchronized可以修饰实例方法,锁对象默认为当前对象this
–synchronized可以修饰静态方法,锁对象为类对象Class对象
–synchronized可以修饰修饰代码块,对给定对象加锁
注意:当多个线程共享一个资源的时候需要进行同步,但是过多的同步可能
导致死锁
生产者---消费者实例
class Producer implements Runnable{
SyncStack theStack;
public Producer(SyncStack s){ theStack = s; }
public void run(){
char c;
for(int i=0; i<20; i++){
c =(char)(Math.random()*26+'A');
theStack.push(c);
System.out.println("Produced: "+c);
try{
Thread.sleep((int)(Math.random()*100));
}catch(InterruptedException e){}
}
}
}
//
class Consumer implements Runnable{
SyncStack theStack;
public Consumer(SyncStack s){
theStack = s;
}
public void run(){
char c;
for(int i=0;i<20;i++){
c = theStack.pop();
System.out.println("Consumed: "+c);
try{
Thread.sleep((int)(Math.random()*1000));
}catch(InterruptedException e){}
}
}
}
//
public class SyncTest{
public static void main(String args[]){
SyncStack stack = new SyncStack();
Runnable source=new Producer(stack);
Runnable sink = new Consumer(stack);
Thread t1 = new Thread(source);
Thread t2 = new Thread(sink);
t1.start();
t2.start();
}
}
进程死锁
当两个或两个以上的线程在执行过程中,因争夺资源而造成
了互相等待,并且若无外力作用,它们都将无法推进下去的
现象称为系统处在死锁状态或系统产生了死锁
–资源占用是互斥的,当某个线程提出申请资源后,使得有关线程在无外力协
助下,永远分配不到必需的资源而无法继续运行
•产生死锁的必要条件
–互斥条件:指线程对所分配到的资源进行排它性使用
–请求和保持条件:指线程已经保持至少一个资源,但又提出了新的资源请求
–不可剥夺条件:线程已获得的资源,在未使用完之前,不能被剥夺,只能在
使用完时由自己释放
–环路等待条件:指在发生死锁时,必然存在一个线程—资源的环形链
•sleep和wait的区别
–sleep是定义在Thread上,wait是定义在Object上
–sleep可以使用在任意代码块,但是wait需要依赖synchronized关键字
–sleep不会释放锁, wait会释放锁
–sleep睡眠的时间到了之后,会自动唤醒,wait需要通过notify或notifyAll
方法来唤醒