进程是一个静态的概念。
线程是一个进程里面的不同的执行路径。
在java技术中,线程通常是抢占式的而不需要时间片分配进程(分配给每个线程相等的CPU时间的进程)。抢占式调度模型就是许多线程处于可以运行状态(等待状态),但实际上只有一个线程在运行。该线程一直运行到它终止进入可运行状态(等待状态),或者另一个具有更高优先级的线程变成可运行状态。在后一种情况下,低优先级的线程被高优先级的线程抢占,高优先级的线程获得运行的机会。Java线程调度器支持不同优先级线程的抢先方式,Java线程调度器本身不支持相同优先级线程的时间片轮换。它依赖于Java运行时系统所在的操作系统:如果操作系统支持时间片的轮换,则线程调度器就支持相同优先级线程的时间片轮换。
在同一个时间点,一个cpu只能支持一个线程。Java运行时系统实现了一个用于调度线程执行的线程调度器,用于确定某一时刻由哪一个线程在CPU上运行。
线程的启动必须使用Thread类的start()方法。不能在主线程main里直接调用run()。
创建线程的方法:
1. 定义线程实现Runnable接口
2. 从Thread类继承。
class Runner1 implements Runnable
class Runner1 extends Thread
启动线程的方式:
1. 通过Thread类
Thread t = new Thread(r);
t.start();
如果是使用class Runner1 extends Thread 来创建线程,可以通过如下方法来启动线程。因为只有Thread类有start方法。
2. 直接Runner1 r = new Runner1();
r.start();
3. 通过Thread类 (父类指向子类的对象)
Thread t = new Runner1();
t.start();
sleep()会抛出interruptedException 必须try catch。
可以用interrupt打断。
使用join()的时候,相当于方法调用。也就是说,使用了join方法的时候,先执行完使用了join的线程。相当于是说,使用了join线程的优先级最高。必须等到使用了join的线程执行完。join()同样会抛出interruptedException。必须try catch。
yield()就是执行到yield的时候让出一下cpu时间,但是只让出一次。
线程同步:线程同步的含义就是在于,不同的线程要访问同一个对象,这部分被称为critical sections。
同步有两种方式:同步块和同步方法
Synchronized可以锁定当前对象。一个线程已经进入到被锁定的区域,不可以有别的线程进入。
解决死锁的方式:把锁的粒度加粗一些。也就是说,尽量锁定一个对象,不要锁定两个对象。
Synchronized锁定的是代码,而不是某个变量。也就是说,别的线程依然可以访问这个变量,只是不能通过锁定的代码部分对变量进行值的改变。但是可以通过别的方法改变变量的值。
锁定问题:加了锁定,效率变低。不加锁定,可能会出现数据不一致的错误。
程序中让线程的终止,一般不用stop方法,常用的是设置一个flag变量,然后结合interrupt()方法。
如果线程已经运行起来,又阻塞了,为了让线程停止:
首要考虑采用join()方法,正常停止。
再考虑使用exception的方式停止。
生产者,消费者问题。其实就是一个堆栈问题当中用到了wait,notify,notifyAll。
有生产者,负责生产。
消费者,负责消费。
//每个生产者或者消费者都是一个线程。
//主类。
publicclass ProducerConsumer {
publicstaticvoid main(String[] args) {
SyncStack ss = new SyncStack();
Producer p = new Producer(ss);
Consumer c = new Consumer(ss);
//三个生产者,一个消费者
new Thread(p).start();
new Thread(p).start();
new Thread(p).start();
new Thread(c).start();
}
}
//产品
class WoTou {
int id;
WoTou(int id) {
this.id = id;
}
public String toString() {
return"WoTou :" + id;
}
}
//堆栈类
class SyncStack {
//栈顶指针
int index = 0;
//栈数组,大小为6
WoTou[] arrWT = new WoTou[6];
//压栈由生产者调用。必须是同步方法。
publicsynchronizedvoid push(WoTou wt) {
//满了,栈顶指针指向栈最顶部。就阻塞。
//必须是同步方法才可以wait。如果方法没有注明synchronized,会编译出错。
while(index == arrWT.length) {
try {
//this是当前对象正在访问的线程,wait
//wait必须被人唤醒,和sleep不一样,自己醒不了。
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//唤醒消费者的线程
this.notifyAll();
//压栈
arrWT[index] = wt;
//指针上升
index ++;
}
//出栈由消费者调用。必须是同步方法。
publicsynchronized WoTou pop() {
//空了,栈顶指针指向栈最底部。就阻塞。必须是同步方法才可以wait。
//必须用while,不能用if
while(index == 0) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notifyAll();
//指针下移
index--;
//拿到产品
return arrWT[index];
}
}
//生产者实现runnable
class Producer implements Runnable {
SyncStack ss = null;
Producer(SyncStack ss) {
this.ss = ss;
}
publicvoid run() {
//每个人最多生产20个馒头
for(int i=0; i<20; i++) {
//生产一个产品,并指定一个id
WoTou wt = new WoTou(i);
//生产好了以后压栈
ss.push(wt);
System.out.println("生产了:" + wt);
try {
//随机睡眠一段时间
//控制睡眠的时间
Thread.sleep((int)(Math.random() * 200));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//消费者
class Consumer implements Runnable {
SyncStack ss = null;
Consumer(SyncStack ss) {
this.ss = ss;
}
publicvoid run() {
//每个人最多消费20个馒头
for(int i=0; i<20; i++) {
//消费就是出栈
WoTou wt = ss.pop();
System.out.println("消费了: " + wt);
try {
//随机睡眠一段时间
//控制睡眠的时间
Thread.sleep((int)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}