前言
程序:一组有序的指令集合
进程:执行中的程序
线程:是进程中“单一持续控制流程”
进程跟程序的区别:程序是一组指令的集合,它是静态的实体,没有执行的含义。而进程是一个动态的实体,有自己的生命周期。一般说来,一个进程肯定与一个程序相对应,并且只有 一个,但是一个程序可以有多个进程,或者一个进程都没有。除此之外,进程还有并发性和交往性。简单地说,进程是程序的一部分,程序运行的时候会产生进程。
进程与线程的区别:进程作为资源分配的单位,线程是调度和执行的单位
线程状态
新生状态、就绪状态、阻塞状态、运行状态、死亡状态
多线程的实现
Thread
PS:无法实现线程之间的数据共享
/** * 通过 extends Thread */ public class MyThread extends Thread{ private final String name; public MyThread(String name){ this.name = name; } @Override public void run(){ for (int i = 0; i < 5; i++) { System.out.println(this.name + ":" + i); } } }
public static void main(String[] args) { MyThread myThread = new MyThread("MyThread-0"); myThread.start(); }
Runnable
/** * 通过 implements Runnable */ public class MyRunnable implements Runnable{ private final String name; public MyRunnable(String name){ this.name = name; } @Override public void run(){ for (int i = 0; i < 5; i++) { System.out.println(this.name + ":" + i); } } }
public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable("MyRunnable-0"); new Thread(myRunnable).start(); }
Callable<V>
/** * 通过 implements Callable<V> */ public class MyCallable implements Callable<String> { private final String name; public MyCallable(String name){ this.name = name; } @Override public String call() throws Exception { for (int i = 0; i < 5; i++) { System.out.println(this.name + ":" + i); } return "执行完成"; } }
public static void main(String[] args) { MyCallable myCallable = new MyCallable("MyCallable-0"); new Thread(new FutureTask<String>(myCallable)).start(); }
线程池
public static void main(String[] args) { MyThread myThread = new MyThread("MyThread-0"); MyRunnable myRunnable = new MyRunnable("MyRunnable-0"); MyCallable myCallable = new MyCallable("MyCallable-0"); //线程池 ExecutorService threadPool = Executors.newFixedThreadPool(10); threadPool.submit(myThread); threadPool.submit(myRunnable); threadPool.submit(myCallable); //关闭线程池 threadPool.shutdown(); }
并发安全
synchronized
使用synchronized关键字对线程加锁,在有线程获取该内存锁后,其它线程无法访问该内存,从而实现JAVA中简单的同步、互斥操作。
同步代码块
synchronized(Obj){//obj叫做同步监听器,只能是引用类型 }
同步方法
public synchronized void fun(){//同步方法中,默认使用this当前对象作为同步对象 }
Lock
从jdk1.5之后,java提供了另外一种方式来实现同步访问,那就是Lock。
//可重入锁 Lock lock = new ReentrantLock(); //加锁 lock.lock(); try{ //并发处理逻辑... }catch(Exception ex){ }finally{ //释放锁 lock.unlock(); }
老公、老婆同时取钱例子:
/** * 银行账户 */ public class Account { //余额 private int balance = 1000; public Account() { } public Account(int balance) { this.balance = balance; } public int getBalance() { return balance; } public void setBalance(int balance) { this.balance = balance; } }
使用synchronized
/** * 通过 implements Runnable */ public class MyRunnable implements Runnable{ private final String name; private final Account account; public MyRunnable(Account account,String name){ this.account = account; this.name = name; } @Override public void run(){ for (int i = 0; i < 5; i++) { //加锁,锁的是银行账户对象 synchronized (account) { //余额大于200 if (account.getBalance() >= 200) { //模拟耗时 try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } //取钱成功 account.setBalance(account.getBalance() - 200); System.out.println(this.name + "取走了200元,当前余额为:" + account.getBalance()); } else { System.out.println(this.name + "取钱失败,余额不足"); } } } } }
使用Lock
/** * 通过 implements Runnable */ public class MyRunnable implements Runnable{ private final String name; private final Account account; //可重入锁 private static final Lock lock = new ReentrantLock(); public MyRunnable(Account account,String name){ this.account = account; this.name = name; } @Override public void run(){ for (int i = 0; i < 5; i++) { //加锁 lock.lock(); try{ //余额大于200 if (account.getBalance() >= 200) { //模拟耗时 try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } //取钱成功 account.setBalance(account.getBalance() - 200); System.out.println(this.name + "取走了200元,当前余额为:" + account.getBalance()); } else { System.out.println(this.name + "取钱失败,余额不足"); } }catch(Exception e){ e.printStackTrace(); }finally{ //释放锁 lock.unlock(); } } } }
测试
public static void main(String[] args) { //初始化银行账户 Account account = new Account(); //老公、老婆同时取钱 new Thread(new MyRunnable(account,"老公")).start(); new Thread(new MyRunnable(account,"老婆")).start(); }
如果不加锁,就会出现并发安全问题
死锁
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程
死锁的发生必须具备以下四个必要条件。
1)互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
2)请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
3)不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
4)环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。
后记
Thread.sleep():使线程进入阻塞状态,会释放CPU资源但不会释放对象锁的控制
Object.wait():当前线程必须拥有此对象监视器。该线程释放对此监视器的所有权(释放synchronize锁)并等待,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。然后该线程将等到重新获得对监视器的所有权后才能继续执行。
Thread.sleep()与Object.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于Object.wait()在释放CPU同时,释放了对象锁的控制