程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一 段静态的代码,静态对象。
进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态 的过程:有它自身的产生、存在和消亡的过程。——生命周期
如:运行中的QQ,运行中的MP3播放器
程序是静态的,进程是动态的 进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。
若一个进程同一时间并行执行多个线程,就是支持多线程的
线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开 销小
一个进程中的多个线程共享相同的内存单元/内存地址空间它们从同一堆中分配对象,可以 访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资 源可能就会带来安全的隐患。
- 关系
进程可以细化为多个线程
每个线程拥有自己独立的:栈区和程序计数器
多个线程共享一个程序中结构:方法区、堆
2.使用多线程的优点背景:以单核CPU为例,只使用单个线程先后完成多个任务(调用多个方 法),肯定比用多个线程来完成用的时间更短,为何仍需多线程呢? 多线程程序的优点: 1. 提高应用程序的响应。对图形化界面更有意义,可增强用户体验。 2. 提高计算机系统CPU的利用率 3. 改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和 修改
3.线程的使用 继承Thread类的方式
实现Runnable接口的方式
3.1方式1 Thread方式1) 定义子类继承Thread类。
2) 子类中重写Thread类中的run方法。
3) 创建Thread子类对象,即创建了线程对象。
4) 调用线程对象start方法:启动线程,调用run方法。
- 注意点:
1. 如果自己手动调用run()方法,那么就只是普通方法,没有启动多线程模式。
2. run()方法由JVM调用,什么时候调用,执行的过程控制都有操作系统的CPU 调度决定。
3. 想要启动多线程,必须调用start方法。
4. 一个线程对象只能调用一次start()方法启动,如果重复调用了,则将抛出以上 的异常“IllegalThreadStateException”。
- 代码演示
public class demoCreat {
public static void main(String[] args) throws InterruptedException {
// 3.创建thread类
MyThread myThread = new MyThread();
MyThread myThread1 = new MyThread();
myThread.setPriority(Thread.MAX_PRIORITY);
myThread1.setPriority(Thread.MIN_PRIORITY);
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
// myThread.run();//切换运行,发现线程的main和Mythread名称都是一致的
myThread.start();
myThread.setName("我的线程000");
// myThread.run();//错误
myThread1.start();
myThread1.setName("我的线程111");
//main线程调度
Thread.currentThread().setName("main线程");
System.out.println("myThread存活状态:"+myThread.isAlive());
// myThread1.join();//表示main线程阻塞,其他线程交替执行
for (Integer i=0;i<100;i++){
if (i%2==0) System.out.println(i+":"+Thread.currentThread().getName());
}
}
}
//1.创建 一个集成于thread类
class MyThread extends Thread{
public MyThread(String name) {
super(name);
}
public MyThread() {
}
@Override
public void run() {
for (Integer i=0;i<100;i++){
// try {
// sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
if (i%2==0) System.out.println(i+":"+Thread.currentThread().getName());
}
}
}
3.2Runnable方式
1) 定义子类,实现Runnable接口。
2) 子类中重写Runnable接口中的run方法。
3) 通过Thread类含参构造器创建线程对象。
4) 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
5) 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。
代码演示
package com.dfp.dome1;
public class RunnableThread {
public static void main(String[] args) {
RThread rThread=new RThread();
Thread thread=new Thread(rThread);
thread.start();
}
}
class RThread implements Runnable{
@Override
public void run() {
for (Integer i=0;i<100;i++){
if (i%2==0) System.out.println(i+":"+Thread.currentThread().getName());
}
}
}
3.3 抢票案例演示
public class WindowsDome {
public static void main(String[] args) {
DomeRunnable domeRunnable=new DomeRunnable();
Thread t1=new Thread(domeRunnable);
Thread t2=new Thread(domeRunnable);
Thread t3=new Thread(domeRunnable);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class DomeRunnable implements Runnable{
private Integer tick=100;
@Override
public void run() {
while (true){
if (tick>0){
System.out.println(Thread.currentThread()
.getName()+"售出车票为:"+tick);
tick--;
}else {
break;
}
}
}
}
结果
我们发现有个问题 票号100的同时被消费了。但是实际情况是不允许的
如果我们让窗口的线程沉睡了,还会出现-1号的票号问题(小概率概率出现)
4.线程的生命周期 4.1生命周期的转换 5.线程同步1. 多线程出现了安全问题
2. 问题的原因: 当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有 执行完,另一个线程参与进来执行。导致共享数据的错误。
3. 解决办法: 对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以 参与执行。
5.1线程同步的方式 5.2 同步锁- 同步机制中的锁
在《Thinking in Java》中,是这么说的:对于并发工作,你需要某种方式来防 止两个任务访问相同的资源(其实就是共享资源竞争)。 防止这种冲突的方法 就是当资源被一个任务使用时,在其上加锁。第一个访问某项资源的任务必须 锁定这项资源,使其他任务在其被解锁之前,就无法访问它了,而在其被解锁 之时,另一个任务就可以锁定并使用它了。
synchronized的锁是什么?
任意对象都可以作为同步锁。所有对象都自动含有单一的锁(监视器)。
同步方法的锁:静态方法(类名.class)、非静态方法(this) 同步代码块:自己指定,很多时候也是指定为this或类名.class
注意:
必须确保使用同一个资源的多个线程共用一把锁,这个非常重要,否则就 无法保证共享资源的安全
一个线程类中的所有静态方法共用同一把锁(类名.class),所有非静态方 法共用同一把锁(this),同步代码块(指定需谨慎)
5.3 方式一同步代码块演示
public class WindowsDome {
public static void main(String[] args) {
DomeRunnable domeRunnable=new DomeRunnable();
Thread t1=new Thread(domeRunnable);
Thread t2=new Thread(domeRunnable);
Thread t3=new Thread(domeRunnable);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class DomeRunnable implements Runnable{
private Integer tick=100;
Object object=new Object();//用做锁的对象,注意定义的位置保证唯一性
@Override
public void run() {
while (true){
//设置同步代码块,并且我们的DomeRunnable之创建一次意味着 objec对象也是创建一次。这么说在3个线程中object是唯一的
synchronized (object){
if (tick>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread()
.getName()+"售出车票为:"+tick);
tick--;
}else {
break;
}
}
}
}
}
5.4 同步方法演示
修改如下
run方法中调用show方法
5.5 继承Thread的方式实现同步因为通过继承thread的方式我们实现线程,这个方式创建一个线程就会创建3个类对于我的锁的机制而言就会冲突。我们主要修改一下这几个地方
将我们的锁设置为静态数据这样就是唯一的
或者在我们的同步方法加上静态
public class WindowsDomeThread {
public static void main(String[] args) {
WindowExThread t1=new WindowExThread();
WindowExThread t2=new WindowExThread();
WindowExThread t3=new WindowExThread();
t1.setName("t1窗口");
t2.setName("t2窗口");
t3.setName("t3窗口");
t1.start();
t2.start();
t3.start();
}
}
class WindowExThread extends Thread{
private static Integer tick =100;
private static Object lock=new Object();//静态
@Override
public void run() {
while (true){
// synchronized (lock){
//
// if (tick>0){
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread()
// .getName()+"售出车票为:"+tick);
// tick--;
// }else {
// break;
// }
// }
show();
}
}
private static synchronized void show(){
if (tick>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread()
.getName()+"售出车票为:"+tick);
tick--;
}
}
}
线程执行同步代码块或同步方法时,程序调用Thread.sleep()、 Thread.yield()方法暂停当前线程的执行 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程 挂起,该线程不会释放锁(同步监视器)。
应尽量避免使用suspend()和resume()来控制线程



