多线程
cpu只能干一件事,好像一次干很多次,是因为处理速度很快,一边一点的做 ,切换着做,就是多线程
进程和线程
进程process是操作系统的一个任务,有一块独立的内存
线程是进程的几个任务,process创建,os会自动申请主线程
并发和并行
并发
通过cpu调度算法,多个线程并发运行,不是同时运行 ,OS划分时间,每个进程轮流着干,用TPS或者QPS来反应这个系统的处理能力
并行
多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
线程生命周期
new => runnable => run =>dead
runnable 和run之间有blockThread 在lang包中
不可能切换一次一次
Thread1 t1 = new Thread1();Thread2 t2 = new Thread2();t1.start();//启动用start,进入runnale状态,一旦获得cpu时间就会运行 t2.start();class Thread1 extends Thread {//run是干的活 public void run(){ for(int a =0;a<3;a++) { System.out.println("hello"); } }}class Thread2 extends Thread { public void run(){ for(int a =0;a<3;a++) { System.out.println("Hi"); } }}
上面写法的不足与改进(实现runnable接口,重写run)
- java单继承,继承了thread后无法继承其他类
- 执行的run和线程有耦合关系,重用性不好
Thread3 task3 = new Thread3();Thread4 task4 = new Thread4();Thread t3 = new Thread(task3);Thread t4 = new Thread(task4);t3.start();t4.start();class Thread3 implements Runnable { public void run() { for(int a =0;a<3;a++) { System.out.println("Hi"); } }}class Thread4 implements Runnable { public void run() { for(int a =0;a<3;a++) { System.out.println("hallo"); } }}
匿名内部类写法
new Thread(new Runnable() { public void run() { for(int a =0;a<3;a++) { System.out.println("I am huahuadavids"); } }}).start();
查看线程信息 Thread.currentThread();
返回在方法中的线程
Thread main = Thread.currentThread();System.out.println(main);// Thread[main,5,main] 名字优先级方法System.out.println(main.getId());// 线程idSystem.out.println(main.getName());//线程名字
线程优先级 1-10(10最高)
线程的时间片是由线程调用被动的,理论上,优先级越高,获取时间片的次数越多
p(Thread.MIN_PRIORITY);// 1 p(Thread.NORM_PRIORITY);// 5 p(Thread.MAX_PRIORITY);// 10 Thread t1 = new Thread(new Runnable() { public void run() { for(int i=0;i<10000;i++) { p("t11111"); } }});Thread t2 = new Thread(new Runnable() { public void run() { for(int i=0;i<10000;i++) { p("t22222"); } }});Thread t3 = new Thread(new Runnable() { public void run() { for(int i=0;i<10000;i++) { p("t33333"); } }});t1.setPriority(1);t2.setPriority(5);t3.setPriority(10);t1.start();t2.start();t3.start();
sleep
让线程进入阻塞,超时后进入runnale状态
Thread rose = new Thread(new Runnable() { public void run() { for(int a=0;a<10;a++) { System.out.println("I will jump"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("I jump !!! "); }});
守护线程
- 守护线程是后台线程
- 进程的前台线程都结束,后台线程也必须结束
- 当进程只剩下守护线程,所有程序线程必须over
- GC就是守护线程
Thread rose = new Thread(new Runnable() { public void run() { for(int a=0;a<10;a++) { System.out.println("I will jump"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("I jump !!! "); }});Thread jack = new Thread(new Runnable() { public void run() { while(true) { System.out.println("You jump, I jump"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }});jack.setDaemon(true);rose.start();jack.start();System.out.println("main结束");
- main的线程首先结束
- rose线程结束
- jack结束
- GC是死循环
- yield方法 主动让出cpu时间,进入Runnable
join方法
join方法可以让他的线程进入block,一直到该方法的线程完成工作,一般用作多个线程的同步工作
//在匿名内部类中,使用保存匿名内部类的变量,必须是final public static boolean isFinish = false;public static void main(String[] args) { final Thread download = new Thread() { public void run() { System.out.println("下载开始"); for(int a =0;a<101;a+=10) { System.out.println("正在下载:"+ a+"%"); try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("下载结束"); isFinish = true; } }; Thread show = new Thread(new Runnable() { public void run() { try { download.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("开始加载图片"); if(!isFinish) { throw new RuntimeException("图片未下载完毕"); } System.out.println("结束加载图片"); } }); download.start(); show.start();}
线程安全
线程的调度顺序不影响任何结果 ,cpu调用时间的不确定造成了结果的可能问题,造成了抢资源 ,java多线程导致的安全性问题,java中的锁机制是解决这个问题,就是排队,不利于效率
解决线程安全问题
加锁
锁能使其保护的代码以串行的形式来访问,当给一个复合操作加锁后,能使其成为原子操作。一种错误的思想是只要对写数据的方法加锁,其实这是错的,对数据进行操作的所有方法都需加锁,不管是读还是写。
加锁时需要考虑性能问题,不能给整个方法加锁synchronized就了事了,应该将方法中不影响共享状态且执行时间比较长的代码分离出去。
加锁的含义不仅仅局限于互斥,还包括可见性。为了确保所有线程都能看见最新值,读操作和写操作必须使用同样的锁对象。
不共享状态
- 无状态对象:无状态对象一定是线程安全的,因为不会影响到其他线程。
- 线程关闭: 仅在单线程环境下使用。
不可变对象
使用final修饰的对象保证线程安全,由于final修饰的引用型变量(除String外)不可变是指引用不可变,但其指向的对象是可变的,所以此类必须安全发布,也即不能对外提供可以修改final对象的接口
线程里的run方法,抛出一个异常到run之外,这个线程就被干掉了
线程安全demo
class Table { private int beans = 20; public int getBeans() { if(beans == 0) { throw new RuntimeException("no beans"); } Thread.yield(); return beans--; }}final Table table = new Table();Thread p1 = new Thread() { public void run() { while(true) { int bean = table.getBeans(); Thread.yield(); System.out.println(getName() + " " + bean); } }};Thread p2 = new Thread() { public void run() { while(true) { int bean = table.getBeans(); Thread.yield(); System.out.println(getName() + " " + bean); } }};p1.start();p2.start();/*Thread-0 -264733Thread-0 -264734Thread-0 -264735Thread-0 -264736Thread-0 -264737Thread-0 -264738Thread-0 -264739*/
synchronized
把对象上锁,只有钥匙才能进 ,否则就在阻塞 ,就是把抢资源变成排队获取资源
class Table { private int beans = 20; public synchronized int getBeans() { if(beans == 0) { throw new RuntimeException("no beans"); } Thread.yield(); return beans--; }}
同步块
缩小范围,提高效率
- 原来的
class shop { public synchronized void buy() { try { Thread t = Thread.currentThread(); System.out.println(t.getName() + "进入商店"); Thread.sleep(3000); System.out.println(t.getName() + "进入试衣间"); Thread.sleep(3000); System.out.println(t.getName() + "付款离开"); }catch (Exception e) { e.printStackTrace(); } }}shop shop = new shop();Thread t1 = new Thread() { public void run() { shop.buy(); }};Thread t2 = new Thread() { public void run() { shop.buy(); }};t1.start();t2.start();
- 改进的
class shop { public void buy() { try { Thread t = Thread.currentThread(); System.out.println(t.getName() + "进入商店"); Thread.sleep(3000); synchronized(this) { System.out.println(t.getName() + "进入试衣间"); Thread.sleep(3000); } System.out.println(t.getName() + "付款离开"); }catch (Exception e) { e.printStackTrace(); } }}
同步和异步
线程排队就是同步,反之亦然
静态方法的同步,一定有效果
静态方法的同步,一定有效果 和对象无关
互斥锁
- synchronized叫同步锁,也叫互斥锁
- 修饰方法就是锁这个的对象
- 同步锁,2个线程不可以同一段代码
- 互斥锁, synchronized修饰的代码不同
class boo { public synchronized void ma() { try { Thread t = Thread.currentThread(); String name = t.getName(); System.out.println(name + "在执行A方法"); Thread.sleep(2000); System.out.println(name + "结束执行A方法"); }catch(Exception e) { e.printStackTrace(); } } public void mb() { try { Thread t = Thread.currentThread(); String name = t.getName(); System.out.println(name + "在执行b方法"); Thread.sleep(2000); System.out.println(name + "结束执行b方法"); }catch(Exception e) { e.printStackTrace(); } }}boo obj = new boo();Thread t1 = new Thread() { public void run() { obj.ma(); }};Thread t2 = new Thread() { public void run() { obj.ma(); }};t1.start();t2.start();
线程安全和集合
- vector是同步的
- ArrayList,linkedlist和hashSet不是同步的
- 就算是线程安全的集合,对于集合的操作和遍历,不是互斥的,需要自行维护
Listlist = new ArrayList ();list.add("one");list.add("one1");list.add("one");System.out.println(list);list = Collections.synchronizedList(list);System.out.println(list);Set set = new HashSet (list);set = Collections.synchronizedSet(set);Map map = new HashMap ();map.put("math", 100);map = Collections.synchronizedMap(map);
线程池
- 控制线程数量
- 重用线程
- 大量使用线程和频繁创建销毁线程
固定大小的线程池
固定大小,多余的分配的线程在队列中
- showdown 把线程池中缓存的任务都干完再停
- shoudownNow 马上停
blockingQueue 双缓冲队列
解决了队列的安全问题,效率也可以