简历模板做的最好的是哪个网站,wordpress数据库加速,网络推广项目代理,网站制作多少钱一个月多线程#xff1a; 多线程就是同时执行多个应用程序#xff0c;需要硬件的支持同时执行#xff1a;不是某个时间段同时#xff0c;cpu切换的比较快#xff0c;所有用户会感觉是在同时运行 并发与并行#xff1a; 并行(parallel)#xff1a;指在同一时刻#xff0c;有多…多线程 多线程就是同时执行多个应用程序需要硬件的支持同时执行不是某个时间段同时cpu切换的比较快所有用户会感觉是在同时运行 并发与并行 并行(parallel)指在同一时刻有多条指令在多个处理器上同时执行。并行必须借助于多核cpu实现 并发(concurrency)指在同一时刻只能有一条指令执行但多个进程指令被快速的轮换执行使得在宏观上具有多个进程同时执行的效果但在微观上并不是同时执行的只是把时间分成若干段通过cpu时间片轮转使多个进程快速交替的执行。
程序、线程、进程
程序是指编译好的可执行文件程序启动的时候进程作为支撑 进程是正在运行的程序(比如360杀毒软件)进程可以产生多线程 独立性进程是一个能独立运行的基本单位同时也是系统分配资源和调度的独立单位 动态性进程的实质是程序的一次执行过程进程是动态产生动态消亡的 并发性任何进程都可以同其他进程一起并发执行 进程基本的状态有5种分别为初始态、就绪态、运行态、挂起态、终止态其中初始态为进程准备阶段常与就绪态结合来看。 线程是程序正在做的事情线程是进程的单个控制流比如360的杀毒扫描木马 单线程一个进程如果只有一条执行路径则称为单线程程序 多线程一个进程如果有多条执行路径则称为多线程程序 进程与线程的区别 进程有独立的内存空间进程中的数据存放空间堆空间和栈空间是独立的至少有一个线程。线程堆空间是共享的栈空间是独立的线程消耗的资源比进程小的多。 多线程同时执行原理: 比如我们同时运行qq和微信其实不是同时运行的而是CPU在多个线程间快速切换,造成同时执行的假象 多线程的好处: 可以同时执行多个任务可以提高资源的利用率(CPU/网络) 线程越多越好吗: 1.创建和销毁线程需要消耗CPU和内存资源 2.线程太多,CPU需要在大量的线程间切换,造成资源的浪费 进程与并发 在使用进程实现并发时会出现以下问题
系统开销比较大占用资源比较多开启进程数量比较少。在unix/linux系统下还会产生孤儿进程和僵尸进程。正常情况下子进程是通过父进程fork创建的子进程再创建新的进程。并且父进程永远无法预测子进程 到底什么时候结束。 当一个 进程完成它的工作终止之后它的父进程需要调用系统调用取得子进程的终止状态。
孤儿进程 父进程比子进程先结束子进程成为孤儿进程子进程的父进程成为init进程称为init进程领养孤儿进程。 僵尸进程 僵尸进程: 进程终止父进程尚未回收子进程残留资源PCB存放于内核中变成僵尸Zombie进程。 WindowsLinux进程 Windows下的进程和Linux下的进程是不一样的它比较懒惰从来不执行任何东西只是为线程提供执行环境。然后由线程负责执行包含在进程的地址空间中的代码。当创建一个进程的时候操作系统会自动创建这个进程的第一个线程成为主线程。 创建线程
实现多线程有三种方法 继承Thread类实现Runnable接口实现Callable接口 开启线程
方法名说明void run()在线程开启后此方法将被调用执行void start()使此线程开始执行Java虚拟机会调用run方法()
为什么要重写run()方法 因为run()是用来封装被线程执行的代码 run()方法和start()方法的区别 run()封装线程执行的代码直接调用相当于普通方法的调用并没有开启线程start()启动线程然后由JVM调用此线程的run()方法 继承Thread
实现步骤 定义一个类MyThread继承Thread类在MyThread类中重写run()方法创建MyThread类的对象启动线程 多线程类
public class ThreadDemo extends Thread {// run是用来封装被线程执行的代码的一定要重写Overridepublic void run() {for (int i 0; i 10; i) {System.out.println(线程启动 i);}}
}测试类
public class TestThread {public static void main(String[] args) {// 创建线程对象ThreadDemo thread1 new ThreadDemo();ThreadDemo thread2 new ThreadDemo();// 开启线程thread1.start();thread2.start();}
}实现Runnable
实现步骤 定义一个类MyRunnable实现Runnable接口在MyRunnable类中重写run()方法创建MyRunnable类的对象创建Thread类的对象把MyRunnable对象作为构造方法的参数启动线程 对象类
public class RunnableDemo implements Runnable{Overridepublic void run() {for (int i 0; i 10; i) {System.out.println(线程启动 i);}}
}测试类
public class TestRunnable {public static void main(String[] args) {// 创建参数对象RunnableDemo r1 new RunnableDemo();RunnableDemo r2 new RunnableDemo();// 创建线程Thread thread1 new Thread(r1);Thread thread2 new Thread(r2);// 开启线程thread1.start();thread2.start();}
}实现Callable接口
实现步骤 定义一个类MyCallable实现Callable接口在MyCallable类中重写call()方法创建MyCallable类的对象创建Future的实现类FutureTask对象把MyCallable对象作为构造方法的参数创建Thread类的对象把FutureTask对象作为构造方法的参数启动线程再调用get方法就可以获取线程结束之后的结果。 构造方法
方法名说明Thread(Runnable target)分配一个新的Thread对象Thread(Runnable target, String name)分配一个新的Thread对象
对象类
public class CallDemo implements CallableString {Overridepublic String call() throws Exception {for (int i 0; i 10; i) {System.out.println(线程执行 i);}// 返回值表示运行完以后的结果return 执行完毕;}
}测试类
public class TestCall {public static void main(String[] args) throws ExecutionException, InterruptedException {// 创建对象线程要执行的代码CallDemo call new CallDemo();// 获取线程执行完毕的结果可以作为参数传给ThreadFutureTaskString sft new FutureTask(call);Thread thread new Thread(sft);thread.start();// get获取线程运行后的结果如果在线程没开启前就获取get方法会一直等待所以get方法要写在start后面System.out.println(sft.get()); }
}三种实现方式对比
实现Runnable、Callable接口 好处: 扩展性强实现该接口的同时还可以继承其他的类 缺点: 编程相对复杂不能直接使用Thread类中的方法 继承Thread类 好处: 编程比较简单可以直接使用Thread类中的方法 缺点: 可以扩展性较差不能再继承其他的类 获取与设置线程名称
方法名说明void setName(String name)将此线程的名称更改为等于参数nameString getName()返回此线程的名称Thread currentThread()返回对当前正在执行的线程对象的引用
对象类
public class ThreadDemo extends Thread {// 要写构造否则不能传线程名称public ThreadDemo() {}public ThreadDemo(String name) {super(name);}Overridepublic void run() {for (int i 0; i 10; i) {System.out.println(getName() i);// 获取当前线程对象}// 线程没有设置名字的话返回的是默认的名字每个线程都是有默认名字的System.out.println(当前执行的线程是 Thread.currentThread().getName());}
}测试类
public class Test {public static void main(String[] args) {ThreadDemo t1 new ThreadDemo();t1.start();t1.setName(线程一);// 通过构造设置对象要创建有参构造ThreadDemo t2 new ThreadDemo(线程二);t2.start();// 如果是通过接口创建线程那么是不能用getname的所以currentThread就可以代替了System.out.println(Thread.currentThread().getName());}
}线程睡眠
方法名说明static void sleep(long millis)使当前正在执行的线程停留暂停执行指定的毫秒数
对象类
public class DemoRunnable implements Runnable {Overridepublic void run() {for (int i 0; i 10; i) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() i);}}
}测试类
public class Test {public static void main(String[] args) {DemoRunnable dr new DemoRunnable();Thread thread new Thread(dr);thread.start();}
}线程优先级 分时调度所有线程轮流使用 CPU 的使用权平均分配每个线程占用 CPU的时间抢占式调度优先让优先级高的线程使用CPU如果线程的优先级相同那么会随机选择一个优先级高的线程获取的 CPU 时间片相对多一些 Java使用的是抢占式调度模型线程优先级高只是枪战CPU的几率更大不代表一定优先执行 方法名说明final int getPriority()返回此线程的优先级final void setPriority(int newPriority)更改此线程的优先级线程默认优先级是5线程优先级的范围是1-10
public class Test {public static void main(String[] args) {DemoRunnable dr new DemoRunnable();Thread thread new Thread(dr);thread.start();// final int getPriority() 返回此线程的优先级System.out.println(thread.getPriority());// final void setPriority(int newPriority) 更改此线程的优先级线程默认优先级是5线程优先级的范围是1-10thread.setPriority(10);System.out.println(thread.getPriority());}
}守护线程
方法名说明void setDaemon(boolean on)将此线程标记为守护线程当运行的线程都是守护线程时Java虚拟机将退出
线程一
public class Thread1 extends Thread {Overridepublic void run() {for (int i 0; i 100; i) {System.out.println(getName() i);}}
}线程二
public class Thread2 extends Thread {Overridepublic void run() {for (int i 0; i 10; i) {System.out.println(getName() i);}}
}测试类
public class Test {public static void main(String[] args) {// 创建线程Thread1 t1 new Thread1();Thread2 t2 new Thread2();// 设置线程名字t1.setName(我女朋友);t2.setName(我);// 设置我女朋友为守护线程我要挂了我女朋友也不能继续活这就是家庭地位// 因为此时守护线程还有执行权cpu执行很快所以守护线程不是马上停止的要把执行权走完t1.setDaemon(true);t1.start();t2.start();}
}线程安全
卖票案例分析线程安全 重复票定义三个线程去卖100张票三个线程sleep后第一个线程醒来拿到CPU执行权此时将票减1为99刚减完线程二醒了。线程二拿到CPU执行权此时将票减1这时候是从99减1为98那这时候线程一也要变成98因为执行的是同一个线程对象。刚减完线程三醒了。线程二拿到CPU执行权此时将票减1这时候是从98减1为97…这时候就会出现重复票数 负数票此时票数为1线程1、2、3开始sleep线程1醒来后拿到CPU执行权做减1操作此时票数为0线程1被销毁。线程2醒过来然后拿到CPU执行权做减1操作此时票数为-1线程2被销毁。线程3醒过来拿到CPU执行权做减1操作此时票数为-2线程3被销毁。所以就会出现负数票问题 安全问题出现的条件 是多线程环境有共享数据有多条语句操作共享数据 如何解决多线程安全问题呢? 把多条语句操作共享数据的代码给锁起来让任意时刻只能有一个线程执行Java提供了同步代码块的方式来解决 例两个人同时上厕所但是就一个马桶于是用户A进入后用户B只能在外面等只有用户A出来用户B才能进去。 同步代码块 synchronized(任意对象){ 多条语句操作共享数据的代码 }// synchronized(任意对象)默认情况是打开的只要有一个线程进去执行代码锁就会关闭当线程执行完出来锁才会自动打开同步代码块的好处和弊端 好处解决了多线程的数据安全问题 弊端当线程很多时每个线程都会去判断同步上的锁很耗费资源会降低程序的运行效率 对象类
public class SellTicket implements Runnable {// 定义总票数private static int tickets 100;// 创建一个任意的对象private final Object object new Object();Overridepublic void run() {while (true) {synchronized (object) {if (tickets 0) {try {// 进来先睡一会Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() 正在出售第 tickets 张票);tickets--;} else {break;}}}}
}测试类
public class SellTicketDemo {public static void main(String[] args) {// 创建对象SellTicket st new SellTicket();// 创建线程并设置名字多线程同一个对象Thread t1 new Thread(st, 窗口一);Thread t2 new Thread(st, 窗口二);// 启动线程t1.start();t2.start();}
}同步方法
同步方法锁对象是this
修饰符 synchronized 返回值类型 方法名(方法参数) {方法体;}对象类
public class SellTicket2 implements Runnable {// 定义总票数private static int tickets 100;// 创建一个任意的对象private final Object object new Object();Overridepublic void run() {while (true) {// 同步方法// 判断当前线程是就调用方法然后判断票数是不是0不是就继续循环if (窗口一.equals(Thread.currentThread().getName())) {boolean result synchronizedMethod();if (result) {break;}}// 同步代码块if (窗口二.equals(Thread.currentThread().getName())) {// 因为同步方法的锁是this所以代码块也要是this才能都用一把锁synchronized (this) {if (tickets 0) {try {// 进来先睡一会Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() 正在出售第 tickets 张票);tickets--;} else {break;}}}}}private synchronized boolean synchronizedMethod() {if (tickets 0) {try {Thread.sleep(50);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() 正在出售第 tickets 张票);tickets--;// 不是最后一张就继续循环return false;} else {// 最后一张就返回true停止return true;}}
}测试类
public class SellTicketDemo {public static void main(String[] args) {SellTicket2 st new SellTicket2();Thread t1 new Thread(st, 窗口一);Thread t2 new Thread(st, 窗口二);t1.start();t2.start();}
}静态同步方法锁对象是类名.class
修饰符 static synchronized 返回值类型 方法名(方法参数) {方法体;}静态方法在方法前加staticthis换成类名.class就行了
Lock锁 为了更清晰的表达如何加锁和释放锁JDK5以后提供了一个新的锁对象Lock Lock是接口不能直接实例化可以用它的实现类ReentrantLock来实例化 ReentrantLock构造方法
方法名说明ReentrantLock()创建一个ReentrantLock的实例
加锁解锁方法
方法名说明void lock()获得锁void unlock()释放锁
对象类
public class Ticket implements Runnable {// 票的数量private int ticket 100;// 创建Lock锁private ReentrantLock lock new ReentrantLock();Overridepublic void run() {while (true) {try {// 获得锁lock.lock();if (ticket 0) {// 卖完了break;} else {Thread.sleep(30);ticket--;System.out.println(Thread.currentThread().getName() 在卖票还剩下 ticket 张票);}} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}}
}测试类 public static void main(String[] args) {Ticket ticket new Ticket();Thread t1 new Thread(ticket, 线程一);Thread t2 new Thread(ticket, 线程二);// 设置优先级t1.setPriority(10);t2.setPriority(5);t1.start();t2.start();}死锁 线程死锁是指由于两个或者多个线程互相持有对方所需要的资源导致这些线程处于等待状态无法前往执行 死锁产生的条件 1.有多个线程 2.有多把锁 3.有同步代码块嵌套 解决办法 干掉其中某个条件 public class Demo05 {public static void main(String[] args) {MyRunnable mr new MyRunnable();new Thread(mr).start();new Thread(mr).start();}
}class MyRunnable implements Runnable {Object objA new Object();Object objB new Object();/*嵌套1 objA嵌套1 objB嵌套2 objB嵌套1 objA*/Overridepublic void run() {synchronized (objA) {System.out.println(嵌套1 objA);synchronized (objB) {// t2, objA, 拿不到B锁,等待System.out.println(嵌套1 objB);}}synchronized (objB) {System.out.println(嵌套2 objB);synchronized (objA) {// t1 , objB, 拿不到A锁,等待System.out.println(嵌套2 objA);}}}
}生产者消费者
生产和消费主要是包含了两类线程 生产和消费也称为等待唤醒机制 生产者线程用于生产数据 消费者线程用于消费数据 实现 为了解耦生产者和消费者的关系通常会采用共享的数据区域就像是一个仓库 生产者生产数据之后直接放置在共享数据区中并不需要关心消费者的行为 消费者只需要从共享数据区中去获取数据并不需要关心生产者的行为 Object类的等待和唤醒方法
方法名说明void wait()导致当前线程等待直到另一个线程调用该对象的 notify()方法或 notifyAll()方法void notify()唤醒正在等待对象监视器的单个线程void notifyAll()唤醒正在等待对象监视器的所有线程
对象类
public class Desk {// 定义一个标记判断食物的状态为false表示没有食物true代表有食物private boolean flag;// 食物的个数private int count;// 锁对象private final Object lock new Object();public Desk() {}public Desk(boolean flag, int count) {this.flag flag;this.count count;}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag flag;}public int getCount() {return count;}public void setCount(int count) {this.count count;}public Object getLock() {return lock;}Overridepublic String toString() {return Desk{ flag flag , count count , lock lock };}
}生产者
public class Cooker extends Thread {private Desk desk;public Cooker(Desk desk) {this.desk desk;}Overridepublic void run() {while (true) {synchronized (desk.getLock()) {if (desk.getCount() 0) {break;} else if (!desk.isFlag()) {System.out.println(生产者正在生产食物);// 有食物就改状态让消费者去消费desk.setFlag(true);desk.getLock().notifyAll();} else {// 没有食物就线程等待唤醒try {desk.getLock().wait();} catch (InterruptedException e) {e.printStackTrace();}}}}}
}消费者
public class Foodie extends Thread {private Desk desk;public Foodie(Desk desk) {this.desk desk;}Overridepublic void run() {while (true) {synchronized (desk.getLock()) {if (desk.getCount() 0) {break;// 布尔类型变量的getset是is开头的} else if (desk.isFlag()) {System.out.println(消费者消费了一个食物);// 消费完毕将标记设为false当为false时可以让生产者去生产desk.setFlag(false);// 唤醒等待中的所有线程desk.getLock().notifyAll();// 消费一个减少一个desk.setCount(desk.getCount() - 1);} else {try {// 线程等待唤醒desk.getLock().wait();} catch (InterruptedException e) {e.printStackTrace();}}}}}
}测试类
public class Demo {public static void main(String[] args) {Desk desk new Desk(false, 10);Foodie foodie new Foodie(desk);Cooker cooker new Cooker(desk);foodie.start();cooker.start();}
}阻塞队列 常见BlockingQueue: ArrayBlockingQueue底层是数组,有界 LinkedBlockingQueue底层是链表,无界.但不是真正的无界,最大为int的最大值 BlockingQueue的核心方法:
方法名说明put(anObject):将参数放入队列,如果放不进去会阻塞take()取出第一个数据,取不到会阻塞
阻塞队列等待唤醒 阻塞队列底层是有自动加锁的但是运行起来可能打印出的是存两个打印两个这个是控制台打印的问题。 生产者者线程
public class Cooker extends Thread {private ArrayBlockingQueueString bd;public Cooker(ArrayBlockingQueueString bd) {this.bd bd;}Overridepublic void run() {while (true) {try {bd.put(汉堡包);System.out.println(厨师放入一个汉堡包);} catch (InterruptedException e) {e.printStackTrace();}}}
}消费者线程 private ArrayBlockingQueueString bd;public Foodie(ArrayBlockingQueueString bd) {this.bd bd;}Overridepublic void run() {while (true) {try {String take bd.take();System.out.println(吃货将 take 拿出来吃了);} catch (InterruptedException e) {e.printStackTrace();}}}测试类 public class Demo {public static void main(String[] args) {// 创建阻塞队列对象容量为1ArrayBlockingQueueString bd new ArrayBlockingQueue(1);Foodie f new Foodie(bd);Cooker c new Cooker(bd);f.start();c.start();}}