当遇见一个对象要做出多个动作,并且多个动作又是穿插到一起时,就要使用线程的概念来编写程序。
在网络编程中,网络上不同的用户操作一个对象时,也可以借助线程来完成程序。
要想理解线程,先来理解什么是进程。进程就是一个执行中的程序,每一个进程都有其独立的内存空
间和系统资源。大多数操作系统都支持多进程,但是实际上cpu在某个时刻,只能运行一个程序,即
一个进程。支持多进程,其实就是cpu在交替轮流执行多个程序,叫好像你可以一边听歌、一边上网。
线程是cpu调度和分派的基本单位,一个进程可以有多个线程组成,而这多个线程共享同一个存储空间
这使得线程间的通信比较容易。不用和切换进程一样,需要改变地址空间位置,在多线程的程序中,只
需要改变运行的顺序。多线程指单个程序可通过同时运行多个不同线程,以执行不同任务。
线程的创建:
1、通过Runnable接口
因为Runnable接口已经有了,可以直接实现接口就可以了
public class thread0 {
public static void main(String[] args) {
//创建对象c和c1
compute c = new compute();
compute1 c1 = new compute1();
//创建线程对象t和t1
Thread t = new Thread();
Thread t1 = new Thread();
t.start(); //启动线程对象t
t1.start(); //启动线程对象t1
}
}
//创建通过循环语句输出数字的类
class compute implements Runnable { //创建实现线程的类compute
int i = 0; //创建成员变量i
public void run() { //实现方法run()
for(int i = 0; i < 10; i++) {
System.out.println(i);
}
}
}
//创建通过循环语句输出数字的类
class compute1 implements Runnable { //创建实现线程的类compute1
public void run() { //实现方法run()
for(int i = 0; i < 10; i++) {
System.out.println("这个数字是:" + i);
}
}
}
2、通过继承Thread类
public class thread0 {
public static void main(String[] args) {
//创建对象c和c1
compute c = new compute();
compute1 c1 = new compute1();
//创建线程对象t和t1
Thread t = new Thread();
Thread t1 = new Thread();
t.start(); //启动线程对象t
t1.start(); //启动线程对象t1
}
}
//创建通过循环语句输出数字的类
class compute extends Thread { //创建继承线程的类compute
int i = 0; //创建成员变量i
public void run() { //实现方法run()
for(int i = 0; i < 10; i++) {
System.out.println(i);
}
}
}
//创建通过循环语句输出数字的类
class compute1 extends Thread { //创建继承线程的类compute1
public void run() { //实现方法run()
for(int i = 0; i < 10; i++) {
System.out.println("这个数字是:" + i);
}
}
}
上面的两个程序再运行一次的话结果可能会不一样,因为线程的抢占方式,在这个程序段里无法控制。
线程的优先级:
public final static int MIN_PRIORITY = 1,表示最低优先级
public final static int MAX_PRIORITY = 10,表示最高优先级
public final static int NORM_PRIORITY = 5,表示默认优先级
线程的休眠与唤醒:
休眠就是线程暂时停止运行,用sleep(millis毫秒)方法可以使线程在指定的时间内休眠
唤醒就是使线程从休眠进入可执行状态,用interrupt()方法来实现。
public class thread3 {
public static void main(String[] args) {
compute t = new compute();
t.start(); //启动线程t
t.interrupt(); //线程的唤醒
}
}
class compute extends Thread {
int i = 0;
public void run() {
System.out.println("在工作中,不要打扰");
try {
sleep(1000000);
} catch (Exception e) {
System.out.println("哦,电话来了");
}
}
}
如果没有interrupt()方法的话,程序在输出“在工作中,不要打扰”后休眠,一直到1000秒后,有的话
就会在输出“在工作中,不要打扰”后,立即会被唤醒输出“哦,来电话了”。
线程让步:就是是当前正在运行的线程对象退出运行状态,让其他线程运行,用yield()方法实现
线程同步:
同步块:使具有某个对象监视点的线程,获得运行权限的方法,每个对象只能在拥有这个监视点的情况下
才能获得运行权限。就好比四个人在一张桌子上吃饭,只有一个勺子,只有一个人可以吃饭,并
且这个人必须是拥有勺子的人。勺子就相当于监视点。
格式如下:synchronized(someobject)
{ 代码段 }
同步化方法:就是对整个方法进行同步。
格式如下:synchronized void f()
{ 代码 }
现在看这个例子:
一个汉堡店,有一个厨师和一个营业员,厨师负责做汉堡,营业员负责卖汉堡,还有个存放汉堡的箱子。
厨师不停的做汉堡,做好了就放在箱子里,当来客人,营业员就会从箱子里拿一个汉堡卖掉。假设前提
是客人每个一秒来一个,也就是营业员一秒买一个汉堡,而厨师3秒做一个汉堡。
class ham {
static Object box = new Object(); //创建对象box
static int totalmaterial = 10; //关于制作汉堡包的材料属性
static int sales = 0; //关于销售多少个汉堡属性
static int production = 5; //关于一共有多少个汉堡属性
}
class hmaker extends Thread { //厨师线程类
//make方法是用了一个同步块,在这个方法里会不断地成产汉堡
public void make() {
synchronized(ham.box) {
(ham.production)++;
try {
ham.box.notify();
} catch(Exception e) { }
}
}
public void run() { //重写run()方法
//使用循环语句保证在汉堡材料用完之前,不断地生产汉堡
while(ham.production < ham.totalmaterial) {
//使用判断语句判断只要有汉堡,厨师就通知营业员可以卖了
if(ham.production > 0) {
System.out.println("厨师" + getName() + ":" + "汉堡包来了(总共" + (ham.production -
ham.sales) + "个)");
}
try {
sleep(3000);
} catch(Exception e){ }
make();
}
}
}
class hassistant extends Thread { //关于营业员的线程类
public void sell() { //创建营业员卖汉堡的方法
if(ham.production == 0) {
System.out.println("营业员:顾客朋友们,请稍微等一下,汉堡包没了!!");
}
try {
ham.box.wait();
} catch(Exception e) { }
ham.sales++;
System.out.println("营业员:顾客好,汉堡包来了,(总共卖了" + ham.sales + "个)");
}
public void run() {
//当盒子里面有汉堡的情况下不断地卖
while(ham.sales < ham.production) {
try {
sleep(1000);
} catch(Exception e) { }
sell();
}
}
}
public class thread { //创建测试类
public static void main(String[] args) { //主方法
hmaker maker = new hmaker(); //创建对象maker
hassistant assistant = new hassistant(); //创建对象assistant
//对对象maker进行设置
maker.setName("甲");
//启动线程
maker.start();
assistant.start();
}
}