多线程技术(同步异步,并发并行)守护线程(java垃圾回收机制)
多线程技术概述
每个线程都有自己的栈空间,并用一份堆内存 1.同步和异步
同步:排队执行,效率低但是安全.
异步:同时执行,效率高但是数据不安全. 2.并发与并行
并发:指两个或多个时间在同一个时间段内发生.
并行:指两个或多个时间在同一时刻发生(同时发生) 3.执行步骤,有利于理解线程时用来做什么的 public class Test { public static void main(String[] args) throws InterruptedException { //1.创建一个任务对象 Xiancheng x1 = new Xiancheng(); //2.创建一个线程,并为其分配一个任务 Thread t = new Thread(x1); //3.执行这个线程 t.start(); } } 4.多用Runnable
5.线程对象可以打标记package com.kkb; public class Demo01_ThreadInterrupt { public static void main(String[] args) { Thread t = new Thread(new MyThings()); t.start(); for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName()+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } t.interrupt();//给线程添加标记 } } class MyThings 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) {//中断异常-->>如果这个事物正在执行的线程有了中断标记,那么就进入到catch块中 //System.out.println("虽然我添加了标记来到了这里,但是,你没有让我死亡哈哈哈哈"); //加上return资源就释放了 System.out.println("添加了interrupt标记,后面返回return,我结束了"); return; } } } } 6.守护线程
线程:分为守护线程和用户线程
用户线程:当一个进程不包含任何的存货线程时,进程结束
守护线程:守护用户的线程,当最后一个用户线程结束时,所有守护线程自动死亡 package com.kkb; public class Demo01_ThreadInterrupt { public static void main(String[] args) { Thread t = new Thread(new MyThings()); t.setDaemon(true);//设置t为守护线程,当这个应用(进程中最后一个用户线程死亡的时候,守护线程自动死亡) t.start(); for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName()+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } // t.interrupt();//给线程添加标记 } } class MyThings 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) {//中断异常-->>如果这个事物正在执行的线程有了中断标记,那么就进入到catch块中 //System.out.println("虽然我添加了标记来到了这里,但是,你没有让我死亡哈哈哈哈"); //加上return资源就释放了 System.out.println("添加了interrupt标记,后面返回return,我结束了"); return; } } } } 7.线程安全问题
概述: * 为什么会导致线程不安全: * 首先假定一种情况:当事物中余票为1时,三个线程进入来买票 * 1.A线程:看到conut>0,进入,正在出票的时候,count还没来得及做数据的变更操作 * 2.B线程:这个时候,B也进来了,因为conut还没有变更 * ..... * 这就是多线程,进入产生时间偏的问题-->>这里为了更好的模拟实际中的放票状态,就引入了一个Thread.sleep,使线程进行休眠的操作package com.kkb; /** * 为什么会导致线程不安全: * 首先假定一种情况:当事物中余票为1时,三个线程进入来买票 * 1.A线程:看到conut>0,进入,正在出票的时候,count还没来得及做数据的变更操作 * 2.B线程:这个时候,B也进来了,因为conut还没有变更 * ..... * 这就是多线程,进入产生时间偏的问题-->>这里为了更好的模拟实际中的放票状态,就引入了一个Thread.sleep,使线程进行休眠的操作 * */ public class UnSafe { public static void main(String[] args) { Runnable r = new Ticket(); Thread t1 = new Thread(r); Thread t2 = new Thread(r); Thread t3 = new Thread(r); t1.start(); t2.start(); t3.start(); } static class Ticket implements Runnable{ private int conut = 10; @Override public void run() { while (conut > 0){ System.out.println("正在准备卖票"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } conut--; System.out.println("余票:"+conut); } } } } 8.synchronized锁
注意:看同一把锁!!!!
同步代码块和同步方法都是隐式锁 1.同步代码块package com.kkb; /** * 所谓上锁:就是在一个线程正在执行任务时,其他的对象只能在外面排队,因为这个事物现在的情况对外是不开放的 * 指定事物中的一个锁对象,在一个线程进入时,这个对象就会被标记上一把锁 * 要看同一把锁 */ public class Safe_Synchronized { public static void main(String[] args) { Runnable r = new Ticket(); Thread t1 = new Thread(r); Thread t2 = new Thread(r); Thread t3 = new Thread(r); t1.start(); t2.start(); t3.start(); } static class Ticket implements Runnable{ private int conut = 10; private Object o = new Object(); //注意这里的o为了保证这个事物中存在一个可以上锁的对象 @Override public void run() { while (true) { synchronized (o) { if (conut > 0) { System.out.println(Thread.currentThread().getName() + "正在准备卖票"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } conut--; System.out.println("余票:" + conut); }else { break; } } } } } } 2.设计为同步方法 * 注意这里面锁方法的时候,锁的对象就是这个方法饿地址上加锁,一个一个进入此方法 * 我们打印出来的this就是:com.kkb.Safe_Synchronized_To_method$Ticket@3694916f * 这个方法的地址 只需要在这个方法上加上synchronized进行标志package com.kkb; /** * 注意这里面锁方法的时候,锁的对象就是这个方法饿地址上加锁,一个一个进入此方法 * 我们打印出来的this就是:com.kkb.Safe_Synchronized_To_method$Ticket@3694916f * 这个方法的地址 */ public class Safe_Synchronized_To_method { public static void main(String[] args) { Runnable r = new Ticket(); Thread t1 = new Thread(r); Thread t2 = new Thread(r); Thread t3 = new Thread(r); t1.start(); t2.start(); t3.start(); } static class Ticket implements Runnable{ private int conut = 10; // private Object o = new Object(); //不需要这个对象了 //因为锁方法的时候锁的其实就是this,这个save方法的this //注意这里的o为了保证这个事物中存在一个可以上锁的对象 //com.kkb.Safe_Synchronized_To_method$Ticket@3694916f @Override public void run() { while (true) { boolean sale = sale(); if (!sale){//如果没票了就结束 break; } } } public synchronized boolean sale(){ if (conut > 0) { System.out.println(Thread.currentThread().getName() + "正在准备卖票"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } conut--; System.out.println("余票:" + conut); System.out.println(this); }else { return false; } return true; } } } 3.显示锁Lock l = new ReentrantLock();package com.kkb; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * */ public class Safe_Synchronized_Lock { public static void main(String[] args) { Runnable r = new Ticket(); Thread t1 = new Thread(r); Thread t2 = new Thread(r); Thread t3 = new Thread(r); t1.start(); t2.start(); t3.start(); } static class Ticket implements Runnable{ private int conut = 10; //给一把显式锁 private Lock l = new ReentrantLock();//Lock锁接口的实现类 @Override public void run() { while (true) { //每次一个线程进入就会加上一个锁,其他线程无法进入 l.lock();//加锁 boolean sale = sale(); l.unlock();//解锁 if (!sale){//如果没票了就结束 break; } } } public boolean sale(){ if (conut > 0) { System.out.println(Thread.currentThread().getName() + "正在准备卖票"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } conut--; System.out.println("余票:" + conut); }else { return false; } return true; } } } 4.公平锁和不公平锁
就是在声明显式锁的时候加上一个true.
排队一个一个进,不会出现线程争先恐后的,谁抢到就是谁的 Lock l = new ReentrantLock(true);Thread-0正在准备卖票 余票:9 Thread-1正在准备卖票 余票:8 Thread-2正在准备卖票 余票:7 Thread-0正在准备卖票 余票:6 Thread-1正在准备卖票 余票:5 Thread-2正在准备卖票 余票:4 Thread-0正在准备卖票 余票:3 Thread-1正在准备卖票 余票:2 Thread-2正在准备卖票 余票:1 Thread-0正在准备卖票 余票:0 9.多线程交互问题
线程的wait和线程的唤醒notifyAll
再引入一个变量交替执行 10.线程池
如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁的船舰线程和销毁线程需要时间.线程池就是一个可以容纳多个线程的容器,池中的线程可以反复的使用,省区了频繁创建线程对象的操作,节省了大量的时间和资源.
1.创建线程
2.创建任务
3.执行任务
4.关闭线程
1.缓存线程池
2.定长线程池
3.单线程池
4.周期定长线程池