任何的软件存储在磁盘(硬盘)中,运行软件的时候,OS(操作系统)使用IO技术,将磁盘中的软件的文件加载到内存,程序才能运行。 (进程是从硬盘到内存)
进程的概念 : 应用程序(typerpa,word,IDEA)运行的时候进入到内存,程序在内存中占用的内存空间(进程).
线程(Thread) : 在内存和CPU之间,建立一条连接通路,CPU可以到内存中取出数据进行计算,这个连接的通路,就是线程.
一个内存资源 : 一个独立的进程,进程中可以开启多个线程 (多条通路)
并发: 同一个时刻多个线程同时操作了同一个数据
并行: 同一个时刻多个线程同时执行不同的程序
今天之前的所有程序都有一个共性 : main启动之后,一条线走到底 (单线程)
一切都是对象,线程也是对象,Thread类是线程对象的描述类
实现线程程序的步骤 :
定义类继承Thread
子类重写方法run
创建子类对象
调用子类对象的方法start()启动线程
- //- 定义类继承Thread
- //- 子类重写方法run
- public class SubThread extends Thread {
- public void run(){
- for(int x = 0 ; x < 50 ;x++)
- System.out.println("run..."+x);
- }
- }
- public static void main(String[] args) {
- //创建线程程序
- SubThread subThread = new SubThread();
- //调用子类对象的方法start()启动线程
- //启动线程,JVM调用方法run
- subThread.start();
- for(int x = 0 ; x < 50 ;x++)
- System.out.println("main..."+x);
- }
此处重写了run()方法,但没有直接调用run()方法,这种方法被称为----------回调函数

Thread类的方法 getName()返回线程的名字,返回值是String类型
- public class ThreadName extends Thread {
- public void run (){
- System.out.println("线程名字:: "+ super.getName());
- }
- }
- public static void main(String[] args) {
- ThreadName threadName = new ThreadName();
- //threadName.setName("旺财");
- threadName.start();
-
- ThreadName threadName1 = new ThreadName();
- //threadName1.setName("小强");
- threadName1.start();
- }
Thread类静态方法 : Thread currentThread().getName()
静态调用,作用是返回当前的线程对象
"当前" 线程,( 当今皇上. 本地主机(谁运行谁就是本地主机) )
- //获取当前线程对象,拿到运行main方法的线程对象
- Thread thread = Thread.currentThread();
- System.out.println("name::"+thread.getName());
Thread类的方法 join()
解释,执行join()方法的线程,他不结束,其它线程运行不了
- public static void main(String[] args) throws InterruptedException {
- JoinThread t0 = new JoinThread();
- JoinThread t1 = new JoinThread();
-
- t0.start();
- t0.join();
- t1.start();
- }
Thread类的方法 static yield()
线程让步,线程把执行权让出
- public void run() {
- for(int x = 0 ; x < 50 ;x++){
- Thread.yield();
- System.out.println(Thread.currentThread().getName()+"x.."+x);
- }
- }
实现线程程序的步骤 :
定义类实现接口
重写接口的抽象方法run()
创建Thread类对象
Thread类构造方法中,传递Runnable接口的实现类对象
调用Thread对象方法start()启动线程
- //- 定义类实现接口
- // - 重写接口的抽象方法run()
- public class SubRunnable implements Runnable{
- @Override
- public void run() {
- for(int x = 0 ; x < 50 ;x++){
- System.out.println(Thread.currentThread().getName()+"x.."+x);
- }
- }
- }
- public static void main(String[] args) {
- //创建接口实现类对象
- Runnable r = new SubRunnable();
- //创建Thread对象,构造方法传递接口实现类
- Thread t0 = new Thread(r);
- t0.start();
-
- for(int x = 0 ; x < 50 ;x++){
- System.out.println(Thread.currentThread().getName()+"x.."+x);
- }
- }
接口实现好处是设计上的分离效果 : 线程要执行的任务和线程对象本身是分离的.
继承Thread重写方法run() : Thread是线程对象,run()是线程要执行的任务
实现Runnable接口 : 方法run在实现类,和线程无关,创建Thread类传递接口的实现类对象,线程的任务和Thread没有联系, 解耦合
出现线程安全的问题需要一个前提 : 多个线程同时操作同一个资源
线程执行调用方法run,同一个资源是堆内存的
火车票的票源是固定的,购买渠道在火车站买,n多个窗口
- /**
- * 票源对象,需要多个线程同时操作
- */
- public class Ticket implements Runnable {
-
- //定义票源
- private int tickets = 100;
-
- @Override
- public void run() {
- while (true) {
- if (tickets > 0) {
- try {
- Thread.sleep(10);//线程休眠,暂停执行
- }catch (Exception ex){}
- System.out.println(Thread.currentThread().getName()+" 出售第" + tickets + "张");
- tickets--;
- }else
- break;;
- }
- }
- }
- public static void main(String[] args) {
- Ticket ticket = new Ticket();
- //创建3个窗口,3个线程
- Thread t0 = new Thread(ticket);
- Thread t1 = new Thread(ticket);
- Thread t2 = new Thread(ticket);
-
- t0.start();
- t1.start();
- t2.start();
- }

线程的安全问题:多线程操作一个资源,就有可能会出现线程的安全问题
出现的原因:当一个线程还没有操作完成,另一个线程就开始操作了
解决线程的安全问题 : 当一个线程没有完成全部操作的时候,其它线程不能操作
同步代码块可以解决线程安全问题 : 格式 synchronized关键字
- synchronized(任意对象){
- //线程操作的共享资源
- }
任意对象 : 在同步中这个对象称为对象锁,简称锁,官方的稳定称为 对象监视器
同步代码块,如何保证线程的安全性.
同步代码块的执行原理 : 关键点就是对象锁
线程执行到同步,判断锁是否存在
如果锁存在,获取到锁,进入到同步中执行
执行完毕,线程出去同步代码块,将锁对象归还
线程执行到同步,判断锁所否存在
如果锁不存在,线程只能在同步代码块这里等待,锁的到来

使用同步 : 线程要先判断锁,然后获取锁,出去同步要释放锁, 增加了许多步骤,因此线程安全运行速度慢. 牺牲性能,不能牺牲数据安全
当一个方法中,所有代码都是线程操作的共享内容,可以在方法的定义上添加同步的关键字 synchronized , 同步的方法,或者称为同步的函数.
同步方法中有对象锁吗 , this对象
静态同步方法中有对象锁吗,锁对象是本类.class属性. 这个属性表示这个类的class文件的对象.
- @Override
- public void run() {
- while (true)
- sale();
- }
-
- private static synchronized void sale(){
- // synchronized (Ticket.class) {
- if (tickets > 0) {
- try {
- Thread.sleep(20);//线程休眠,暂停执行
- } catch (Exception ex) {
- }
- System.out.println(Thread.currentThread().getName() + " 出售第" + tickets + "张");
- tickets--;
- }
- // }
- }
类名.class 拿去class文件的对象 (栈内存、堆内存、元数据区)

死锁程序 : 多个线程同时争夺同一个锁资源,出现程序的假死现象
面试点 : 考察开发人员是否充分理解同步代码的执行原理
同步代码块 : 线程判断锁,获取锁,释放锁,不出代码,锁不释放
完成死锁的案例 : 同步代码块的嵌套
- public class ThreadDeadLock implements Runnable {
-
- private boolean flag;
-
- public ThreadDeadLock(boolean flag) {
- this.flag = flag;
- }
-
- @Override
- public void run() {
- while (true) {
- //同步代码块的嵌套
- if (flag) {
- //先进入A锁同步
- synchronized (LockA.lockA) {
- System.out.println("线程获取A锁" + Thread.currentThread().getName());
- //在进入另一个同步B锁
- synchronized (LockB.lockB) {
- System.out.println("线程获取B锁" + Thread.currentThread().getName());
- }
- }
- } else {
- //先进入b锁再进入a锁
- synchronized (LockB.lockB) {
- System.out.println("线程获取B锁" + Thread.currentThread().getName());
- synchronized (LockA.lockA) {
- System.out.println("线程获取A锁" + Thread.currentThread().getName());
- }
- }
- }
- }
- }
- }
- public class LockB {
- public static LockB lockB = new LockB();
- }
- public class LockA {
- public static LockA lockA = new LockA();
- }
- public class ThreadTest {
- public static void main(String[] args) {
- ThreadDeadLock threadDeadLock1 = new ThreadDeadLock(true);
- ThreadDeadLock threadDeadLock2 = new ThreadDeadLock(false);
-
- new Thread(threadDeadLock1).start();
- new Thread(threadDeadLock2).start();
- }
- }
JDK5新的特性 : java.util.concurrent.locks包. 定义了接口Lock.
Lock接口替代了synchronized,可以更加灵活 6以后优化了synchronized
Lock接口的方法
void lock() 获取锁
void unlock()释放锁
Lock接口的实现类ReentrantLock
- /**
- * 优化为juc包的接口Lock
- */
- public class Ticket implements Runnable {
-
- //定义票源
- private int tickets = 100;
- //获取Lock接口的实现类对象
- private Lock lock = new ReentrantLock();
-
- @Override
- public void run() {
- while (true)
- sale();
- }
-
- private void sale(){
- //获取锁
- lock.lock();
- if (tickets > 0) {
- try {
- Thread.sleep(20);//线程休眠,暂停执行
- } catch (Exception ex) {
- }
- System.out.println(Thread.currentThread().getName() + " 出售第" + tickets + "张");
- tickets--;
- }
- //释放锁
- lock.unlock();
- }
- }
创建2个线程,一个线程表示生产者,另一个线程表示消费者 生产一个消费一个
多线程操作一个资源(但是资源的操作方向是不同的,生产一个消费一个)
如何实现线程通信?(wait等待 ,notify唤醒)如何确定生产者生产了flag,如何确定消费者消费了flag,

- /**
- * 定义资源对象
- * 成员 : 产生商品的计数器
- * 标志位
- */
- public class Resource {
- int count ;
- boolean flag ;
- }
- /**
- * 生产者线程
- * 资源对象中的变量++
- */
- public class Produce implements Runnable {
-
- private Resource r;
-
- public Produce(Resource r) {
- this.r = r;
- }
-
- @Override
- public void run() {
- while (true){
- synchronized (r) {
- //是否要消费,判断标志位 ,允许消费才能执行
- if (!r.flag )
- //消费完成,不能再次消费,等待生产
- try{r.wait();}catch (Exception ex){}
- System.out.println("消费第" + r.count);
- //消费完成后,修改标志位,变成已经消费
- r.flag = false;
- //唤醒生产线程
- r.notify();
- }
- }
- }
- }
- /**
- * 消费者线程
- * 资源对象中的变量输出打印
- * */
- public class Customer implements Runnable{
-
- private Resource r;
-
- public Customer(Resource r) {
- this.r = r;
- }
-
- @Override
- public void run() {
- while (true){
- synchronized (r) {
- //是否要消费,判断标志位 ,允许消费才能执行
- if (!r.flag )
- //消费完成,不能再次消费,等待生产
- try{r.wait();}catch (Exception ex){}
- System.out.println("消费第" + r.count);
- //消费完成后,修改标志位,变成已经消费
- r.flag = false;
- //唤醒生产线程
- r.notify();
- }
- }
- }
- }
- public class ThreadTest {
- public static void main(String[] args) {
- Resource resource = new Resource();
- //接口实现类,生产,消费
- Produce produce = new Produce(resource);
- Customer customer = new Customer(resource);
- //创建线程
- new Thread(produce).start();
- new Thread(customer).start();
- }
- }
线程通信的方法 wait() notify()
方法的调用必须写在同步中
调用者必须是作为锁的对象
wait(),notify()为什么要定义在Object类
同步中的锁,是任意对象,任何类都继承Object
案例改为方法实现
- /**
- * 定义资源对象
- * 成员 : 产生商品的计数器
- * 标志位
- */
- public class Resource {
- private int count ;
- private boolean flag ;
-
- //消费者调用
- public synchronized void getCount() {
- //flag是false,消费完成,等待生产
- if (!flag)
- //无限等待
- try{this.wait();}catch (Exception ex){}
- System.out.println("消费第"+count);
- //修改标志位,为消费完成
- flag = false;
- //唤醒对方线程
- this.notify();
- }
- //生产者调用
- public synchronized void setCount() {
- //flag是true,生产完成,等待消费
- if (flag)
- //无限等待
- try{this.wait();}catch (Exception ex){}
- count++;
- System.out.println("生产第"+count+"个");
- //修改标志位,为生产完成
- flag = true;
- //唤醒对方线程
- this.notify();
- }
- }
- /**
- * 消费者线程
- * 资源对象中的变量输出打印
- */
- public class Customer implements Runnable{
- private Resource r ;
-
- public Customer(Resource r) {
- this.r = r;
- }
-
- @Override
- public void run() {
- while (true) {
- r.getCount();
- }
- }
- }
- /**
- * 生产者线程
- * 资源对象中的变量++
- */
- public class Produce implements Runnable{
-
- private Resource r ;
-
- public Produce(Resource r) {
- this.r = r;
- }
-
- @Override
- public void run() {
- while (true) {
- r.setCount();
- }
- }
- }