一.线程和进程
进程:进程是执行中的代码,严格的来说,进程不仅仅是程序代码,还包括运行在CPU 中的一系列数据,包括全局变量和临时数据等等。
线程:线程是CPU使用的基本单元,它由线程ID,程序计数器,寄存器集合和栈组成。
线程和进程的关系:一个进程至少包括一个线程,一个或者多个线程组成一个进程,进程和线程是一对多的关系。
二.CPU调度
关于CPU调度的内容过多,所以这里解释2个部分内容,分别是FIFO调度和轮转法调度
FIFO调度:先到先服务调度,即谁先到达谁最先执行。
轮转法调度:CPU将自身的使用时间分成一个小的时间片,每一个时间片执行一个进程或线程。教学视频中的调度方法应该就是这一种。
三.线程状态
java中规定的线程状态有一下5种:
NEW
至今尚未启动的线程处于这种状态。RUNNABLE
正在 Java 虚拟机中执行的线程处于这种状态。BLOCKED
受阻塞并等待某个监视器锁的线程处于这种状态。WAITING
无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。TIMED_WAITING
等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。TERMINATED
已退出的线程处于这种状态。
四.线程的创建
java线程的创建有继承Thread和实现Runnable两种方法,相比较而言第二种方法更为常见也更为方便。
五.线程同步
由于采用时间片制度,当run()方法中执行的操作过多时,就会出现在一个时间片不能执行完毕的情况,这种情况下线程执行会混乱,可能产生不可知和错误的数据,这个时候就要用到同步函数和同步代码块,关键字是synchronized。
同步的前提:
a,必须要有两个或者两个以上的线程。
b,必须是多个线程使用同一个锁。
同步的利弊:
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源。
如何寻找多线程中的安全问题:
a,明确哪些代码是多线程运行代码。
b,明确共享数据。
c,明确多线程运行代码中哪些语句是操作共享数据的。
静态的同步方法,使用的锁是该方法所在类的字节码文件对象。类名.class。
public class ThreadSafeTest implements Runnable {
int num = 10; // 设置当前总票数
public void run() {
while (true) {
if (num > 0) {
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("tickets" + num--);
}
}
}
public static void main(String[] args) {
ThreadSafeTest t = new ThreadSafeTest(); // 实例化类对象
Thread tA = new Thread(t); // 以该类对象分别实例化4个线程
Thread tB = new Thread(t);
Thread tC = new Thread(t);
Thread tD = new Thread(t);
tA.start(); // 分别启动线程
tB.start();
tC.start();
tD.start();
}
}
六.死锁
在多线程情况下,多个线程可能竞争一定数量的资源,某个线程申请资源,如果这个资源不可用,那么该程序进入等待状态,如果所申请的资源被其他等待线程所占有,那么该线程可能再也无法进入等待状态,这种情况称为死锁,为了避免死锁,请不要在同步函数中内嵌同步函数,这会导致非常严重的死锁问题
七.JDK1.5中提供了多线程升级方案
将同步替换成现实Lock操作,将Object中的wait,notify,notifyall,替换了Condition对象。
该对象可以从Lock锁进行获取。
/*
生产者生产商品,供消费者使用
有两个或者多个生产者,生产一次就等待消费一次
有两个或者多个消费者,等待生产者生产一次就消费掉
*/
import java.util.concurrent.locks.*;
class Resource
{
private String name;
private int count=1;
private boolean flag = false;
//多态
private Lock lock=new ReentrantLock();
//创建两Condition对象,分别来控制等待或唤醒本方和对方线程
Condition condition_pro=lock.newCondition();
Condition condition_con=lock.newCondition();
//p1、p2共享此方法
public void setProducer(String name)throws InterruptedException
{
lock.lock();//锁
try
{
while(flag)//重复判断标识,确认是否生产
condition_pro.await();//本方等待
this.name=name+"......"+count++;//生产
System.out.println(Thread.currentThread().getName()+"...生产..."+this.name);//打印生产
flag=true;//控制生产\消费标识
condition_con.signal();//唤醒对方
}
finally
{
lock.unlock();//解锁,这个动作一定执行
}
}
//c1、c2共享此方法
public void getConsumer()throws InterruptedException
{
lock.lock();
try
{
while(!flag)//重复判断标识,确认是否可以消费
condition_con.await();
System.out.println(Thread.currentThread().getName()+".消费."+this.name);//打印消费
flag=false;//控制生产\消费标识
condition_pro.signal();
}
finally
{
lock.unlock();
}
}
}
//生产者线程
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res=res;
}
//复写run方法
public void run()
{
while(true)
{
try
{
res.setProducer("商品");
}
catch (InterruptedException e)
{
}
}
}
}
//消费者线程
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res=res;
}
//复写run
public void run()
{
while(true)
{
try
{
res.getConsumer();
}
catch (InterruptedException e)
{
}
}
}
}
class ProducerConsumer
{
public static void main(String[] args)
{
Resource res=new Resource();
new Thread(new Producer(res)).start();//第一个生产线程 p1
new Thread(new Consumer(res)).start();//第一个消费线程 c1
new Thread(new Producer(res)).start();//第二个生产线程 p2
new Thread(new Consumer(res)).start();//第二个消费线程 c2
}
}