目录java
进程(Process) 是程序的运行实例。例如,一个运行的 Eclipse 就是一个进程。进程是程序向操做系统申请资源(如内存空间和文件句柄)的基本单位。线程(Thread)是进程中可独立执行的最小单位。一个进程能够包含多个线程。进程和线程的关系,比如一个营业中的饭店与其正在工做的员工之间的关系。编程
在 Java 中实现多线程主要用两种手段,一种是继承 Thread 类,另外一种就是实现 Runnable
接口。(固然还有Callable和线程池)。下面咱们就分别来介绍这两种方式的使用,其余请关注此博客下文。安全
public class PrimeThread extends Thread{ //线程执行体 @Override public void run(){ for(int i = 0; i < 100; i++){ if(i % 2 == 0){ System.out.println(Thread.currentThread().getName() + "=" + i); } } } }
public class TestThread { public static void main(String[] args) { //新建一个线程 PrimeThread p1 = new PrimeThread(); //启动一个线程 p1.start(); PrimeThread p2 = new PrimeThread(); p2.start(); for(int i = 0; i < 100; i++ ){ if(i % 2 == 0){ System.out.println(Thread.currentThread().getName() + "=" + i); } } } }
public class Ticket implements Runnable{ private int ticket = 100; @Override public void run() { while(ticket > 0){ System.out.println(Thread.currentThread().getName() + "=" + --ticket); } } }
public class TestThread2 { public static void main(String[] args) { Ticket ticket = new Ticket(); //虽然是实现了Runnable接口 本质上只是实现了线程执行体 启动工做仍是须要Thread类来进行 Thread t1 = new Thread(ticket,"售票窗口一"); t1.start(); Thread t2 = new Thread(ticket,"售票窗口二"); t2.start(); Thread t3 = new Thread(ticket,"售票窗口三"); t3.start(); } }
两种实现方式的对比:多线程
1.从面向对象编程角度看:第一种建立方式(继承Thread类) 是一种基础继承的技术,第二种建立方式(以Runnable接口实例为构造器参数直接经过new建立Thread实例)是一种基础组合的技术。方式二不只会避免单继承的尴尬,也会下降类与类之间的耦合性。app
2.从对象共享角度看:第二种建立方式意味着多个线程实例能够共享同一个Runnable实例。而第一种方式则须要依赖static关键字来完成操做。ide
Java的调度方法this
同优先级线程组成先进先出队列(先到先服务),使用时间片策略操作系统
对高优先级,使用优先调度的抢占式策略线程
Thread类的相关方法3d
public class TestThread3 { public static void main(String[] args) throws Exception { Thread t1 = new Thread(new Runnable() { @Override public void run() { for(int i = 1; i < 100;i++){ if(i % 2 == 0){ System.out.println(Thread.currentThread().getName() + "=" + i); } } } },"线程1"); t1.start(); //线程1在 sleep以前就执行完了 t1.sleep(10000); //join方法 迫使t2 必须等线程1 执行完 才能执行 然而 t1输出完本身的 睡着了 t2被迫等了10秒 t1.join(); Thread t2 = new Thread(new Runnable() { @Override public void run() { for(int i = 1; i < 100;i++){ if(i % 2 != 0){ System.out.println(Thread.currentThread().getName() + "=" + i); } } } },"线程2"); t2.start(); } }
线程同步:模拟售票程序,实现三个窗口同时售票 100 张 (1.1案例)
问题:当三个窗口同时访问共享数据时,产生了无序、重复、超额售票等多线程安全问题
解决:将须要访问的共享数据“包起来”,视为一个总体,确保一次只能有一个线程执行流访问该“共享数据”
Java给上述问题提供了几种相应的解决方法
synchronized(同步监视器) {
//须要访问的共享数据
}
同步监视器 : 俗称“锁”,可使用任意对象的引用充当,注意确保多个线程持有同一把锁(同一个对象)
public class SafeTicket implements Runnable{ private int ticket = 100; @Override public void run() { while(true){ //使用同步代码块 synchronized (this) { if(ticket > 0){ System.out.println(Thread.currentThread().getName() + " 完成售票,余票:" + --ticket); } } } } }
同步方法 : 在方法声明处加 synchronized. 注意:非静态同步方法隐式的锁 ---- this
例如:
public synchronized void show(){}
public class SafeTicket implements Runnable{ private int ticket = 100; @Override public void run() { while(true){ //使用同步代码块 sale(); } } public synchronized void sale(){ if(ticket > 0){ System.out.println(Thread.currentThread().getName() + " 完成售票,余票:" + --ticket); } } }
同步锁 : Lock 接口
public class SafeTicket implements Runnable{ private int ticket = 100; private Lock l = new ReentrantLock(); @Override public void run() { while(true){ l.lock(); try { if(ticket > 0){ System.out.println(Thread.currentThread().getName() + " 完成售票,余票:" + --ticket); } } finally { l.unlock();//释放锁 } } } }
死锁 是指两个或两个以上的进程在执行过程当中,因为竞争资源或者因为彼此通讯而形成的一种阻塞的现象,若无外力做用,它们都将没法推动下去。此时称系统处于 死锁 状态或系统产生了 死锁 ,这些永远在互相等待的进程称为 死锁 进程
public class TestDeadLock { public static void main(String[] args) { final StringBuffer s1 = new StringBuffer(); final StringBuffer s2 = new StringBuffer(); new Thread() { public void run() { synchronized (s1) { s2.append("A"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (s2) { s2.append("B"); System.out.print(s1); System.out.print(s2); } } } }.start(); new Thread() { public void run() { synchronized (s2) { s2.append("C"); synchronized (s1) { s1.append("D"); System.out.print(s2); System.out.print(s1); } } } }.start(); } }
在 java.lang.Object 类中:
wait() : 使当前“同步监视器”上的线程进入等待状态。同时释放锁
notify() / notifyAll() : 唤醒当前“同步监视器”上的(一个/全部)等待状态的线程
注意:上述方法必须使用在同步中
场景1:使用两个线程打印 1-100 线程1和线程2交替打印
public class MyThread implements Runnable{ int i = 0; @Override public void run() { while(true){ synchronized (this) { this.notify(); if(i <= 100){ System.out.println(Thread.currentThread().getName() + "=" + i++); } try { this.wait(); } catch (InterruptedException e) { } } } } }
public class TestThread4 { public static void main(String[] args) { MyThread myThread = new MyThread(); Thread t1 = new Thread(myThread,"线程1"); Thread t2 = new Thread(myThread,"线程2"); t1.start(); t2.start(); } }
经典例题:生产者/消费者问题
public class TestProduct { public static void main(String[] args) { Clerk clerk = new Clerk(); Productor pro = new Productor(clerk); Customer cus = new Customer(clerk); new Thread(pro).start(); new Thread(cus).start(); } } // 店员 class Clerk { private int product; // 进货 public synchronized void getProduct() { if (product >= 20) { System.out.println("产品已满!"); try { wait(); } catch (InterruptedException e) { } } else { System.out.println("生产者生产了第" + ++product + " 个产品"); notifyAll(); } } // 卖货 public synchronized void saleProduct() { if (product <= 0) { System.out.println("缺货!"); try { wait(); } catch (InterruptedException e) { } } else { System.out.println("消费者消费了第" + --product + " 个产品"); notifyAll(); } } } // 生产者 class Productor implements Runnable { private Clerk clerk; public Productor() { } public Productor(Clerk clerk) { this.clerk = clerk; } public Clerk getClerk() { return clerk; } public void setClerk(Clerk clerk) { this.clerk = clerk; } @Override public void run() { while (true) { clerk.getProduct(); } } } // 消费者 class Customer implements Runnable { private Clerk clerk; public Customer() { } public Customer(Clerk clerk) { this.clerk = clerk; } public Clerk getClerk() { return clerk; } public void setClerk(Clerk clerk) { this.clerk = clerk; } @Override public String toString() { return "Customer [clerk=" + clerk + "]"; } @Override public void run() { while(true){ clerk.saleProduct(); } } }