程序是为了完成特定任务、用某种语言编写的一组指令的集合。即一段静态的代码,静态的对象。
进程是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程——声明周期。
线程,进程可以进一步细化为线程,是一个程序内部的一条执行程序。
一个Java应用程序其实至少有三个线程:main() 主线程、gc() 垃圾回收线程、异常处理线程。如果发生异常,会影响主线程。
并行:多个CPU同时执行多个任务。并发:一个CPU(采用时间片轮转)同时执行多个任务。
多线程程序的优点:
多线程的需求场景:
多线程的创建有两种方法:
两种方式的比较:开发中,优先选择实现 Runnable 接口的方式
原因:
Thread 类中的常用方法:
JDK 中用 Thread.State 类定义了线程的几种状态。Java语言使用 Thread 类及其子类的对象来表示线程,在它的一个完整的生命周期中通常要经历如下的五种状态:
线程状态转换图
其中 suspend() 和 resume() 已过时,这两个方法可能造成死锁
问题的提出:
synchronized(同步监视器) {
/*
需要被同步的代码
说明:
1.操作共享数据的代码,即为需要被同步的代码
2.共享数据:多个线程共同操作的变量。
3.同步监视器,俗称:锁。任何一个类的对象都可以来充当锁。
4.锁的要求:多个线程必须要共用同一把锁
5.对于实现类可以考虑用this(当前对象)来充当锁,也可以使用"类名.class"来充当锁
*/
}
如果操作共享数据的方法完整声明在一个方法中,可以将此方法声明成同步方法。
// 以多窗口买票为例
// 继承 Thread 类
class Test1 extends Thread {
private static int ticket = 100;
@Override
public void run() {
while(true) {
if(!sell()) {
break;
}
}
}
//正确写法
private static synchronized boolean sell() { //同步监视器为:Test1.class
//错误写法
//private synchronized boolean sell() 同步监视器为:this
if(ticket == 0) {
return false;
}
System.out.println("票号:" + ticket);
ticket--;
return true;
}
}
//实现 Runnable 接口
class Test2 implements Runnable {
private int ticket = 100;
@Override
public void run() {
while(true) {
if(!sell()) {
break;
}
}
}
private synchronized boolean sell() {
if(ticket == 0) {
return false;
}
System.out.println("票号:" + ticket);
ticket--;
return true;
}
}
总结:
举例:以多窗口卖票
class Window implements Runnable {
private int ticket = 100;
// 1. 实例化ReentrantLock
private ReentrantLock lock = new ReentrantLock(true);
@Override
public void run() {
while(true) {
try {
// 2.调用lock()
lock.lock();
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "卖票,票号:" + ticket);
ticket--;
} else {
break;
}
} finally {
lock.unlock();
}
}
}
}
什么是死锁?
解决方法
举例:使用两个线程交替打印1~100.
class Number implements Runnable {
private int number = 1;
@Override
public void run() {
while(true) {
synchronized (this) {
notify();
// notifyAll();
if(number <= 100) {
System.out.println(Thread.currentThread().getName() + " : " + number++);
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}
}
public class Test {
public static void main(String[] args) {
Number n = new Number();
Thread t1 = new Thread(n);
Thread t2 = new Thread(n);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
涉及到的三个方法:
说明:
面试题:wait()和sleep()的异同?
经典问题:生产者/消费者问题
// 简单实现
class Product {
private int productCount = 0;
// 生产产品
public synchronized void produceProduct() {
if (productCount < 20) {
productCount++;
System.out.println(Thread.currentThread().getName() + "生产第" + productCount + "个产品");
notify();
} else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 消费产品
public synchronized void consumeProduct() {
if(productCount > 0) {
System.out.println(Thread.currentThread().getName() + "消费第" + productCount + "个产品");
productCount--;
notify();
} else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer implements Runnable {
private Product product;
public Producer(Product product) {
this.product = product;
}
@Override
public void run() {
System.out.println("开始生产");
while(true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
product.produceProduct();
}
}
}
class Customer1 implements Runnable {
private Product product;
public Customer1(Product product) {
this.product = product;
}
@Override
public void run() {
System.out.println("开始消费");
while(true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
product.consumeProduct();
}
}
}
与Runnable相比,Callable功能更加强大
Future接口
实现方法
背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。
好处:
JDK5.0起提供了线程池相关的API:ExecurtorService和Executors
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
Executors: 工具类、线程池的工厂类,用于创建并返回不同类型的线程池
实现方法
原文:https://www.cnblogs.com/icehapi/p/14586492.html