第一章 多线程概述
| 1 2 3 | 1. 什么是程序? 2. 什么是进程? 3. 什么是线程? | 
- 程序
| 1 | 是为完成特定任务、用某种语言编写的一组指令的集合(一段静态的代码) | 
- 进程
| 1 | 是程序的一次执行过程,或是正在运行的一个程序 | 
- 线程
| 1 2 3 | 进程可进一步细化为线程,是一个程序内部的一条执行路径 若一个程序可同一时间执行多个线程,我们称之为多线程. | 
- 多线程的使用
| 1 2 3 4 5 | 我们在什么情况下使用多线程呢? 1. 程序需要同时执行多个任务的时候(2个或2个以上) 2. 程序需要实现一些需要等待的任务时(例如360杀毒软件中有很多功能,但是这些功能都没有被调用,都在等待被调) 3. 后台运行的程序 | 
第二章 多线程创建和使用
| 1 2 3 | Java线程创建有两种方式 1. 一种是继承Thread类 2. 一种是实现Runnable接口 | 
第1节 继承Thread类
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public class TestDemo1 {
    public static void main(String[] args) {
        //创建线程
        MyThread mt = new MyThread();
        //启动线程
        mt.start();
    }
}
/**
 * 编写自定义类实现Thread类,重写run方法
 * run方法由jvm虚拟机调用,待抢到CPU资源之后才会执行
 */
class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("我被线程调用了...");
    }
}
 | 
第2节 实现Runnable接口
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class TestDemo2 {
    public static void main(String[] args) {
        //创建MyRunnable实例
        MyRunnable mr = new MyRunnable();
        //创建线程
        Thread t = new Thread(mr);
        //启动
        t.start();
    }
}
class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println("我被线程调用了...");
    }
}
 | 
第三章 线程的优先级
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /** * Thread类中自带优先级设置 最小优先级 */ public final static int MIN_PRIORITY = 1; /** * Thread类中自带优先级设置 默认优先级 */ public final static int NORM_PRIORITY = 5; /** * Thread类中自带优先级设置 最大优先级 */ public final static int MAX_PRIORITY = 10; | 
| 1 2 3 4 5 6 | Thread类中提供了设置和获取优先级的方法 setPriority(): 设置优先级(取值范围1-10) getPriority(): 获取优先级方法 在线程中设置线程优先级不会出现明显的变化,优先级高的话只是分配到CPU资源的概率高. | 
第四章 Thread类常用方法
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | start():线程启动方法 run(): 线程被调度时执行的方法 getName(): 返回线程名称 setName(String name): 设置线程名称 currentThread(): 返回当前线程 yield(): 线程让步 暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程 若队列中没有同优先级的线程,忽略此方法 线程让步,不代表暂停执行,就像你在高速开车,让了路,不代表车停下来,让给其他线程,CPU可能还会分配给它资源. join():当某个程序执行中调用其他线程的join()方法时,调用线程将被阻塞,直到join()方法加入的join线程执行完为止.低优先级的线程也可以获得执行 sleep(long millis): 令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队 stop(): 强制线程生命期结束(已过时) isAlive(): 返回boolean,判断线程是否还活着 | 
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | //join测试
public class TestDemo01 {
    public static void main(String[] args) throws InterruptedException {
        TaskRnnable t = new TaskRnnable();
        Thread t1 = new Thread(t, "线程1");
        Thread t2 = new Thread(t, "线程2");
        t1.start();
        t1.join();//join之后必须t1结束之后才能执行t2
        t2.start();
    }
}
class TaskRnnable implements Runnable{
    @Override
    public void run() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"执行了....");
    }
}
 | 
第五章 线程的生命周期
| 1 | 线程的生命周期状态: 创建 -- 就绪 -- 运行 -- 停止 -- 阻塞 | 

第六章 线程同步
| 1 2 3 4 5 6 7 8 9 | 因为多线程执行的不确定性引起执行结果的不稳定性,可能造成数据出现问题. Java中引入互斥锁的概念,来保证共享数据操作的完整性.每个对象都对应于一个可称为"互斥锁" 的标记;这个标记用来保证在任一时刻,只能有一个线程访问该对象. 在Java中使用synchronized关键字来实现互斥锁 1. 同步代码块 锁对象: 1.1 实例对象(锁实例数据) 1.2 静态对象(锁静态数据) 2. 同步方法 2.1 实例方法(锁为this) 2.2 静态方法(锁为当前类本身) | 
模拟火车站售票程序,开启三个窗口售票
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | //通过这个案例,多次执行会发现他的数据执行顺序是有问题的.
public class TestDemo3 {
    public static void main(String[] args) {
        //创建车票
        Ticket ticket = new Ticket();
        //创建线程并启动
        new Thread(ticket,"win1").start();
        new Thread(ticket,"win2").start();
        new Thread(ticket,"win3").start();
    }
}
class Ticket implements Runnable{
    private int ticket=100;
    @Override
    public void run() {
        while(true){
            if(ticket>0){
                System.out.println("窗口-> "+ Thread.currentThread().getName() +" <- 买票"+ticket+"剩余票数为:"+ --ticket);
            }else {
                break;
            }
        }
    }
}
 | 
解决问题
- 同步代码块
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | synchronized (锁对象){
	//需要锁定的资源
}
public class TestDemo3 {
    public static void main(String[] args) {
        //创建车票
        Ticket ticket = new Ticket();
        //创建线程并启动
        new Thread(ticket,"win1").start();
        new Thread(ticket,"win2").start();
        new Thread(ticket,"win3").start();
    }
}
class Ticket implements Runnable{
    private int ticket=100;
    @Override
    public void run() {
        while(true){
          	//锁定共享资源
            synchronized (this){
                if(ticket>0){
                    System.out.println("窗口-> "+ Thread.currentThread().getName() +" <- 买票"+ticket+"剩余票数为:"+ --ticket);
                }else {
                    break;
                }
            }
        }
    }
}
 | 
- 同步方法
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | //使用synchronized修饰操作共享资源的方法
public class TestDemo4 {
    public static void main(String[] args) {
        //创建车票
        Ticket1 ticket = new Ticket1();
        //创建线程并启动
        new Thread(ticket,"win1").start();
        new Thread(ticket,"win2").start();
        new Thread(ticket,"win3").start();
    }
}
class Ticket1 implements Runnable{
    private int ticket=100;
    @Override
    public void run() {
        while(true){
            sale();
        }
    }
  	//给操作共享资源的方法添加synchronized
    public synchronized void sale(){
        if(ticket>0){
            System.out.println("窗口-> "+ Thread.currentThread().getName() +" <- 买票"+ticket+"剩余票数为:"+ --ticket);
        }else{
            return;
        }
    }
}
 | 
第七章 死锁
| 1 | 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁 | 
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | public class DeadLock implements Runnable {
	private static Object obj1 = new Object();
	private static Object obj2 = new Object();
	private boolean flag;
	public DeadLock(boolean flag) {
		this.flag = flag;
	}
	@Override
	public void run() {	
		if(flag) {
			synchronized (obj1) {
				System.out.println(Thread.currentThread().getName()+"已经锁定obj1");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				//如果出现死锁当前执行不到
				synchronized (obj2) {
					System.out.println("一秒钟后"+Thread.currentThread().getName()+"已经锁定obj2");
				}
			}
		}else {
			synchronized (obj2) {
				System.out.println(Thread.currentThread().getName()+"已经锁定obj2");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				//如果死锁访问不到
				synchronized (obj1) {
					System.out.println("一秒钟后"+Thread.currentThread().getName()+"已经锁定obj1");
				}
			}
		}		
	}
}
//main方法
/**
 * 死锁
 *
 * obj1
 * obj2
 * 
 * A线程
 * run(){
 * 	synchronized(obj1){
 * 		//代码
 * 		synchronized(obj2){
 * 			//
 * 		}	
 * 	}
 * }
 * B线程
 * run(){
 * 	synchronized(obj2){
 * 		//代码
 * 		synchronized(obj1){
 * 			//
 * 		}	
 * 	}
 * }
 */
public class TestDemo01 {
	public static void main(String[] args) {
		//new Thread(new DeadLock(true)).start();
		new Thread(new DeadLock(false)).start();//死锁
		new Thread(new DeadLock(true)).start();
	}
}
 | 
第八章 线程通信
第1节 为什么要进行线程通信
| 多个线程并发执行时,在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务.并且我们希望他们有规律的执行,那么多线程之间需要一些协调通信 | 
第2节 Java语言实现通信的方式
| java.lang.Object类中提供了wait()/notify/notifyAll()方法实现线程之间的通信;这三个方法必须在synchronized方法或synchronized代码块中才能使用,否则会报java.lang.IllegalMonitorStateException异常 | 
- wait()方法
| 当前线程挂起并放弃CPU,释放对锁的拥有权(在wait时必须先获取锁,所以wait必须在synchronized中),同时在等待的位置加一个标志,以备后面被唤醒时它好能从标志位置获得锁的拥有权,变成就绪状态. | 
- notify()方法
| 1 | 唤醒一个等待当前对象的锁的线程 | 
- notifyAll()方法
| 1 | 方法会唤醒在此对象监视器上等待的所有线程 | 
第3节 练习(使用两个线程打印 1-100 线程1, 线程2 交替打印)
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | public class PrintNum implements Runnable{
	int num=1;
	@Override
	public void run() {
		while(true) {
			synchronized (this) {
				notify();//唤醒
				try {
					Thread.sleep(100);
				} catch (InterruptedException e1) {
					e1.printStackTrace();
				}
				if(num<=100) {
					System.out.println(Thread.currentThread().getName()+"-->"+ num);
					num++;
					try {
						//等待
						wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}			
				}else {
					break;
				}
			}
		}
	}
}
public static void main(String[] args) {
		/*
		 * 创建两个线程,循环1-100
		 */
		PrintNum p = new PrintNum();
		new Thread(p).start();
		new Thread(p).start();
	}
 | 
第九章 生产着消费者模式
- 容器
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | public class Box {
	//设置盒子容量
	private int c=0; //最大值10
	/**
	 * 向盒子中添加对象
	 */
	public synchronized void add() {
		if(c>=10) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}else {
			System.out.println("生产产品"+c);
			c++;
			notifyAll();
		}
	}
	/**
	 * 获取
	 */
	public synchronized void get() {
		if(c<=0) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}else {
			System.out.println("消费产品"+c);
			c--;
			notifyAll();
		}	
	}	
}
 | 
- 生产者
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /*
 * 生产线程
 */
public class Pro implements Runnable{
	Box box;
	public Pro(Box box) {
		this.box = box;
	}
	@Override
	public void run() {
		System.out.println("生产开始....");
		while(true) {
			try {
				Thread.sleep((int)Math.random()*1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}		
			box.add();
		}
	}
}
 | 
- 消费者
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /**
 * 消费线程
 */
public class Cus implements Runnable {
	Box box;
	public Cus(Box box) {
		this.box = box;
	}
	@Override
	public void run() {
		System.out.println("消费开始...");
		while(true) {
			try {
				Thread.sleep((int)Math.random()*1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}		
			box.get();
		}
	}
}
 | 
- 测试
| 1 2 3 4 5 6 7 8 9 10 11 12 | public static void main(String[] args) {
		//创建容器	
		Box box = new Box();
		//创建生产者
		Pro pro = new Pro(box);
  	//创建消费者
		Cus cus = new Cus(box);
		//给生产者分配线程
		new Thread(pro).start();
  	//给消费者分配线程
		new Thread(cus).start();
	} | 



















