进程和线程以及线程池(一)
千山鸟飞绝,万径人踪灭 是一种境界
进程与线程
线程:要说线程,我们得先知道另一个该类,进程 线程要依赖于进程
进程:正在执行的程序,我们现在的电脑支持多进程
单核CPU 在某个时间点上,能执行几个进程,只能执行一个,我们的CPU会在多个进程间进行告诉的切换执行,你是感觉不出来的
多进程的意义:提高CPU的使用率
当你的进程开启之后,要执行很多任务,每一个任务,每一个要执行的任务, 我们就叫做线程
多线程的意义:提高程序的使用率
大家注意两个词汇的区别:并行和并发。
前者是逻辑上同时发生,指在某一个时间段同时运行多个程序。
后者是物理上同时发生,指在某一个时间点同时运行多个程序。
注意:进程是拥有资源的基本单位,线程是CPU调度的基本单位
Java如何实现多线程?
由于线程是依赖进程而存在的,所以我们应该先创建一个进程出来。而进程是由系统创建的,所以我们应该去调用系统功能创建一个进程。但是Java是不能直接调用系统功能的,所以,我们没有办法直接实现多线程程序。但是呢 ? Java可以去调用C / C++ 写好的程序来实现多线程程序。由C / C++ 去调用系统功能创建进程,然后由Java去调用这样的东西,然后提供一些类供我们使用。我们就可以实现多线程程序了。
java 提供了一个类 Thread 通过这个类就可以实现多线程。Thred 线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。创建新执行线程有两种方法。一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。
接下来可以分配并启动该子类的实例。
接下来就开一个线程出来:
创建一个MyThread继承Thread
public class MyThread extends Thread {
//run() 是线程要执行的方法,方法里面的代码是让线程来执行的
@Override
public void run() {
//就是需要线程来执行的代码
//一般来说,一些耗时的操作,需要我们开启一个线程,在run()方法里面去操作
//复制操作
//模拟耗时操作
for (int i = 0; i < 100; i++) {
System.out.println(i);
}
}
}
开启线程:
public class ThreadTest {
public static void main(String[] args) {
//创建新执行线程有两种方法。
//一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。
//接下来可以分配并启动该子类的实例
//1.写一个类继承Thread类
//2.重写Thread类中的run()方法
//3.开启线程
MyThread th = new MyThread();
//如何开启线程
//th.run(); //这个不是开启线程,这是你一个普通调用方法的操作,线程没有开启
//怎么开启
//public void start () 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
th.start();//开启线程
//th.start();重复开启线程会抛异常
//可以再开启一个
MyThread th2 = new MyThread();
th2.start();
}
}
注意:
进程是拥有资源的基本单位,线程是CPU调度的基本单位
多个线程具有随机性,都在抢占CPU的执行权(时间片),哪个线程抢到了CUP的执行权,CPU就会执行这个线程
锁
public class MyThread extends Thread {
private static int index = 100 ;
@Override
public void run() {
try {
while (index>=0){
Thread.sleep(50);//模拟网络延迟
String name = Thread.currentThread().getName();
System.out.println("窗口"+name+"电影票还剩下"+index--+"张");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args) {
MyThread myThread = new MyThread();
MyThread myThread1 = new MyThread();
myThread.setName("一号");
myThread1.setName("二号");
myThread.start();
myThread1.start();
}
}
结果:
会出现以上这种结果,出现-1。这是为什么么???
其实很容易理解,比如说现在一号窗口剩一张票了,网络延迟了一下,刚好二号窗口一看还有最后一张票就要出售,等二号窗口真正的出售的时候,其实一号窗口已经把最后一张票卖出了,所以二号窗口现在拿的是0票,所以等二号窗口真是出售的时候就是0票再卖一张,不就是-1票嘛。
所以并发运行就涉及到了线程安全问题,其实解决线程的问题,最基本的就是上锁,那么锁其实就是,假如我线程一现在执行上锁的这段代码的时候,其他线程就不能执行,就得等待,等线程一执行完,释放锁其他的就可以执行了。
那么我们对以上的代码加上锁,关键字(synchronized)。
只对MyTread类加锁
public class MyThread extends Thread {
private static int index = 100 ;
private static final Object obj = new Object() ;//给锁创建参数对象
@Override
public void run() {
synchronized (obj){//上锁
try {
while (index>=0){
Thread.sleep(50);//模拟网络延迟
String name = Thread.currentThread().getName();
System.out.println("窗口"+name+"电影票还剩下"+index--+"张");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
在这里,需要注意的是synchronized(对象),需要传个对象参数,切记,不能直接在括号里new一个对象,这样的话,相当于每次都创建一个新锁,就没用了。所以应该总共创建一把共享锁。
当然,创建锁并不是这一种,在JDK1.5之后,就提供了Lock锁。
把上面的synchronized锁我们换成Lock看一下。
public class MyThread extends Thread {
private static int index = 100 ;
private static final Lock lock = new ReentrantLock() ;//创建锁对象,同样只用一把锁
@Override
public void run() {
lock.lock();//上锁
try {
while (index>=0){
Thread.sleep(50);//模拟网络延迟
String name = Thread.currentThread().getName();
System.out.println("窗口"+name+"电影票还剩下"+index--+"张");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();//释放锁
}
}
我们可以看得出,Lock锁,直接调用方法,进行加锁,释放锁。
死锁
如果出现了同步嵌套,就容易产生死锁问题
是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象
同步代码块的嵌套案例
死锁: 两个或者两个以上的线程,在抢占CPU的执行权的时候,都处于等待状态
举例: 中国人和美国人一起吃饭
中国人使用的筷子
美国人使用的刀和叉
中国人获取到了美国人的刀
美国人获取到了中国人的一根筷子
那么,双方都不肯放手,就陷入一个僵局,都没得饭吃,死锁就是这样的。
先创建两个Object类型的锁
public abstract class ObjectUtils {
public static final Object objA= new Object();
public static final Object objB= new Object();
}
开启线程A,B
public class MyTest {
public static void main(String[] args) {
MyThread myThread1 = new MyThread(true);
MyThread myThread2 = new MyThread(false);
myThread1.start();
myThread2.start();
}
}
继承Threa,重写的run方法
public class MyThread extends Thread {
boolean b;
public MyThread(boolean b) {
this.b = b;
}
@Override
public void run() {
while (true) {
if (b) {
synchronized (ObjectUtils.objA) {
System.out.println("true,A进来了");
synchronized (ObjectUtils.objB) {
System.out.println("treu,B进来了");
}
}
synchronized (ObjectUtils.objB) {
System.out.println("false,B进来了");
synchronized (ObjectUtils.objA) {
System.out.println("false,A进来了");
}
}
}
}
}
}