1,线程概念
一个程序运行后至少有一个进程,一个进程中可以包含多个线程。
线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
2,线程调度
分时调度:所有线程轮流获取CPU的使用权,平均分配每个线程占用的CPU的时间。
抢占式调度:优先级高的线程先使用CPU,如果线程优先级相同,
3,主线程
java使用java.lang.Thread类代表线程,所有线程对象都必须是Thread类或其他子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来代表这段程序流。
4,实现线程的方式
public class xiancehng {
public static void main(String[] args) {
Thread thread = new SubThread();
thread.start();
}
}
//创建子线程
class SubThread extends Thread {
@Override
//重写run方法
public void run() {
Thread t = Thread.currentThread();
String name = t.getName();
for (int i = 0; i < 10; i++) {
System.out.println(name + i);
}
}
}
public class xiangcheng2 {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("hello word");
}
};
new Thread(runnable).start();
}
}
5,多线程原理
public class duoxiancheng1 {
public static void main(String[] args) {
System.out.println("这里是main线程");
Thread thread = new ZiThread("小强");
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println("旺财" + i);
}
}
}
class ZiThread extends Thread {
public ZiThread(String name) {
super(name);
}
@Override
public void run() {
Thread t = Thread.currentThread();
String name = t.getName();
for (int i = 0; i < 10; i++) {
System.out.println(name + i);
}
}
}
6,Thread类
构造方法:
public Thread(); 分配一个新的线程对象
public Thread(String name); 分配一个指定名字的新的线程对象
public Thread(Runnable target); 分配一个指定目标的新的线程对象
public Thread(Runnable target,String name); 分配一个指定目标的新的线程对象并指定名字
成员方法:
public String getName(); 获取当前线程名称
public void start(); 调用线程执行run方法
public void run(); 线程执行的任务在此处定义
public static void sleep(long millis); 当前正在执行的线程指定多少毫秒暂停
public static Thread currentThread(); 返回对当前正在执行的线程对象的引用
7,Thread和Runnable的区别
Runnable的优势:
1,适合多个线程操作同一个任务对象。
2,可以避免java中的单继承的局限性。
3,增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
4,线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。可以避免java中的单继承的局限性。
8,匿名内部类方式实现线程的创建
public static void main(String[] args) {
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
};
new Thread(r).start();
}
9,线程安全
案例:电影院要卖票,我们模拟电影院的卖票过程。假设要播放的电影是 “我和我的祖国”,本次电影的座位共100个(本场电影只能卖100张票)。
我们来模拟电影院的售票窗口,实现多个窗口同时卖 “小猪佩奇”这场电影票(多个窗口一起卖这100张票)需要窗口,采用线程对象来模拟;需要票,Runnable接口子类来模拟。
public class xcanq {
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(ticket, "窗口1").start();
new Thread(ticket, "窗口2").start();
new Thread(ticket, "窗口3").start();
}
}
class Ticket implements Runnable {
private int ticket = 100;
@Override
public void run() {
while (true) {
synchronized (this) {
if (ticket > 0) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String name = Thread.currentThread().getName();
System.out.println(name + "正在卖" + ticket + "张票");
ticket--;
}
}
}
}
}
10,Lock锁
使用方法:
public void lock();
public void unlock();
11,线程状态
NEW | 新建状态 | 一个线程创建以后,启动之前,就处于该状态。 |
TERMINATED | 消亡状态 | 线程执行完任务后,处于该状态。 |
RUNNABLE | 可运行状态 | 线程正在整型任务,就处于该状态。 |
BLOCKED | 阻塞状态 | 获取synchronized锁对象失败,处于该状态。 |
WAITING | 无限等待状态 | 获取Lock锁对象失败,就处于该状态。 |
TIMED_WAITING | 计时等待状态 | 执行sleep方法,就处于该状态。 |
12,sleep和wait的区别
13,wait和notify方法
(1)wait方法
线程不再活动,不再参与调度,进入 wait set 中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时的线程状态即是 WAITING。
它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中。
(2)notify方法
则选取所通知对象的 wait set 中的一个线程释放;例如,餐馆有空位置后,等候就餐最久的顾客最先入座。
(3)notifyAll方法
释放所通知对象的 wait set 上的全部线程。
注意:
(1)wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。
(2)wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。
(3)wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为:必须要通过锁对象调用这2个方法。
14,线程的通信
案例:
包子铺线程生产包子,吃货线程消费包子。当包子没有时(包子状态为false),吃货线程等待,包子铺线程生产包子(即包子状态为true),并通知吃货线程(解除吃货的等待状态),因为已经有包子了,那么包子铺线程进入等待状态。
接下来,吃货线程能否进一步执行则取决于锁的获取情况。如果吃货获
取到锁,那么就执行吃包子动作,包子吃完(包子状态为false),并通知包子铺线程(解除包子铺的等待状态),吃货线程进入等待。包子铺线程能否进一步执行则取决于锁的获取情况。
定义包子类:
public class Baozi {
String pier;
String xianer;
boolean flag = false;
}
定义吃货线程:
public class Chihuo extends Thread {
private Baozi bz;
public Chihuo(String name, Baozi bz) {
super(name);
this.bz = bz;
}
@Override
public void run() {
while (true) {
synchronized (bz) {
if (bz.flag == true) {
System.out.println("吃货正在吃"+bz.pier+bz.xianer+"包子");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
bz.flag = false;
bz.notify();
} else {
try {
bz.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
定义包子铺线程:
public class BaoZiPu extends Thread {
private Baozi bz;
public BaoZiPu(String name, Baozi bz) {
super(name);
this.bz = bz;
}
@Override
public void run() {
int count = 0;
while (true) {
synchronized (bz) {
if (bz.flag == true) {
try {
bz.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
System.out.println("包子铺开始做包子");
if (count % 2 == 0) {
bz.pier = "冰皮";
bz.xianer = "五仁";
} else {
bz.pier = "薄皮";
bz.xianer = "牛肉大葱";
}
count++;
bz.flag = true;
System.out.println("包子造好了:" + bz.pier + bz.xianer);
System.out.println("吃货来吃吧");
bz.notify();
}
}
}
}
}
测试线程:
public class Test {
public static void main(String[] args) {
Baozi bz = new Baozi();
Chihuo ch = new Chihuo("吃货", bz);
BaoZiPu bzp = new BaoZiPu("包子铺",bz);
bzp.start();
ch.start();
}
}
15,线程池概念
其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
16,线程池的好处
(1)降低资源消耗。减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
(2)提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
(3)提高线程的可管理性。可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。
17,线程池的使用
创建线程池:public static ExecutorService newFixedThreadPool(int nThreads)
使用线程池对象:public Future<?> submit(Runnable task)
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class xcc {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
MyRunnable r = new MyRunnable();
service.submit(r);
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("我要一个教练");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("教练来了: " + Thread.currentThread().getName());
System.out.println("教我游泳,交完后,教练回到了游泳池");
}
}