线程
一、进程与线程
进程就是正在运行的程序。线程是程序执行单元,执行路径,是程序使用cpu的最基本单位。其关系为
进程包含线程,进程是有若干线程组成。
1.多进程与多线程
多进程:就是可以在同一时间断内执行多个任务,也就是计算机即可以运行qq的同时,运行酷狗。
多线程:程序有多条执行路径,即执行qq聊天窗口,也可以打开新的窗口。
(1)多线程的意义
多线程是为了提高程序的使用效率,程序的执行就是抢占cpu的资源,cpu的执行权。多个进程是抢占这个资源,而其中 的某个一个继承如果执行路径比较多,就会有更高的几率抢占到CPU的执行权
二、线程
1、进程的创建
线程依赖于进程,所以必须创建进程,进程是系统已经创建好的。Java是不能直接调用系统功能的,
Java可以调用C/C++写好的程序来实现多线程的程序。
(1)线程的创建方式1
1.自定义一个类去继承于Thread
2.在自定义类重写run()方法
run()才是线程运行的核心方法
不是类中的所有代码都需要被线程所执行。
而这个时候,为了区分哪些代码能被线程所执行,java提供了Thread类中的run()用来包含那些线程执行的代码
3.创建对象
4.启动线程
例1:
package com.ThreadDemo1;
public class ThreadClass extends Thread{ //定义一个类继承Thread
private String name;
public ThreadClass() {
}
public ThreadClass(String name) {
this.name = name;
}
@Override
public void run() { //重写run方法
// TODO Auto-generated method stub
for (int i = 0; i < 30; i++) {
System.out.println(name+i); //此处的名字可以用Thread.currentThread():返回对当前正在执行的线程对象,也可以
// 用getName()
}
}
}
package com.ThreadDemo1;
public class ThreadDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadClass t=new ThreadClass("二狗"); //创建线程对象
t.start(); //线程执行
ThreadClass t1=new ThreadClass("狗蛋");
t1.start();
}
}
2、线程的优先级
(1)如何获取线程对象的优先级呢?
public final int getPriority():返回线程对象的优先级
如何设置线程对象的优先级呢?
public final void setPriority(int newPriority):更改此线程的优先级。
注意:线程的默认优先级:5
线程优先级的范围是:1-10
线程优先级高仅仅表示线程获取CPU时间片的几率高,但是要在次数比较多,或者多运行的时候才能看到比较效果
3、线程睡眠
线程失眠就是线程的暂停,也就是线程停止一段时间,然后再继续执行
例2:
package com.ThreadDemo2;
import java.util.Date;
public class ThreadSleep extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 30; i++) {
System.out.println(Thread.currentThread().getName()+i+new Date());
try {
Thread.sleep(2000); //线程等待两秒再继续执行
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
package com.ThreadDemo2;
public class ThreadDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadSleep sleep=new ThreadSleep();
sleep.setName("刘亦菲");
sleep.start();
}
}
5、线程加入
public final void join():等待这个线程死亡。 也就是说等待一个进程死亡之后,另外的进程才会执行
6、线程礼让
public static void yield():暂停当前正在执行的线程对象,并执行其他的线程。
让多个线程的执行更加和谐,但是不能保证一人一次,礼让应当写在run()里
7、后台线程
public final void setDaemon(boolean on):将此线程标记为 后台线程或用户线程。
当正在运行的线程都是守护线程,JVM退出。该方法必须在启动线程前调用,
守护线程,就是当主线程死了,子线程也活不了多久。
8、中断线程
public final void stop():让线程停止,过时了。但是还是可以用的。
public void interrupt():中断线程,把线程的状态终止,并抛出一个InterruptException.
例3:
package com.hwua.ThreadDemo2;
import java.util.Date;
public class ThreadStop extends Thread{
@Override
public void run() {
while(true){
System.out.println("开始执行:"+new Date());
//我要休息10秒钟,请不要打扰我
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
System.out.println("线程已经被停止了");
break;
}
}
System.out.println("结束执行:"+ new Date());
}
}
package com.hwua.ThreadDemo2;
/*
* 线程的休眠:线程的暂停
* */
public class ThreadSleepDemo {
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
Thread currentThread = Thread.currentThread();
ThreadSleep sleep = new ThreadSleep();
sleep.setName("刘亦菲");
sleep.start();
sleep.stop();
}
}
三、线程的创建方式2
(1)实现线程方式2:实现Runnable接口
步骤:
A.自定义类MyRunnable实现Runnable接口
B.重写run()方法
C.创建MyRunnable类的对象
D.创建Thread类的对象,并把步骤C的对象作为构造参数传递
例4:
package com.ThreadDemo4;
public class RunableDemo implements Runnable{ //自定义类实现Runnable接口
@Override
public void run() { //重写run()方法
// TODO Auto-generated method stub
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+i); //只能用Thread.currentThread()获得对象
}
}
}
package com.ThreadDemo4;
public class MyRunnble {
public static void main(String[] args) {
// TODO Auto-generated method stub
RunableDemo rd=new RunableDemo(); //创建对象
Thread t1=new Thread(rd,"杨过"); //把对象作为构造参数传入
t1.start();
}
}
练习
某电影院目前正在上映经典大片,共有100张票,而它有3个售票窗口,请设计一个程序模拟该电影院售票系统
例5
package com.hwua.ThreadDemo4;
public class SellTicket extends Thread{
private static int tickes=100;
//因为static所修饰的变量 只会初始化一次。
@Override
public void run() { //重写run()方法
// TODO Auto-generated method stub
while(true){
if(tickes>0){ //票还有剩余就可以购买
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickes--)+"张票"); //
}else{
break;
}
}
}
}
package com.hwua.ThreadDemo4;
public class SellTicketDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
SellTicket st1 = new SellTicket();
st1.setName("窗口1");
SellTicket st2 = new SellTicket();
st2.setName("窗口2");
SellTicket st3 = new SellTicket();
st3.setName("窗口3");
st1.start();
st2.start();
st3.start();
}
}
(2)、synchronized
同步代码是用于解决线程的安全问题,在有数据共享的时候,常会出现线程安全问题,这个时候就
需要同步代码块或者是 同步方法去实现线程的安全
1、会出现线程安全问题的条件
A 是否是多线程环境
B 是否有共享数据
C 是否有多条语句操作共享数据
为了解决线程安全的问题,引入了安全机制
第一种方法:同步代码块:
synchronized(对象){
//需要同步的代码
}
对象是随便创建一个对象
同步的代码是把多条语句操作共享数据的代码的部分包起来
需要注意的是:同步可以解决线程安全问题的根本原因就在那个对象上,该对象如同锁的功能。
多个线程必须是同一把锁。
例6:
package com.hwua.ThreadDemo5;
public class SellTicket implements Runnable{
private int tickes=1000;
private Object obj = new Object();
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
synchronized(this){ //同步代码块
if(tickes>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickes--)+"张票");
}else{
break;
}
}
}
}
}
package com.hwua.ThreadDemo5;
import com.hwua.ThreadDemo4.SellTicket3;
public class SellTicketDemo {
public static void main(String[] args) {
// TODO Auto-generated method stub
SellTicket ss = new SellTicket();
Thread t1 = new Thread(ss, "窗口1");
Thread t2 = new Thread(ss, "窗口2");
Thread t3 = new Thread(ss, "窗口3");
t1.start();
t2.start();
t3.start();
}
}
2、同步方法
- 同步方法的格式及锁对象问题?
把同步的关键字加在方法上
同步方法的对象是谁呢?
this.
(3)lock和unlock
Lock:
-
void lock():获取锁 上锁
-
void unlock():释放锁 放锁
- ReentrantLock 是Lock的实现类
(4)死锁
同步的弊端:
-
A.效率低
-
B.容易发生死锁
- 死锁:
-
两个或两个以上的线程在争夺资源的过程中,发生一种相互等待的现象。
(5)生产与消费的问题
- 资源类:Phone
- 生产类:SetPhone (生产者)
- 消费类:GetPhone (消费者)
- 测试类:PhoneDemo
(6)定时器
定时器是指在指定的时间做某一件事,还可以重复做某件事
定时器的类分别是Timer和Timertask两个类
-
schedule(TimerTask task, Date firstTime, long period) // Timertask:任务
从指定 的时间开始 ,对指定的任务执行重复的 固定延迟执行 。
void schedule(TimerTask task, long delay)
在指定的延迟之后安排指定的任务执行。
void schedule(TimerTask task, long delay, long period)
第一个参数:任务
第二个参数:延迟多少秒后执行任务
第三个参数:每隔多少秒后执行任务
void cancel()终止此计时器,丢弃任何当前计划的任务。