进程
计算机同一时刻只能有一个进程
CPU分配时间片
CPU通过一个指针,在打开的进程里,非常迅速的轮转,所以可以边听音乐,边敲代码,边看ppt
进程是由开发人员设置的
进程就是正在进行的程序
独立性:系统中独立存在的实体,进程之间相互独立
动态性:程序是一个静态的指令集和,进程是一个正在系统中活动的指令集和,进程中加入时间概念,进程具有自己的生命周期和各种不同的状态,是程序中不具备的
并发性:多个进程可以在单个处理器并发执行,多个进程之间不会互相影响
并行:指在同一时刻,有多条指令在处理器上同时执行,宏观和微观都是同时的
并发:指在同一时刻只能有一条指令执行,因为指针的迅速轮转,宏观上认为是同时的
线程
线程是进程中的顺序执行控制流,是一条执行路径,是操作系统能够进行运算调度的最小单位
简而言之:一个程序运行至少一个进程,一个进程包含一个线程(单线程)或多个线程
线程依赖进程存在
Thread类,JAVASE中规定,一个类只要继承Thread类,此类就是一个线程类
子类中必须重写父类Thread类的run()方法,此方法为线程的主体,也就是当线程被CPU调度时要执行的方法
如果无法使用this获取线程名称,可以使用currentThread获取
可设置CPU的优先级
线程的优先级并不能保证线程的执行顺序,只是CPU给优先级高的线程的资源概率大
优先级低的也不是没有机会执行
生命周期
新建状态(New):
当一个线程对象被创建后,线程就处于新建状态。
在新建状态中的线程对象从严格意义上看还只是一个普通的对象,还不是一个独立的线程,不会被线程调度程序调度。
新建状态是线程生命周期的第一个状态。
就绪状态 (Runnable):
处于新建状态中的线程被调用start()方法就会进入就绪状态。
处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执行,但并不是说执行了start()方法线程就会立即执行。
在等待/阻塞状态中的线程,被解除等待和阻塞后将不直接进入运行状态,而是先进入就绪状态
运行状态(Running):
处于就绪状态中的线程一旦被系统选中,使线程获取了CPU时间,就会进入运行状态。
线程在运行状态下随时都可能被调度程序调度回就绪状态。在运行状态下还可以让线程进入到等待/阻塞状态。
在通常的单核CPU中,在同一时刻只有一个线程处于运行状态。在多核的CPU中,就可能两个线程或更多的线程同时处于运行状态,这也是多核CPU运行速度快的原因。
注:就绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,必须先处于就绪状态中。
阻塞状态(Blocked):
根据阻塞产生的原因不同,阻塞状态又可以分为三种
等待阻塞:运行状态中的线程执行wait()方法,使当前线程进入到等待阻塞状态;
锁阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程所占用),线程会进入同步阻塞状态。
其他阻塞:通过调用线程的sleep()或发出了I/O请求时等,线程会进入到阻塞状态。当sleep()睡眠结束或I/O处理完毕时,线程重新转入就绪状态。
继承Thread类
public class XianCheng {
public static void main(String[] args) {
Thread01 t1=new Thread01();
Thread01 t2=new Thread01();
t1.start();
t2.start();
Thread02 t3=new Thread02();
Thread02 t4=new Thread02();
Thread02 t5=new Thread02();
t3.setName("t3");
t4.setName("t4");
t5.setName("t5");
System.out.println(t3.getPriority());
System.out.println(t4.getPriority());
System.out.println(t5.getPriority());
t3.setPriority(1);
t4.setPriority(5);
t5.setPriority(10);
t3.start();
t4.start();
t5.start();
}
}
class Thread01 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(this.getName()+"run方法执行" +i);
}
}
}
class Thread02 extends Thread{
@Override
public void run() {
for (int i = 0; i < 101; i++) {
System.out.println(this.getName()+"线程执行run()方法"+i);
}
}
}
实现Runnable接口
Thread类的局限性,猫类继承了动物类,就不能继承Thread类
为此,Thread提供了两个构造方法
→使用Runnable接口实现多线程
→Runnable接口中没有start方法,依赖于Thread启动线程
好处:继承其他类没有限制
public class XianCheng02 {
public static void main(String[] args) {
Runnable01 target=new Runnable01();
Thread t1=new Thread(target,"t1");
Thread t2=new Thread(target,"t2");
t1.start();
t2.start();
}
}
class Runnable01 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 101; i++) {
System.out.println(Thread.currentThread().getName()+"线程执行了run()"+i);
}
}
}
线程案例
public class XCAnLi {
public static void main(String[] args) {
Ticket t1=new Ticket();
t1.setName("窗口1");
Ticket t2=new Ticket();
t2.setName("窗口2");
Ticket t3=new Ticket();
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Ticket extends Thread {
static int tickets=100;
@Override
public void run() {
while (true) {
if (tickets > 0) {
System.out.println(this.getName() + "卖出了第" + (tickets--) + "张票");
} else {
break;
}
}
}
}
public class XCAnLi {
public static void main(String[] args) {
Tickets t=new Tickets();
Thread t1=new Thread(t,"窗口1");
Thread t2=new Thread(t,"窗口2");
Thread t3=new Thread(t,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Tickets implements Runnable{
int tickets=100;
@Override
public void run() {
while (true){
if(tickets>0){
System.out.println(Thread.currentThread().getName()+"卖出了第"+(tickets--)+"张票");
}else {
break;
}
}
}
}
线程安全
线程安全问题:
例如上边的案例可能出现一票多卖或者负数情况。多次运行有可能出现问题
判断一个程序是否有线程安全问题依据:
是否存在多线程环境
是否存在共享数据
是否存在多条语句访问,修改共享数据
为了解决这样的问题就使用到了:同步代码块
同步代码块是指一个时间段内只能有一个线程执行,提供synchronized关键字
将有可能发生线程安全问题的代码包含在同步代码块中,同一时间只允许一个线程进入同步代码块
必须指定一个需要同步的对象,也称为锁对象,这里的锁对象,可以是任意对象,但必须只能是一个
若使用this作为锁对象,需保证多个线程执行时,this指向的是同一个对象
也可以使用关键字将一个方法修饰成同步方法,它能实现和同步代码块同样的功能
访问该方法的其他线程都会发生阻塞,直到当前线程访问完毕,其他线程才有机会访问
同步代码块
synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问
格式:synchronized (锁对象) {需要同步的代码}
锁对象可以是任意类型。
多个线程对象要使用同一把锁。
public class XCAQ {
public static void main(String[] args) {
Tickets2 t=new Tickets2();
Thread t1=new Thread(t,"窗口1");
Thread t2=new Thread(t,"窗口2");
Thread t3=new Thread(t,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Tickets2 implements Runnable {
int tickets = 100;
@Override
public void run() {
while (true) {
synchronized (this) {
if (tickets > 0) {
try {
Thread.sleep(200);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖出了第" + (tickets--) + "张票");
} else {
break;
}
}
}
}
}
同步方法
用synchronized修饰的方法叫做同步方法,保证一个线程执行该方法的时候,其他线程只能在方法外等着。
格式:public synchronized void method(){可能会产生线程安全问题的代码}
public class XCAQ2 {
public static void main(String[] args) {
Tickets4 t1=new Tickets4();
t1.setName("窗口1");
Tickets4 t2=new Tickets4();
t2.setName("窗口2");
Ticket t3=new Ticket();
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Tickets4 extends Thread {
static int tickets = 100;
@Override
public void run() {
while (true) {
setSyncObj();
if(tickets<=0){
break;
}
}
}
public static synchronized void setSyncObj(){
if (tickets > 0) {
try {
Thread.sleep(200);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖出了第" + (tickets--) + "张票");
}
}
}
Lock锁
为了更清晰的表达如何加锁和释放锁,jdk5以后提供了一个新的锁对象Lock
比使用synchronized方法和语句获得更广泛的锁定操作
提供了获得锁和释放锁的方法
void lock():获得锁
void unlock():释放锁
Lock是不能直接实例化,采用实现ReentrantLock来实例化
睡眠sleep()方法: 让当前线程进入到睡眠状态,到指定毫秒后自动醒来继续执行
public class LockDemo {
public static void main(String[] args) {
Tickets5 t=new Tickets5();
Thread t1=new Thread(t,"窗口1");
Thread t2=new Thread(t,"窗口2");
Thread t3=new Thread(t,"窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Tickets5 implements Runnable {
static int tickets = 100;
private ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock();
if (tickets > 0) {
Thread.sleep(200);
System.out.println(Thread.currentThread().getName() + "卖出了第" + (tickets--) + "张票");
} else {
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
}
死锁
死锁:线程死锁是指由于两个或多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行
线程嵌套会出现死锁情况
下面案例就是死锁现象
public class LockDemo {
public static void main(String[] args) {
Object obj1=new Object();
Object obj2=new Object();
new Thread(()->{
while(true){
synchronized (obj1){
synchronized (obj2) {
System.out.println("tom run");
}
}
}
}).start();
new Thread(()->{
while(true){
synchronized (obj2){
synchronized (obj1) {
System.out.println("jack run");
}
}
}
}).start();
}
}
线程状态
锁中有提到睡眠方法
等待和唤醒
void wait():导致当前线程等待,直到另一个线程调用该对象的唤醒方法
void notify():唤醒正在等待对象监视器的单个线程
void notifyAll():唤醒正在等待对象监视器的所有线程
public class WaitNotify {
public static void main(String[] args) {
customer c=new customer();
Producer p=new Producer();
c.start();
p.start();
}
}
class customer extends Thread{
@Override
public void run() {
while (true){
synchronized (Desk.lockobj){
if(Desk.count==0){
break;
}else {
if (Desk.flag){
System.out.println("消费者在消费面包");
Desk.flag=false;
Desk.lockobj.notifyAll();
System.out.println("消费者吃了第"+Desk.count+"个面包");
Desk.count--;
}else {
try {
Desk.lockobj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
class Producer extends Thread{
@Override
public void run() {
while (true){
synchronized (Desk.lockobj){
if(Desk.count==0){
break;
}else {
if (!Desk.flag){
System.out.println("生产者开始生产面包");
Desk.flag=true;
Desk.lockobj.notifyAll();
}else {
try {
Desk.lockobj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
}
class Desk{
public static boolean flag=false;
public static int count=15;
public static final Object lockobj=new Object();
}
线程池
线程池:用来装线程
代码实现:
1.创建一个空池子,创建Executors中的静态方法
2.有任务需要执行时,创建线程对象 ---------->submit方法 线程池会自动帮创建对象
任务执行完毕,线程对象归还线程池 ------>^ 执行完毕也会自动归还
创建线程池方式一
static ExcutorService newCachedThreadPool():创建一个根据需要创建新线程的线程池,但在他们可用时将重用以前构造的线程
创建线程池方式二
static ExcutorService newFixedThreadPool(int nThreads):创建一个指定最多线程数量的线程池,nThreads表示最多线程数量
public class XCpool {
public static void main(String[] args) throws InterruptedException {
ExecutorService pool = Executors.newCachedThreadPool();
pool.submit(()->{
System.out.println(Thread.currentThread().getName()+"1在执行");
});
Thread.sleep(3000);
pool.submit(()->{
System.out.println(Thread.currentThread().getName()+"2在执行");
});
pool.submit(()->{
System.out.println(Thread.currentThread().getName()+"3在执行");
});
pool.shutdown();
}
}
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(10);
for (int i = 0; i < 20; i++) {
es.submit(()->{
System.out.println(Thread.currentThread().getName()+"1在执行");
});
es.submit(()->{
System.out.println(Thread.currentThread().getName()+"2在执行");
});
es.submit(()->{
System.out.println(Thread.currentThread().getName()+"3在执行");
});
}
ThreadPoolExecutor pe=(ThreadPoolExecutor)es;
System.out.println(pe.getPoolSize());
es.shutdown();
}
}
public static void main(String[] args) {
ThreadPoolExecutor pe=new ThreadPoolExecutor(1,3,2, TimeUnit.SECONDS,new ArrayBlockingQueue<>(2), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 5; i++) {
pe.submit(new RunnableTest());
}
pe.shutdown();
ThreadPoolExecutor tpe=new ThreadPoolExecutor(1,3,2, TimeUnit.SECONDS,new ArrayBlockingQueue<>(2), Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 0; i < 5; i++) {
int y=i;
tpe.submit(()->{
System.out.println(Thread.currentThread().getName()+" :"+y);
});
}
tpe.shutdown();
}
}
class RunnableTest implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程执行了");
}
}