1.进程与线程
1.1 进程
进程是指一个动态的过程,它指的是从代码加载到执行完毕的一个完成过程。
特点:
(1)独立性:不同进程之间是独立的,相互之间资源不共享
(2)动态性:进程是一直活动的
(3)并发性:多个进程可以在单个处理器上同时执行,而且互不影响
1.2 线程
线程是一条执行路径,是进程的组成部分,一个进程可以有多个线程,每个线程去处理一个特定的子任务。
特点:
线程的执行时抢占式的,多个线程在同一个进程中可以并发的执行(其实就是CPU快速的在不同线程之间切换)
1.3 进程和线程的区别
(1)一个程序运行后至少有一个进程,一个进程至少有一个线程,否则这个进程是没有意义的
(2)进程之间不能共享资源 (独立),线程之间是抢占式,谁抢到CPU谁先执行
(3)系统创建进程需要为该进程重新分配资源,而创建线程容易的多,因此使用线程实现多任务并发比多进程的效率高
2.多线程
2.1 继承Thread类
(例)四个窗口各卖100张票:
public class TicktWindow extends Thread{
//票
private int ticket=100;
public TicktWindow(String name) {
//Thread类中有name属性
super(name);
}
//run实现卖票功能
@Override
public void run() {
while(true) {
if(ticket<1) {
break;//结束循环
}
System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票");
ticket--;
}
}
}
public class Demo {
public static void main(String[] args) {
TicktWindow w1=new TicktWindow("窗口1");
TicktWindow w2=new TicktWindow("窗口2");
TicktWindow w3=new TicktWindow("窗口3");
TicktWindow w4=new TicktWindow("窗口4");
//启动线程
w1.start();
w2.start();
w3.start();
w4.start();
}
}
2.2 实现Runnable接口
(例)四个窗口一起卖100张票
public class Ticket implements Runnable{
//票
private int ticket=100;
/**
* 卖票功能
*/
@Override
public void run() {
while(true) {
if(ticket<1) {
break;
}
System.out.println(Thread.currentThread().getName()+"卖第"+ticket+"张票");
ticket--;
}
}
}
public class Demo2 {
public static void main(String[] args) {
//创建票对象
Ticket ticket=new Ticket();
//创建窗口
Thread w1=new Thread(ticket, "窗口1");
Thread w2=new Thread(ticket, "窗口2");
Thread w3=new Thread(ticket, "窗口3");
Thread w4=new Thread(ticket, "窗口4");
//启动线程
w1.start();
w2.start();
w3.start();
w4.start();
}
}
注意:这里会出现多线程同步问题(四个窗口都卖同一张票),原因是上一个线程还没执行完下一个线程就已经开始执行了
(例)男友朋友存钱取钱问题
同理:有可能输出结果是女朋友先取钱,实际上是男朋友线程存钱还没结束,女朋友线程就抢到CPU进来执行
而且也有可能是女朋友线程一直在取钱
2.3 两种方法实现方式的比较
(1)两中方式获取线程名字的方式:Thread.currentThread().getName
(2)继承Thread类的方式:没有资源共享,编写简单
弊端:已经继承了Thread类,不能再继承其他类
(3)实现Runnable接口的方式:适合多个线程共享同一个资源的情况;资源类实现Runnable接口(四个窗口一起卖票问题),如果资源类有多个操作,需要把功能提出来,单独实现Runnable接口(男女朋友存钱取钱问题)
2.4 使用Callable接口实现多线程
public class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int sum=0;
for(int i=0;i<=100;i++) {
sum+=i;
}
System.out.println(Thread.currentThread().getName()+"计算完毕");
return sum;
}
}
public class Demo {
public static void main(String[] args) throws Exception {
//1创建Callable对象
MyCallable callable=new MyCallable();
//2把Callable变成任务 (未来的任务)
FutureTask<Integer> task=new FutureTask<>(callable);
//3创建线程对象
Thread thread=new Thread(task, "future1");
//4执行
thread.start();
//5获取执行结果
Integer sum=task.get();
System.out.println(sum);
}
}
3. 线程常用方法
3.1 设置获取线程的名称
设置:setName("xxx");
获取:super.getName();
Thread.currentThread().getName();
3.2 线程休眠(sleep)
public class SleepThread extends Thread{
@Override
public void run() {
for(int i=0;i<20;i++) {
System.out.println(Thread.currentThread().getName()+"....."+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class Demo {
public static void main(String[] args) {
SleepThread sleepThread=new SleepThread();
sleepThread.start();
}
}
3.3 设置优先级(setPriority)
可以通过设置优先级来改变线程抢到时间片的概率,优先级高的线程获得较多的执行机会
默认情况下,每个线程都与创建他的父线程具有相同的优先级
优先级范围:1~10,默认为5,对应数值越大,说明优先级越高
public class PriorityThread extends Thread{
public PriorityThread(String name) {
super(name);
}
@Override
public void run() {
for(int i=0;i<100;i++) {
System.out.println(Thread.currentThread().getName()+"----------------"+i);
}
}
}
public class Demo {
public static void main(String[] args) {
PriorityThread p1=new PriorityThread("线程1");
PriorityThread p2=new PriorityThread("线程2");
PriorityThread p3=new PriorityThread("线程3");
//设置优先级
p1.setPriority(1);
p3.setPriority(10);
//启动
p1.start();
p2.start();
p3.start();
}
}
3.4 合并(加入)线程(join)
在执行原来线程的过程中,如果遇到了合并线程,则优先执行合并进来的线程
public class JoinThread extends Thread{
@Override
public void run() {
for(int i=0;i<50;i++) {
System.out.println(Thread.currentThread().getName()+"------------"+i);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Demo {
public static void main(String[] args){
JoinThread joinThread=new JoinThread();
joinThread.start();
//加入线程
try {
joinThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//阻塞当前线程(主线程),等到子线程执行完,才继续执行
for(int i=0;i<20;i++) {
System.out.println("主线程..........."+i);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3.5 后台线程(setDaemon)
线程分为前台(用户)线程和后台(守护)线程
后台线程:隐藏起来一直默默运行的线程,直到进程结束,又称守护线程(JVM的垃圾回收线程)
public class DaemonThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName()+"---"+i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class Demo {
public static void main(String[] args) throws Exception {
DaemonThread daemonThread=new DaemonThread();
//设置线程为后台线程
daemonThread.setDaemon(true);
daemonThread.start();
for(int i=0;i<20;i++) {
System.out.println("主线程............."+i);
Thread.sleep(100);
}
}
}
3.6 线程让步(yield)
让正在执行的线程暂停,但它不会阻塞该线程,他只是将该线程转入就绪状态
实际上,当某个线程调用了yield方法暂停之后,只有优先级与它相同或比他高的线程才会获得执行的机会
public class YieldThread extends Thread{
public YieldThread(String name) {
super(name);
}
@Override
public void run() {
for(int i=0;i<1000;i++) {
System.out.println(Thread.currentThread().getName()+"=="+i);
if(i==50) {
Thread.yield();//暂停线程执行,让给其他优先级相同或高的线程执行
}
}
}
}
public class Demo {
public static void main(String[] args) {
YieldThread y1=new YieldThread("线程1");
YieldThread y2=new YieldThread("线程2");
y1.start();
y2.start();
}
}
3.7 线程中断()
程序在等待过程中,可以使用interrupt方法打断
public class InterruptThread extends Thread {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始执行了...");
try {
Thread.sleep(20000);
System.out.println(Thread.currentThread().getName()+"自然醒了");
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"被打醒了...");
}
System.out.println(Thread.currentThread().getName()+"执行完毕...");
}
}
public class Demo {
public static void main(String[] args) throws Exception{
InterruptThread interruptThread=new InterruptThread();
interruptThread.start();
System.out.println("20秒输入任意字符,结束子线程");
System.in.read(); //会停止,等到键盘录入
interruptThread.interrupt();//打断子线程
System.out.println("主线程结束了");
}
}
4. 线程的生命周期
对于线程,当线程被创建,进入新生状态,调用start()方法 ,进入就绪状态(可执行状态),如果抢到cpu,进入运行状态,运行过程中出现了阻塞,进入阻塞状态,如果程序正常结束进入死亡状态。
五状态生命周期:
七状态生命周期