线程和进程
线程调度进程:
- 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间。
线程:
- 是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行。一个进程最少有一个线程
- 线程实际上是在进程基础之上进一步划分,一个进程启动后,里面的若干执行路径又可以划分成若干个线程。
分时调度:
抢占式调度:
- 所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。
优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
概念:
CPU使用抢占式调度模式再多个线程间进行高度的切换。对于CPU的一个核而言,某个时刻,只能执行一个线程,而CPU在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。其实,多线程程序并不能提高程序的运行速度,但是能够提高程序运行效率,让CPU的使用率更高。
同步与异步
并发和并行同步: 排队执行,效率低但是安全
异步: 同时执行,效率高但是数据不安全
并发: 指两个或者多个事件在同一个时间段内发生。
并行: 指两个或多个事件在同一时刻发生(同时发生)。
二、Thread(继承) 三、Runnable(接口)
实现 Runnable 和 继承 Thread 相比有如下优势:
- 首先通过创建任务,然后给线程分配的方式来实现多线程,更适合多个线程同时执行相同任务的情况。
- 可以避免单继承带来的局限性。
- 任务与线程本身是分离的,提高程序的健壮性。
- 后续学习地址池技术,接受Runnable 类型的任务,不接受Thread 类型的线程。
创建线程类
设置和获取线程名称继承式创建 Thread:
// Thread 创建线程。 这是单继承方式的创建,不推荐使用,但也能玩。 public class MyThread extends Thread{ //run方法 @Override public void run(){ //这里的代码 就是一条新的执行路径 for(int i = 0; i < 10; i++){ System.out.println(i); } } }//MyThread 线程 //调用 在main 中调用 public static void main(String[] args){ MyThread m = new MyThread(); // 线程启动 m.start(); }接口实现类 Runnable:
// 通过实现接口 Runnable 来创建线程 public class MyRunnable implements Runnable{ //run @Override public void run(){ for(int i = 0; i < 10;i++){ System.out.println(i); } } } // 调用 public static void main(String[] args){ // 1.创建一个任务对象 MyRunnable R1 = new MyRunnable(); // 2.创建一个线程,并为其分配一个任务 Thread t = new Thread(R1); // 3.执行 t.start(); }匿名内部类的写法
//只临时使用一次,没有起名字,但也是个线程。 // 还是继承了 Thread, 属于 第一种创建方式 new Thread(){ @Override public void run(){ for(int i = 0;i<10;i++){ System.out.pintln(i); } } }.start();
线程休眠设置线程名称:
// MyRunnable 这是我自定义的一个线程类 new Thread(new MyRunnable(),"线程一");获取线程名称:
//通过Thread.currentThread() 这个静态方法获取 Thread.currentThread().getName();
线程阻塞线程休眠
// 休眠 1秒 Thread.sleep(1000);
线程的中断所有比较消耗时间的操作,可以理解为阻塞。
比如常见的,读取文件操作,它会导致线程一直等待在那个位置,代码不会继续执行,除非文件读取结束。
还有,接受用户输入,它会等待在那个位置,等待用户输入。用户不输入,代码不会继续执行。
守护线程一个线程是一个独立的执行路径,他是否应该结束,应该由其自身决定。
stop(); // 已过时
现在,通过给线程打标记的方式 。通过这个方法就给这个属性附上值了,就算打上标记了。在某些特定的情况下,它会查看这个标记,如果有,那么就会抛出一个异常。
t1.interrupt();例子:
//通过给 线程Thread添加 interrupt(); 线程内部会进入 catch模块,再添加return 线程就会结束 t1.interrupt(); static class MyRunnable implements Runnable{ @Override public void run() { for (int i = 0;i<10;i++){ System.out.println(Thread.currentThread().getName()+":"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { // e.printStackTrace(); System.out.println("发现了中断标记,线程结束"); return; } } } }这个是完整代码
public class Demo { public static void main(String[] args) throws InterruptedException { // 线程中断 // 一个线程是一个独立的执行路径,他是否应该结束,应该由它自身决定。 //创建线程 Thread t1 = new Thread(new MyRunnable()); t1.start(); // mian 中同步走一个循环 for (int i =0;i<5;i++){ System.out.println(Thread.currentThread().getName()+":"+i); try{ Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } } // 给 t1.线程打标记。如果此时线程正在运行,就会发现标记,并且安排自己的后事 t1.interrupt(); }//main static class MyRunnable implements Runnable{ @Override public void run() { for (int i = 0;i<10;i++){ System.out.println(Thread.currentThread().getName()+":"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { // e.printStackTrace(); System.out.println("发现了中断标记,线程结束"); return; } } }//run } }
首先需要知道线程分为 守护线程 和 用户线程
用户线程: 当一个进程不包含任何存活的用户线程时,进行结束。
守护线程: 守护用户线程的,当最后一个用户线程结束时,所有守护线程自动死亡。
当主线程结束时,哪怕自己的事情没有做完,守护线程也会结束
设置守护线程:
public static void main(String[] args){ Thread t1 = new Thread(new MyRunnable()); // 设置为守护线程 t1.setDaemon(true); t1.start(); // mian 中同步走一个循环 for (int i =0;i<5;i++){ System.out.println(Thread.currentThread().getName()+":"+i); try{ Thread.sleep(1000); }catch (InterruptedException e){ e.printStackTrace(); } } } public class MyRunnable implements Runnable{ @Override public void run() { for (int i = 0;i<10;i++){ System.out.println(Thread.currentThread().getName()+":"+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
关于线程安全问题举个例子:A、B、C 三个人去ATM机取钱,A先进去取1000块,插入卡运行一般,A脚滑了。。B看见直接插队把自己的卡塞进去,也要取1000块。过了一会机器,把A的钱吐了出来,B的钱也吐了出来。但是实际上ATM里仅有1000块,那么B的1000块哪来的???
一检查发现,ATM存款显示 -1000 块,这明显不合理。所以ATM出现了“线程不安全”问题,警察就把ATM机带走了。
实际上,计算机线程运行时,A刚跑完“3.”,B就已经在跑 “2.”,C可能已经也在“2.” 。这样跑下来 A带走1000,B带走1000,C又可以带走1000,线程不安全,ATM又得再坐一年牢。
根本原因是,多个线程同时运行,去争抢操作同一个数据,那么会导致数据会出现意外的情况。//1. ATM 的余额 private int count = 1000; @Override public void run() { while(count > 0){ System.out.println("取钱中"); //2. 机器正常运转 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //3. ATM -1000 count -= 1000; System.out.println("取出一千,ATM还剩"+count); } }解决线程方法某一个线程在运行的时候,其他线程不能插足。即排队执行
解决方法一 同步代码块 线程同步:synchromized//格式 synchromized(锁对象){ 要被锁的区域 }举个例子:(下面程序仅做学习 同步代码块学习。)
package 线程安全1同步代码块; public class Demo { public static void main(String[] args) { Object o = new Object(); // 线程不安全 //解决方法1. 同步代码块 // 格式: synchronized(锁对象){ // // } // 创建线程 1 2 3 Runnable run = new Ticket(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); } static class Ticket implements Runnable{ // 定义票的数量 10张 private int count = 10; private Object o = new Object(); @Override public void run() { while(true){ //标记代码块 synchronized (o) { if (count > 0) { // 卖票 System.out.println("正在准备卖票"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count--; System.out.println(Thread.currentThread().getName()+"出票成功,余票:"+count); }else { break; } }//synchronized }//while } } }解决方法二 同步方法//格式 public synchronized boolean method(){ //这里面的代码,被锁定。 // 线程排队执行 }举个例子:(三个窗口每个窗口售卖10张票,方法被锁定)
public class 线程安全2同步方法 { public static void main(String[] args) { Object o = new Object(); // 线程不安全 //解决方法1. 同步代码块 // 格式: synchronized(锁对象){ // // } // 创建线程 1 2 3 //Runnable run = new Ticket(); new Thread(new Ticket()).start(); new Thread(new Ticket()).start(); new Thread(new Ticket()).start(); } public static class Ticket implements Runnable{ // 定义票的数量 10张 private int count = 10; private Object o = new Object(); @Override public void run() { while(true){ boolean flag = sale(); if(!flag){ break; } }//while } public synchronized boolean sale(){ if (count > 0) { // 卖票 System.out.println("正在准备卖票"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count--; System.out.println(Thread.currentThread().getName()+"出票成功,余票:"+count); return true; }else{ return false; } }//sale }// Ticket }解决方法三 显式锁Lock// 显式锁Lock子类, ReentrantLock() Lock lock = new ReentrantLock(); lock.lock(); lock.unlock();举个例子:
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class 显式锁 { public static void main(String[] args) { Object o = new Object(); // 线程不安全 //解决方法1. 同步代码块 // 格式: synchronized(锁对象){ // // } // 创建线程 1 2 3 Runnable run = new Ticket(); new Thread(run).start(); new Thread(run).start(); new Thread(run).start(); } static class Ticket implements Runnable{ // 定义票的数量 10张 private int count = 10; // 显式锁 private Lock lock = new ReentrantLock(); @Override public void run() { while(true){ // 上锁 ---------------------- lock.lock(); if (count > 0) { // 卖票 System.out.println("正在准备卖票"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } count--; System.out.println(Thread.currentThread().getName()+"出票成功,余票:"+count); }else{ break; } // 解锁 ------------------------- lock.unlock(); }//while } }// Ticket }公平锁和不公平锁公平锁 :先来先等,排队执行。
不公平锁:抢占式,谁抢上就是谁的。
显示锁实现公平锁的代码
// ReentantLock() 传一个 true ,表示公平锁 Lock lock = new ReentantLock(true);



