操作系统他的发展史:
手工操作
批处理系统
多道批处理
分时系统
实时系统
进程和线程:
进程:正在执行的程序,其实就是一块儿内存区域,内部存储着程序的资源
线程:程序被CPU调度的最小单位。
继承Thread类,重写run方法。
实现Runnable接口,实现run方法。
必须:run(), start()
run(): 线程执行的时候要执行的代码 start():启动一个线程
继承Thread类 实现MyThread类
public class MyThread extends Thread {
@Override
public void run() {
// 要把子线程执行的内容写在run里面
for(int i = 0; i < 1000; i ++) {
System.out.println("我是子线程:" + i);
}
}
}
线程的使用与创建
// 1. 创建线程对象
MyThread mt = new MyThread();
// 2.调用start方法启动一个线程
mt.start();
实现Runable接口
实现Runable中fun方法
public class MyRunnable implements Runnable {
public void run() {
for(int i = 0; i < 1000; i ++) {
System.out.println("我是子线程:" + i);
}
}
}
线程的使用与创建
// 1. 先创建Runnable类
Runnable r = new MyRunnable();
// 2.创建线程对象必须指向我的Runnable
Thread td = new Thread(r);
// 3.调用start方法启动一个线程
td.start();
优先级高的线程,是会有一定程度的先执行的权限。但是具体是操作系统来实现的,不同的操作系统可以效果不同。 至于先执行哪个线程,还是看操作系统是怎么分的。优先级相当于只是让操作系统稍微参考一下。
public class test {
public static void main(String[] args) {
MyThread mt1 = new MyThread("A线程");
MyThread mt2 = new MyThread("B线程");
mt2.setPriority(10);
mt1.setPriority(1);
mt1.start();
mt2.start();
}
}
睡眠函数,是让当前线程等待一段时间再去执行之后的语句。
sleep里面的单位是毫秒,下面代码中相当于是每一秒执行一次。
@Override
public void run() {
while (true) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date now = new Date();
System.out.println(sdf.format(now));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
mt.join相当于是会程序卡在这个位置,等到mt线程执行完毕之后再去执行后面的语句。
public class MyThread extends Thread {
@Override
public void run() {
for(int i = 0; i < 1000; i ++) {
System.out.println("我是子线程:" + i);
}
}
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
for(int i = 0; i < 100; i++) {
System.out.println("------我是主线程:" + i);
}
try {
mt.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("---------------------------");
}
}
相当于是在可以把当前的cpu让出去,让别人先用一下,只是让一下,而不会一直让下去。 主要是实现一个交替执行的效果。 具体效果,主要还是看操作系统的分配。
public class MyThread extends Thread {
public MyThread(String name) {
super.setName(name);
}
@Override
public void run() {
for(int i= 0; i < 500; i++) {
System.out.println(super.getName() + "--" + i);
if(i % 10 == 0) {
Thread.yield();
}
}
}
public static void main(String[] args) {
MyThread mt1 = new MyThread("A线程");
MyThread mt2 = new MyThread("B线程");
mt1.start();
mt2.start();
}
}
相当于是子线程因为一些原因进行睡眠操作,但是可以用interrupt来打断子线程的睡眠操作。继续执行之后的工作。
public class MyThread extends Thread{
@Override
public void run() {
System.out.println("我要睡觉了");
try {
Thread.sleep(1000000000);
} catch (InterruptedException e) {
System.out.println("为什么不上睡了");
}
System.out.println("清醒了,开始工作了。");
}
public static void main(String[] args) {
MyThread mt = new MyThread();
mt.start();
for(int i = 0; i < 1000; i ++) {
System.out.println(i);
}
mt.interrupt(); // 打断正在睡眠的子线程
}
}
线程同步: 当多个线程共享同一个资源的时候,我们可以在某个一个线程访问到这个资源的时候,把这个资源暂时封锁,等待执行结束,释放这个锁,其他线程才可以进行执行,线程同步。
总结:等待其它线程释放锁,让线程变得更加安全。
例子:如果在不同线程中操作同一个资源,比如ATM机以及银行柜台同时取钱,同时查询都是有钱的,同时取钱的话会让银行赔钱。所以需要对这个公共资源进行处理,不能有多个人同时使用这个资源,就需要在使用的时候对资源进行封锁。当其他人要用的时候需要等之前的人用完才能用,这样可以避免产生问题。
封锁的方法如下:
synchronized关键字可以将整个方法上锁,只要有人使用就上锁,用完之后自动释放。
public class Account {
private double blance;
public Account(double blance) {
this.blance = blance;
}
public synchronized void getMoney() {
if(this.blance <= 0) {
System.out.println("没钱了!!");
return;
}
System.out.println("我要取钱了,目前还剩下:" + this.blance);
this.blance -= 1000;
System.out.println("取完了,还剩下:" + this.blance);
}
}
通过synchronized() {}语句将需要上所的位置上锁。 相比于synchronized关键字的话,语句比较灵活,所以用synchronized的话首推语句。
public class Account {
private double blance;
public Account(double blance) {
this.blance = blance;
}
public void getMoney() {
synchronized (this){
if(this.blance <= 0) {
System.out.println("没钱了!!");
return;
}
System.out.println("我要取钱了,目前还剩下:" + this.blance);
this.blance -= 1000;
}
System.out.println("取完了,还剩下:" + this.blance);
}
}
建一个锁、lock,使用的时候上锁,用完后解锁。
public class Account {
private double blance;
private Lock lock = new ReentrantLock();
public Account(double blance) {
this.blance = blance;
}
public void getMoney() {
lock.lock();
if(this.blance <= 0) {
System.out.println("没钱了!!");
return;
}
System.out.println("我要取钱了,目前还剩下:" + this.blance);
this.blance -= 1000;
System.out.println("取完了,还剩下:" + this.blance);
lock.unlock();
}
}
当两个线程都需要两个资源的时候,A拥有1 申请2,B拥有2 申请1,这个时候就会产生死锁。
注意事项:使用synchronized的时候一定要格外注意,有没有互相调用的方法被锁定,慎重使用synchronized。
死锁实例代码:
分别锁定一个资源之后申请第二个资源。资源被占用,并且也是等待资源状态。
public class ResourceObject {
public static final Object obj1 = new Object();
public static final Object obj2 = new Object();
}
public class DeadLock1 extends Thread{
@Override
public void run() {
synchronized(ResourceObject.obj1) {
System.out.println("锁定资源1");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (ResourceObject.obj2) {
System.out.println("锁定资源2");
System.out.println("使用完毕");
}
}
}
}
public class DeadLock2 extends Thread{
@Override
public void run() {
synchronized(ResourceObject.obj2) {
System.out.println("锁定资源2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (ResourceObject.obj1) {
System.out.println("锁定资源1");
System.out.println("使用完毕");
}
}
}
}
public class Test {
public static void main(String[] args) {
DeadLock1 dl1 = new DeadLock1();
DeadLock2 dl2 = new DeadLock2();
dl1.start();
dl2.start();
}
}
Thread t = new Thread(); // 创建一个线程 t.start(); //开启一个线程,线程处于就绪状态
是一个非常常用的模型,增加资源的利用率以及效率。
图中上半部分是一个在进行的时候另一个必须要等待。效率非常低,总会有一个是在等待。
异步,左边不用等右边,右边不用等左边。
生产者消费者模型:读取视频这一方被称为生产者,产品就是中间的视频,右边发送给公安局的就是消费者,消费的是视频。
Queue:队列,BlockingQueue阻塞队列,当队列中没有数据的时候,需要拿出数据,队列将会将程序阻塞,阻塞到有数据,队列继续工作。
AtomicInteger 线程安全的数字类型。
public class Video { // 封装一个类用来储存单一的数据,仅仅用来表示数据。
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Video(String name) {
this.name = name;
}
}
public class ReadVideoThread extends Thread { // 生产者,读取视频
// private static int i = 0; // 虽然这个语句是所有线程共享的,因为是静态的,但是不安全,因为多个线程同时i++的时候可能会产生冲突
private static AtomicInteger i = new AtomicInteger(); // 线程安全的数字类型
private BlockingQueue