线程的创建和使用
一、基本概念
程序 - 进程 - 线程
程序:是为完成特定任务、用某种语言编写的一组指令的集合。
进程:是程序的一次执行过程,或是正在运行的一个程序
线程:进程可进一步细化为线程,是一个程序内部的一条执行路径
二、创建或使用线程
1)继承Thread类
创建线程的语法:
- 1)继承一个线程类;public class TortThread extends Thread
- 2)重写run()方法;
- 3)run()就是线程要执行的代码;
- 4)当run()方法执行完了,线程就结束了。
//创建兔子线程:
public class HareThread extends Thread {
@Override
public void run() {
// 兔子跑步的一个行为;
int hareStep = 0;
while (hareStep <5) {
hareStep += 2;
System.out.println("兔子跑了" + hareStep + "步");
}
}
}
//创建乌龟线程:
public class TortThread extends Thread {
@Override
public void run() {
//乌龟跑步的一个行为;
int tortStep=0;
while(tortStep<5){
tortStep+=1;
System.out.println("乌龟跑了"+tortStep+"步");
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
for(int i=0;i<5;i++){
System.out.println("主线程开始:");
}
TortThread tt=new TortThread();//创建线程对象
HareThread ht=new HareThread();//创建线程对象
tt.start();//启动线程;
ht.start();//启动线程;
for(int i=0;i<5;i++){
System.out.println("主线程结束:");
}
}
}
2)实现Runnable接口
创建线程的语法:
- 1)实现Runnable接口;public class HareRunnable implements Runnable
- 2)实现Runnable的run()方法;
- 3)run()就是线程要执行的代码;
- 4)当run()方法执行完了,线程就结束了。
- 5)先创建Runnable对象:TortRunnable tr=new TortRunnable();
-
再创建线程对象:Thread tt=new Thread(tr);
-
Runnable不是线程对象,而是线程的一个参数;
-
启动时要启动线程,不要启动Runnable:tt.start();
public class TortRunnable implements Runnable {
@Override
public void run() {
//乌龟跑步的一个行为;
int tortStep=0;
while(tortStep<5){
tortStep+=1;
System.out.println("乌龟跑了"+tortStep+"步");
}
}
}
public class HareRunnable implements Runnable {
@Override
public void run() {
// 兔子跑步的一个行为;
int hareStep = 0;
while (hareStep <5) {
hareStep += 2;
System.out.println("兔子跑了" + hareStep + "步");
}
}
}
public class Test {
public static void main(String[] args) {
TortRunnable tr=new TortRunnable();
Thread tt=new Thread(tr);
HareRunnable hr=new HareRunnable();
Thread ht=new Thread(hr);
tt.start();
ht.start();
}
}
3)常用方法
currentThread() 返回对当前正在执行的线程对象的引用。
getName() 返回此线程的名称。
setName(String name) 当前线程赋值线程名称
getPriority() 返回此线程的优先级。
setPriority(int newPriority) 设置线程的优先级。(1~10)由低到高,默认的优先级5
isAlive() 测试这个线程是否活着。
join() 参加 当前线程A中调用了B线程的join();A线程让出CPU资源给B线程,直到B线程结束
再去执行A线程的剩余内容
run() 启动线程调用的方法
sleep(long millis) 线程休眠 millis 毫秒
start() 导致此线程开始执行; Java虚拟机调用此线程的run方法。
yield() 当前线程会让出CPU资源(大公无私)。
三、继承方式和实现方式的联系与区别
两种方法的优劣势:
继承Thread类:就不能继承其他类了;但思路简单;
实现Runnable接口:还可以继承其他类;但设计复杂;实现多个线程访问同一资源的设计;
1)避免了单继承的局限性
2)多个线程可以共享同一个接口实现类的对象的资源,非常适合多个相同线程来处理同一份资源。
类似于示例中的:ticket共享资源
四、线程的生命周期(五个状态)Thread.state
新建 就绪 运行 ( 阻塞) 死亡
线程执行过程和生命周期:
1)新生状态:TortThread tt=new TortThread();
2)就绪状态:tt.start();
3)就绪状态《----》运行状态之间切换。
4)终止:当run()方法执行完了,线程就终止了。
/*
*1.sleep():Thread.sleep():哪个线程调用,哪个线程sleep();
- 线程进入阻塞状态:不抢占CPU了,释放CPU;
- 阻塞解除:进入就绪状态;
*2.yield():Thread.yield():哪个线程调用,哪个线程yield();
- 线程暂停执行;进入就绪状态;
*3.join():tt.join(),"tt"这个线程先执行完,在此期间"tt.join()"之后的主线程的代码就不执行了(主线程进入阻塞状态);
- 等tt执行完之后,主线程解除阻塞。
*/
注意:启动线程:tt.start();不要直接调用run()方法。
public class Test {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
System.out.println("主线程开始:");
}
TortThread tt = new TortThread();
HareThread ht = new HareThread();
tt.start();
ht.start();
try {
Thread.sleep();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for (int i = 0; i < 5; i++) {
System.out.println("主线程结束:");
}
}
}
//乌龟线程和兔子线程
public class HareThread extends Thread {
@Override
public void run() {
// 兔子跑步的一个行为;
int hareStep = 0;
while (hareStep < 5) {
hareStep += 2;
System.out.println("兔子跑了" + hareStep + "步");
}
}
}
public class TortThread extends Thread {
@Override
public void run() {
// 乌龟跑步的一个行为;
int tortStep = 0;
while (tortStep < 5) {
tortStep += 1;
System.out.println("乌龟跑了" + tortStep + "步");
}
}
}
五、同步锁同步方法
1.java中每一个对象都有自己的一把对象锁。
2.同步锁即同一对象的锁,也叫同步监视器。特点如下:
同步监视器
synchronized (obj){ }中的obj称为同步监视器
同步代码块中同步监视器可以是任何对象,但是推荐使用共享资源作为同步监视器
同步方法中无需指定同步监视器,因为同步方法的同步监视器是this,也就是该对象本事
优点:解决了线程安全问题
缺点:性能下降;会带来死锁
线程同步:synchronized关键字
*1.同步方法:public synchronized void changeBalance();
/*
*功能:4个窗口卖200张票(把同步代码块转成同步方法)
*
*分析:1.4个窗口是4个线程对象;
* 2.4个窗口卖票的行为是一样的,一个线程类即可;
* 3.4个窗口要访问的同一资源是:200张票;
* 4.200张票:设计实现Runnable接口;
*
*
*
*/
//设计票务资源;
public class TicketRunnable implements Runnable {
private int ticketNum = 20;// 四个窗口卖的总票数;
Object lock=new Object();
@Override
public void run() {
while (true) {
boolean flag=sellOne();//接收同步方法的返回值;
if(!flag){//判断返回值,确定是否结束循环。
break;
}
try {
Thread.sleep(5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//定义同步方法;
public synchronized boolean sellOne(){
if(ticketNum==0){
return false;
}
System.out.println(Thread.currentThread().getName() + ":卖出了第" + ticketNum + "张票");
ticketNum--;
return true;
}
}
//测试类
public class Test {
public static void main(String[] args) {
//创建票务资源;
TicketRunnable tr=new TicketRunnable();
//创建四个窗口;
Thread th1=new Thread(tr,"1号窗口");
Thread th2=new Thread(tr,"2号窗口");
Thread th3=new Thread(tr,"3号窗口");
Thread th4=new Thread(tr,"4号窗口");
th1.start();
th2.start();
th3.start();
th4.start();
}
}
*2.同步代码块:synchronized(this){需要同步的代码}
-
this:同步锁,线程得到同步锁才能执行同步代码块;执行完了,释放锁,其他线程可以获取该锁。
//设计票务资源;
public class TicketRunnable implements Runnable {
private int ticketNum = 20;// 四个窗口卖的总票数;
Object lock=new Object();
@Override
public void run() {
while (true) {
synchronized(this){
if(ticketNum==0){
break;
}
System.out.println(Thread.currentThread().getName() + ":卖出了第" + ticketNum + "张票");
ticketNum--;
}
try {
Thread.sleep(5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
//创建票务资源;
TicketRunnable tr=new TicketRunnable();
//创建四个窗口;
Thread th1=new Thread(tr,"1号窗口");
Thread th2=new Thread(tr,"2号窗口");
Thread th3=new Thread(tr,"3号窗口");
Thread th4=new Thread(tr,"4号窗口");
th1.start();
th2.start();
th3.start();
th4.start();
}
}
六、线程通信
wait() 与 notify() 和 notifyAll()
/*
*功能:生产者和消费者
*
*wait();释放锁,进入阻塞状态;
*notify();只唤醒一个等待本对象锁的线程;
*notifyAll();唤醒全部等待本对象锁的线程;
*
*
*/
public class ProduceThread extends Thread {
private SteamStack ss;
public ProduceThread() {
// TODO Auto-generated constructor stub
}
public ProduceThread(SteamStack ss) {
super();
this.ss = ss;
}
public void run() {
int i = 1;
while (i <= 10) {
synchronized (ss) {
// 1.判断SteamStack有没有产品;
// 2.如果没有,就生产;如果有,就不生产,放掉锁,进入阻塞状态;
while (ss.hasProduct) {
try {
ss.wait();// 释放锁;线程进入阻塞状态;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 生产;
System.out.println("生产第" + i + "个馒头");
i++;
ss.hasProduct = true;// 把状态设成true;
ss.notify();// 把现在正在等待对象锁的线程唤醒;
}
}
}
}
public class ProduceThread extends Thread {
private SteamStack ss;
public ProduceThread() {
// TODO Auto-generated constructor stub
}
public ProduceThread(SteamStack ss) {
super();
this.ss = ss;
}
public void run() {
int i = 1;
while (i <= 10) {
synchronized (ss) {
// 1.判断SteamStack有没有产品;
// 2.如果没有,就生产;如果有,就不生产,放掉锁,进入阻塞状态;
while (ss.hasProduct) {
try {
ss.wait();// 释放锁;线程进入阻塞状态;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 生产;
System.out.println("生产第" + i + "个馒头");
i++;
ss.hasProduct = true;// 把状态设成true;
ss.notify();// 把现在正在等待对象锁的线程唤醒;
}
}
}
}
public class SteamStack {
boolean hasProduct=false;//初始值是false,表示没有产品;
}
public class Test {
public static void main(String[] args) {
//馒头筐;
SteamStack ss=new SteamStack();
//生产线程和消费线程;
ProduceThread pt=new ProduceThread(ss);
ConsumeThread ct=new ConsumeThread(ss);
pt.start();
ct.start();
}
}