(五)、并发(锁)
18.守护(daemon)线程
1.线程分为用户线程和守护线程 2.虚拟机必须确保用户线程执行完毕 3.虚拟机不用等待守护线程执行完毕 4.eg:后台垃圾回收
基本思路: 守护线程是一直运行,但当用户线程结束后,后台也就结束了
public class Demo15 { public static void main(String[] args) { God god=new God(); You_daemon you_daemon=new You_daemon(); Thread thread=new Thread(god); thread.setDaemon(true); //默认是false表示用户线程,正常的都是用户线程 thread.start(); //守护线程开启 new Thread(new You_daemon()).start(); } } class God implements Runnable{ @Override public void run() { while (true){ System.out.println("上帝一只保佑着你!"); } } } class You_daemon implements Runnable{ @Override public void run() { for (int i=0;i<10;i++) { System.out.println("人类一生都要快乐!"); } System.out.println("========goodbye world ========"); } }
19.线程同步(队列+锁)
1.发生条件: 多个线程操作同一个资源 2.由于统一进程的多个线程共享一块储存空间,在带来方便的同时,也带来了访问 冲突的问题,为了保证数据在方法中被访问时的正确性,在访问时假如(锁机制) synchronized,当一个线程获得对象的排他锁,独占资源,其他线程必须等待, 使用后释放锁即可. 3.一个线程持有锁会导致其他所有需要此锁的线程挂起 4.在多线程竞争下,加锁,释放锁会导致比较多的上下文切换 和 调度延时, 引起性能问题 5.如果一个优先级高的线程等待一个优先级低的线程释放锁们会导致优先级倒置, 引起性能问题。
19.1 线程三大不安全案列
买票的问题:
(1).线程不安全,有负数。
public class Sercity_one { //不安全的买票 public static void main(String[] args) { Buy_tickets buy_tickets=new Buy_tickets(); new Thread(buy_tickets,"你").start(); new Thread(buy_tickets,"我").start(); new Thread(buy_tickets,"他").start(); } } class Buy_tickets implements Runnable{ //初始化票 private int ticket=10; boolean flag=true; @Override public void run() { while (flag){ buy(); } }//买票 public void buy(){ if(ticket<=0){ flag=false; return; }else { //进行网络延迟,放大问题的发生性 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } //买票 System.out.println(Thread.currentThread().getName()+"买到了"+ticket--); } } }
银行取钱的问题:
假如两个人同时取同一笔钱,那么银行可能会变成负
public class Sercity_two { public static void main(String[] args) { //账户初始化 account account_two=new account(100,"结婚基金"); //线程类的初始化 Bank bank=new Bank(account_two,50,"新浪"); Bank bank_one=new Bank(account_two,100,"新娘"); bank.start(); bank_one.start(); } } //账户 class account { int money; //存款 String name; //账户名字 public account(int money, String name) { this.money = money; this.name = name; } } //银行 class Bank extends Thread{ account account_one; int get_money; //取了多少钱 int now_money; //现在手上还有多少钱 //利用构造器进行 public Bank(account account_one,int get_money,String name){ super(name); this.account_one=account_one; this.get_money=get_money; } //取钱操作 public void run(){ //判断有没有钱 if(account_one.money-get_money<0){ System.out.println(Thread.currentThread().getName()+"余额不足,取不出"); return; } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //卡内余额 account_one.money=account_one.money-get_money; //你手上的钱 now_money=now_money+get_money; //卡内余额为: System.out.println(account_one.name+"余额为:"+account_one.money); System.out.println(Thread.currentThread().getName()+"手里的钱:"+now_money); } }
集合不安全
实际数目:会出现实际集合数目减少的问题
import java.util.ArrayList; import java.util.List; public class Sercity_three { public static void main(String[] args) { List<String> list=new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ list.add(Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } System.out.println(list.size()); } }
20.同步方法/同步块
同步方法: public synchroniz ed void method(int args){} synchronized 方法控制对 对象 的访问,每个对象都有对应的一把锁, 每个synchronized方法都必须获得调用该方法的对象的锁才能执行, 否则线程会被阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁, 后面被阻塞得线程才能获得这个锁,继续执行。 缺点: 如果加了一个synchroniz的方法,会影响执行效率,锁太多的话,会影响资源。 《锁的对象一定要是变化的对象》 同步块的格式: synchronized (obj){} 1.obj称之为:同步监视器 2.obj: 可以是任何对象,但是推荐使用共享资源作为同步监视器 3.同步方法中无需指定同步监听器,因为同步方法的同步监视器就是this,就是 这个对象本身,或则是class【反射中讲解】 4.同步监视器的执行过程: (1).第一个线程访问,锁定同步监视器,执行其中的代码 (2).第二个线程访问,发现同步监视器被锁定,无法访问 (3).第一个线程访问完毕,解说同步监视器 (4).第二个线程访问,发现同步监视器没有锁,然后锁定并访问