1 线程的创建与启动
1.线程是进程中的实体,一个进程可以拥有多个线程,一个线程必须有一个父进程。线程不拥有系统资源,只有运行必须的一些数据结构;它与父进程的其它线程共享该进程所拥有的全部资源。线程可以创建和撤消线程,从而实现程序的并发执行。
2. 进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
3.Start()…… 启动线程……
4.(1)创建一个Runnable接口的对象并使用Thread对象启动它。
(2)继承Thread对象
1.一个Thread类的对象对应一个线程
2. Thread t = new Thread(obj);
3. Obj:Runnable的对象 Runnable的实现类是线程执行的主体
4.(一)继承Thread类创建多线程----单线程
(二)通过继承Thread类实现多线程:
如果希望(一)中的两个循环打印语句都能够执行的话,那么就需要实现多线程。为此jdk提供了一个多线程类Thre ad,通过继承Thread类,并重写Thread类中的run()方法便可以实现多线程。在Thread类中,提供了一个start()方法用于启动新线程,线程启动后,系统就会自动调用run()方法。
(三)实现Runnable接口创建多线程
在(二)中通过继承Thread类实现多线程,但是这种方式有一定的局限性。因为在java中只支持单继承,一个类一旦继承了某个父类就无法再继承Thread类,比如学生类Student继承了person类,就无法再继承Thread类创建的线程。为了克服这种弊端,Thread类提供了另外一种构造方法Thread(Runnable target),其中Runnable是一个接口,它只有一个run()方法。当通过Thread(Runnabletarget)构造方法创建一个线程对象时,只需该方法传递一个实现了Runnable接口的实例对象,这样创建的线程将调用实现了Runnable接口中的run()方法作为运行代码,二不需要调用Thread类中的run()方法。
1.
第一种方法:继承Thread类,重写run()方法,run()方法代表线程要执行的任务。
第二种方法:实现Runnable接口,重写run()方法,run()方法代表线程要执行的任务。
第三种方法:实现callable接口,重写call()方法,call()作为线程的执行体,具有返回值,并且可以对异常进行声明和抛出
2 线程简单同步(同步块)
同步锁:
1.Java5开始,Java提供了一种功能更加强大的线程同步机制——通过显式定义同步锁对象来实现同步,这里的同步锁由Lock对象充当。
Lock 对象提供了比synchronized方法和synchronized代码块更广泛的锁定操作,Lock是控制多个线程对共享资源进行访问的工具。通常, 锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应该先获得Lock对象。
某些锁可能允许对共享资源并发访问,如ReadWriteLock(读写锁),Lock,ReadWriteLock是Java5提供的两个根接口,并为 Lock提供了ReentrantLock实现类,为ReadWriteLock提供了 ReentrantReadWriteLock实现类。在 Java8中提供了新型的StampLock类,在大多数场景下它可以替代传统的ReentrantReadWriteLock。ReentrantReadWriteLock为读写操作提供了三种锁模式:Writing,ReadingOptimistic,Reading。
2.在实现线程安全的控制中,比较常用的是ReentrantLock(可重入锁)。
同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说。
"同"字从字面上容易理解为一起动作
其实不是,"同"字应是指协同、协助、互相配合。
如进程、线程同步,可理解为进程或线程A和B一块配合,A执行到一定程度时要依靠B的某个结果,于是停下来,示意B运行;B依言执行,再将结果给A;A再继续操作。
所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回,同时其它线程也不能调用这个方法。按照这个定义,其实绝大多数函数都是同步调用(例如sin, isdigit等)。但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时间完成的任务。例如Window API函数SendMessage。该函数发送一个消息给某个窗口,在对方处理完消息之前,这个函数不返回。当对方处理完毕以后,该函数才把消息处理函数所返回的LRESULT值返回给调用者。
在多线程编程里面,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,以保证数据的完整性。
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块 的对象;
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
packagezheng;
importcom.sun.media.jfxmedia.events.NewFrameEvent;
publicclass testhread {
static int c=0;
static Object lock = new Object();
public static void main(String[] args) {
Thread[] thread = new Thread[1000];
for(int i=0;i<1000;i++) {
final int index = i;
thread[i] = new Thread(()->{
synchronized(lock) {
System.out.println("thread"+index+"enter");
int a = c;//获取c的值
a++;//将值加一
try {//模拟复杂处理过程
Thread.sleep((long)(Math.random()*1000));
}
catch(InterruptedException e) {
e.printStackTrace();
}
c=a;//存回去
System.out.println("thread"+index+"leave");
}
});
thread[i].start();//线程开始
}
for(int i=0;i<1000;i++) {
try {
thread[i].join();//等待thread i完成
}catch(InterruptedException e) {
e.printStackTrace();
}
}//循环后所有的线程都完成了
System.out.println("c="+c);//输出c的结果
}
}
3 生产者消费者问题
3.1 问题表述
生产者消费者问题,也称有限缓冲问题,是一个多线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。通常采用进程间通信的方法解决该问题,常用的方法有信号灯法等。如果解决方法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。该问题也能被推广到多个生产者和消费者的情形。
package org.zheng;
import java.util.LinkedList;
importjava.util.concurrent.locks.Condition;
importjava.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Queue { //队列
//(1)建立一个锁,俩信号量
private Lock lock =new ReentrantLock(); //锁
private Condition fullC; //信号量
private Condition emptyC; //信号量
private int size;
public Queue(int size) {
this.size = size;
//(2)为信号量赋初值
fullC = lock.newCondition();
emptyC = lock.newCondition();
}
LinkedList<Integer> list = new LinkedList<Integer>();
/**
* 入队
* @return
*/
public boolean EnQueue(int data) {
lock.lock(); //上锁
while(list.size()>=size) {
try {
fullC.await();
} catch (InterruptedException e) {
lock.unlock();
return false;
}
}
list.addLast(data);
emptyC.signalAll(); lock.unlock();
return true;
}
/**
* 出队
* @return
*/
public int DeQueue() {
lock.lock(); //先上锁
while(list.size() == 0) { //如果队列为空,则等待生产者 唤醒我
try {
emptyC.await();
} catch (InterruptedException e) {
lock.unlock();
return -1; //失败返回
}
}
int r = list.removeFirst(); //获取队列头部
fullC.signalAll(); //唤醒所有的生产者
lock.unlock(); //解锁
return r;
}
public boolean isFull() {
return list.size()>=size;
}
public boolean isEmpty() {
return list.size()==0;
}
}
当生产力超出消费能力时,没有缓冲区让消费者去放产品,此时消费者等待,消费者消费,当有缓冲区空闲,可以放产品时,生产者才可以去放产品。
当生产力弱于消费能力时,缓冲区的产品不够消费,供不应求,会有空的缓冲区,当缓冲区空了之后,让消费者等待,当生产者生产了产品,缓冲区不再为空,则唤醒消费者。
经过一天的学习,让我对操作系统的实践有了更深的认识,学到了更多的知识,这些知识是平常课堂上学不到的。所以我对每一次课程设计的机会都非常珍惜。不一定我的课程设计能够完成得有多么完美,但是要用心去做,去发现问题解决问题,发现自己的不足,让自己在编程的能力上有所进步。