一、线程与进程
1.进程:正在执行的程序(QQ.exe),表示一个可执行程序一次的执行过程。
2.进程的三种基本状态。
(1).就绪状态:
当进程分配到了所需要的所有资源后,等待CPU的资源时(表示具有的cpu的执行资格,但还没有获得cpu的执行权),此时只要获得了cpu的资源后,便可执行,这样的进程状态就称为就绪状态。通常一个系统中就绪状态的进程有多个,通常他们会排成一个队列,成为就绪队列。
(2)执行状态:
就绪队列中进程已经获得了cpu的资源,拿到了cpu的执行权,通过处理机调度,该进程就可以执行了。在单处理机系统中,只有一个进程处于执行状态;多处理机系统中,可以有多个进程处于执行状态。
(3)阻塞状态:
正在执行的进程因某些其他事情暂时无法继续执行时候,(通常是正在执行的进程遇到了I/O请求时候),便进入阻塞状态。
进程三种状态转换图:
3.线程:
通俗理解就是:每一个进程在执行的时候,可以做很多不同的事情,那每一件事都是由一个线程来执行的任务,这就是线程。
(1).线程的创建方法
-a .继承Thread类,重写run()方法。
// 自定义类继承Thread public class MyThread extends Thread { // 使用带名称的构造方法 public MyThread(String name) { // 调用父类的构造方法 super(name); } // 重写run方法 public void run() { for (int i = 0; i < 10; i++) { System.out.println(this.getName() + ":" + i); } } }
// 测试 public class ThreadTest { public static void main(String[] args) { MyThread myThread1 = new MyThread("线程1"); // 启动线程 myThread1.start(); MyThread myThread2 = new MyThread("线程2"); // 启动线程 myThread2.start(); } }
-b.实现Runnable接口,重写run()方法。
// 自定义类实现Runnable接口 public class MyRunnable implements Runnable { // 重写run方法 @Override public void run() { for (int i = 0; i < 10; i++) { // 获得当前线程的名称 System.out.println(Thread.currentThread().getName() + ":" + i); } } } public class RunnableTest { public static void main(String[] args) { MyRunnable myRunnable1 = new MyRunnable(); MyRunnable myRunnable2 = new MyRunnable(); // 调用相应的构造方法,指定线程名 Thread thread1 = new Thread(myRunnable1, "线程1"); // 启动线程 thread1.start(); // 调用相应的构造方法,指定线程名 Thread thread2 = new Thread(myRunnable2, "线程2"); // 启动线程 thread2.start(); } }
-c.匿名内部类形式,直接new一个Thread,里面重写run()方法。或者new一个Thread,里面再new一个Runnable,重写run()方法。
ThreadInner:
public class ThreadInner { public static void main(String[] args) { // 使用匿名内部类实现线程执行具体内容 new Thread("新线程") { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(this.getName() + ":" + i); } } // 启动线程 }.start(); } }
RunnableInner :
public class RunnableInner { public static void main(String[] args) { // 实例化线程 new Thread(new Runnable() { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } } // 启动线程 }, "新线程").start(); } }
(2).线程的常用方法:
1).设置线程名称
setName(String name);
Thread(String name);
2).设置线程休眠状态
sleep(long millis):此时线程会进入阻塞状态,时间到了自动唤醒就绪执行。
3).等待某一线程终止后,再执行其他线程
join()方法,线程开启后,调用该方法的线程,会是的其他线程等待它执行完毕后再执行其他的。
4).设置线程的优先级
setPriority(int newPriority) 参数范围:1-10,在开启线程之前执行(调用start()之前)。但是这个仅仅是提升线程获得cpu时间片的概率,并不能一定保证优先执行。
5).设置后台线程
setDaemon(booolean on): 后台线程也称守护线程,比如jvm的垃圾回收线程,在开启线程之前执行。设置完毕后,表示该线程会随着前台线程都死亡后,会自动死亡。
(3).java中的线程的生命周期。
- 新生(创建):实例化一个线程对象。
- 就绪:准备执行了,暂时还没有分配到cpu的时间片(cpu执行权)
- 运行:分配到了时间片,执行相关任务。
- 阻塞:由于某些原因导致暂时不能继续执行下去,此时进入阻塞,等待某些原因解除后,可以进入就绪状态
- 消亡:线程结束(stop方法可以强制结束,不建议)
线程状态图:
4.多线程同步问题
当我们多个线程去访问同一个资源的时候,如果不同步,就会出现多个线程访问的资源并不一致的情况。比如售卖火车票,如果仅仅有100张票,在不同步的情况下,A B C D 同时进来看都有100张,每人都可以买100张,那实际就需要400张来满足需求,实际情况并不能这样子。所以需要同步(加锁)。在A进来的时候,B是不可以进来的。只有等A完毕后,资源释放了其他的才可以进来。
代码:
public class Ticket implements Runnable { // 使用private定义竞争资源 private int ticket = 100; @Override public void run() { while (true) { // 对当前线程进行同步加锁 synchronized (this) { ticket--; // 当车票售空时跳出循环 if (ticket < 0) { break; } System.out.println(Thread.currentThread().getName() + "购买,当前剩余票数:" + ticket); } } } }
public class TicketTest { // 模拟两名旅客的抢票 public static void main(String[] args) { Ticket ticket = new Ticket(); Thread thread1 = new Thread(ticket,"旅客1"); Thread thread2 = new Thread(ticket,"旅客2"); thread1.start(); thread2.start(); } }
经典案例 生产者-消费者问题 值得考虑。