zoukankan      html  css  js  c++  java
  • Java 多线程系列02

    1. 线程安全

      synchronized的使用

      在多线程使用共享资源时,可以使用synchronized来锁定共享资源,使得同一时刻,只有一个线程可以访问和修改它,修改完毕后,其他线程才可以使用。

      当一个共享数据被synchronized修饰后,在同一时刻,其他线程只能等待,直到当前线程释放该锁。

      示例1  在不适用线程锁的情况下模拟账户取款操作

    public class AccountTest{
        public static void main(String[] args){    
            Account ac = new Account("actno-1",10000);
            AccountThread at1 = new AccountThread(ac);
            AccountThread at2 = new AccountThread(ac);
            at1.setName("t1");
            at2.setName("t2");
            at1.start();
            at2.start();
        }
    } 
    
    
    class AccountThread extends Thread{
        private Account act;
        
        public AccountThread(){}
        
        public AccountThread(Account act){
            this.act = act;
        }
        
        public void run(){
            act.withdraw(5000);
            System.out.println(Thread.currentThread().getName() + "对" + act.getActno() + "取款成功,余额" + act.getMoney());
            
        }
    }
    
    class Account{
        private String actno;
        private double money;
        public Account(){}
        public Account(String actno, double money){
            this.actno = actno;
            this.money = money;
        }
        
        public void setActno(String actno){
            this.actno = actno;
        }
        public String getActno(){
            return actno;
        }
        public void setMoney(double money){
            this.money = money;
        }
        public double getMoney(){
            return money;
        }
        
        //取款
        public void withdraw(double money){
            double begin = getMoney();
            double after = begin - money;
            try{
                Thread.sleep(1000);   //模拟取款过程中如果发生延迟,则会出现安全问题
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            setMoney(after);
        }
    }
    运行结果
    t1对actno-1取款成功,余额5000.0
    t2对actno-1取款成功,余额5000.0

      显然这个结果非预期的结果,两个线程对同一个账户取款,每次取5000,最后余额应该为0,但是余额还是5000。

      这是因为当t1线程还没执行完,就执行了t2线程,这样就会出现网络安全问题,要解决这个网络安全问题,我们就可以使用synchronized同步锁。

      

      synchronized原理

      当一个线程访问对象的synchronized方法或synchronized代码块时,其他线程对该对象的该synchronized代码块或synchronized方法的访问将会被阻塞。

      

      synchronized的三种作用方式

      修饰实例方法,作用与当前实例加锁,进入同步代码块要获得当前实例的锁;

      修饰静态方法,作用于当前类对象枷锁,进入同步代码块要获得当前类对象的锁;

      修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码块要获得给定对象的锁;

      示例2  对取款操作修改,使用synchronized修饰实例方法

    public class AccountTest{
        public static void main(String[] args){    
            Account ac = new Account("actno-1",10000);
            AccountThread at1 = new AccountThread(ac);
            AccountThread at2 = new AccountThread(ac);
            at1.setName("t1");
            at2.setName("t2");
            at1.start();
            at2.start();
        }
    } 
    
    
    class AccountThread extends Thread{
        private Account act;
        
        public AccountThread(){}
        
        public AccountThread(Account act){
            this.act = act;
        }
        
        public void run(){
            act.withdraw(5000);
            System.out.println(Thread.currentThread().getName() + "对" + act.getActno() + "取款成功,余额" + act.getMoney());
            
        }
    }
    
    class Account{
        private String actno;
        private double money;
        public Account(){}
        public Account(String actno, double money){
            this.actno = actno;
            this.money = money;
        }
        
        public void setActno(String actno){
            this.actno = actno;
        }
        public String getActno(){
            return actno;
        }
        public void setMoney(double money){
            this.money = money;
        }
        public double getMoney(){
            return money;
        }
        
        //取款
        public synchronized void withdraw(double money){
            double begin = getMoney();
            double after = begin - money;
            try{
                Thread.sleep(1000);   //模拟取款过程中如果发生延迟,则会出现安全问题
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            setMoney(after);
        }
    }
    运行结果
    t1对actno-1取款成功,余额5000.0
    t2对actno-1取款成功,余额0.0

      修饰示例方法,就是在方法的前面加synchronized,这时的共享对象就是this,同步范围是整个方法体,这样会扩大同步范围,效率较低。

      示例3  对取款操作修改,使用synchronized修饰代码块

    public class AccountTest01{
        public static void main(String[] args){
            Account act = new Account("actno-1",10000);
            AccountThread at1 = new AccountThread(act);
            AccountThread at2 = new AccountThread(act);
            at1.setName("t1");
            at2.setName("t2");
            at1.start();
            at2.start();
        }
    }
    
    class AccountThread extends Thread{
        private Account act;
        public AccountThread(){}
        public AccountThread(Account act){
            this.act = act;
        }
        public void run(){
            act.withdraw(5000);
            System.out.println(Thread.currentThread().getName() + "对" + act.getActno() + "取款成功,余额" + act.getMoney());
        }
    }
    
    class Account{
        private String actno;
        private double money;
        Object obj1 = new Object();
        
        public Account(){}
        public Account(String actno, double money){
            this.actno = actno;
            this.money = money;
        }
        
        public void setActno(String actno){
            this.actno = actno;
        }
        public String getActno(){
            return actno;
        }
        public void setMoney(double money){
            this.money = money;
        }
        public double getMoney(){
            return money;
        }
        
        public void withdraw(double money){
            //Object obj2 = new Object();
            synchronized(this){            //this可以作为共享对象,可以使t1和t2线程排队执行
            //synchronized(obj1){            //obj1也可作为共享对象
            //synchronized(obj2){              //obj2不可以作为共享对象
                double begin = getMoney();
                double after = begin - money;
                try{
                    Thread.sleep(1000);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                setMoney(after);
            }
        }
    }

      关于synchronized括号内的共享对象

      this和obj1都可以作为共享对象,可以让线程排队执行,所以就是线程的共享对象;

      obj2不可以作为共享对象,obj2是一个局部变量,每次执行都会创建一个对象,不是共享对象。

    2. 死锁

       死锁是两个线程都在等待彼此先执行完,多个线程共享同一资源时需要进行同步,以保证资源操作完整性,可能会产生死锁。

    public class ThreadTest13{
        public static void main(String[] args){
            Object o1 = new Object();
            Object o2 = new Object();
            //mt1线程和mt2线程共享o1和o2对象;
            MyThread1 mt1 = new MyThread1(o1,o2);
            MyThread2 mt2 = new MyThread2(o1,o2);
            mt1.start();
            mt2.start();
        }
    }
    
    class MyThread1 extends Thread{
        Object o1;
        Object o2;
        public MyThread1(Object o1, Object o2){
            this.o1 = o1;
            this.o2 = o2;
        }
        public void run(){
            synchronized(o1){
                try{
                    Thread.sleep(1000);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
            synchronized(o2){}
        }
    }
    
    class MyThread2 extends Thread{
        Object o1;
        Object o2;
        public MyThread2(Object o1, Object o2){
            this.o1 = o1;
            this.o2 = o2;
        }
        public void run(){
            synchronized(o2){
                try{
                    Thread.sleep(1000);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
            synchronized(o1){}
        }
    }

      mt1线程和mt2线程共享o1和o2对象,都在等待对方先释放资源,产生死锁。

    3.  售票程序

       用4个线程实现卖票

      MyException.java
    public class MyException extends Exception{
        public MyException(){}
        public MyException(String message){
            super(message);
        }
    }

      Ticket.java

    public class Ticket{
        private String[] tickets;
        private int initialTicketNumber;
        private int sellOutTicket;
        
        public String[] getTickets(){
            return tickets;
        }
        public int getInitialTicketNumber(){
            return initialTicketNumber;
        }
        public Ticket(int initialTicketNumber){
            tickets = new String[initialTicketNumber];
            this.initialTicketNumber = initialTicketNumber;
        }
        public Ticket(){
            this(200);
        }
        public void initializeTicket(){
            for(int i=0; i < tickets.length; i++){
                tickets[i] = i + "号车票";
            }
        }
        public synchronized String sellTickets() throws MyException{
            if(initialTicketNumber - sellOutTicket > 0){
                String ticket = tickets[sellOutTicket];
                try{
                    Thread.sleep(100);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                sellOutTicket++;
                return ticket;
            }else{
                throw new MyException("票已卖完");
            }
        }
    }

      InitialTicketThread.java

    public class InitialTicketThread extends Thread{
        private Ticket tk;
        
        public InitialTicketThread(Ticket tk){
            this.tk = tk;
        }
        
        public void run(){
            tk.initializeTicket();
        }
    }

      SaleTicketThread.java

    public class SaleTicketThread extends Thread{
        private Ticket tk;
        boolean flag = true;
        
        public SaleTicketThread(Ticket tk){
            this.tk = tk;
        }
        
        public void run(){
            while(flag){
                try{
                    String ti = tk.sellTickets();
                    System.out.println(Thread.currentThread().getName() + "卖票成功:" + ti);
                }catch(MyException e){
                    e.printStackTrace();
                    flag = false;
                }
            }
        }
    }

      SaleTicketThreadTest

    public class SaleTicketThreadTest{
        public static void main(String[] args) throws InterruptedException{
            Ticket ticket = new Ticket(10);
            Thread t0 = new Thread(new InitialTicketThread(ticket));
            t0.setName("t0");
            t0.start();
            t0.join();
            
            Thread t1 = new Thread(new SaleTicketThread(ticket));
            t1.setName("t1");
            t1.start();
            
            Thread t2 = new Thread(new SaleTicketThread(ticket));
            t2.setName("t2");
            t2.start();
            
            Thread t3 = new Thread(new SaleTicketThread(ticket));
            t3.setName("t3");
            t3.start();
            
            Thread t4 = new Thread(new SaleTicketThread(ticket));
            t4.setName("t4");
            t4.start();
        }
    }

    4. 生产者消费者模式

       生产者给容器(list)中生产一个产品,消费者从容器中消费一个产品

    public class ThreadTest16{
        public static void main(String[] args){
            List list = new ArrayList();
            Thread t1 = new Thread(new Producer(list));
            Thread t2 = new Thread(new Consumer(list));
            t1.setName("生产者");
            t2.setName("消费者");
            t1.start();
            t2.start();
        }
    }
    
    class Producer implements Runnable{
        //生产者和消费者对同一个容器操作;
        private List list;
        public Producer(List list){this.list = list;}
        
        public void run(){
            while(true){
                synchronized(list){
                    if(list.size() > 0){    //每次容器中只有一个产品
                        try{
                            list.wait();    //生产者处于等待状态,wait()方法同时会释放当前对象的线程所占用的锁
                        }catch(InterruptedException e){
                            e.printStackTrace();
                        }
                    }
                    Object obj = new Object();  //容器中没有产品了,就需要生产了
                    list.add(obj);
                    System.out.println(Thread.currentThread().getName() + "--->" +obj);
                    list.notifyAll();    //唤醒消费者对象开始消费;此时可能会抢占资源,但是就算生产者抢到资源也会因为容器内有产品而处于等待状态
                }
            }
        }
    }
    
    class Consumer implements Runnable{
        private List list;
        public Consumer(List list){this.list = list;}
        
        public void run(){
            synchronized(list){
                while(true){
                    synchronized(list){
                        if(list.size() == 0){    //如果容器中没有产品就需要等待
                            try{
                                list.wait();
                        }catch(InterruptedException e){
                                e.printStackTrace();
                            }
                        }
                        Object obj = list.remove(0);
                        System.out.println(Thread.currentThread().getName() + "--->" +obj);
                        list.notifyAll();
                    }
                }
            }
        }
    }
  • 相关阅读:
    SharePoint 2013 商务智能报表发布
    sharepoint designer web 服务器似乎没有安装microsoft sharepoint foundation
    SharePoint 2013 Designer系列之数据视图
    SharePoint 2013 Designer系列之数据视图筛选
    SharePoint 2013 Designer系列之自定义列表表单
    SharePoint 2013 入门教程之创建及修改母版页
    SharePoint 2013 入门教程之创建页面布局及页面
    SharePoint 2010 级联下拉列表 (Cascading DropDownList)
    使用SharePoint Designer定制开发专家库系统实例!
    PL/SQL Developer 建立远程连接数据库的配置 和安装包+汉化包+注册机
  • 原文地址:https://www.cnblogs.com/homle/p/15257736.html
Copyright © 2011-2022 走看看