201521123011《Java程序设计》第11周学习总结

1. 本周学习总结

1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容。

2. 书面作业

本次PTA作业题集多线程
1.互斥访问与同步访问
完成题集4-4(互斥访问)与4-5(同步访问)
1.1 除了使用synchronized修饰方法实现互斥同步访问,还有什么办法实现互斥同步访问(请出现相关代码)?
1.2 同步代码块与同步方法有何区别?
1.3 实现互斥访问的原理是什么?请使用对象锁概念并结合相应的代码块进行说明。当程序执行synchronized同步代码块或者同步方法时,线程的状态是怎么变化的?
1.4 Java多线程中使用什么关键字实现线程之间的通信,进而实现线程的协同工作?为什么同步访问一般都要放到synchronized方法或者代码块中?

答:1.1
在Java中,提供了两种方式来实现同步互斥访问:synchronized和Lock

class Account{
    private int balance;
    private Lock poolLock = new ReentrantLock();
    private Condition condition = poolLock.newCondition();
    public Account(int balance) {
        super();
        this.balance = balance;
    }
    public  int getBalance() {
        return balance;
    }
    public  void deposit(int money){
                poolLock.lock();
        try{    
            this.balance=getBalance() + money;
            condition.signal();
        }
        finally
        {
            poolLock.unlock();
        }
    }
    public  void withdraw(int money){
                poolLock.lock();
        try{
            while (getBalance() <money) {
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            this.balance=getBalance() - money;
            condition.signal();
        }
        finally{
            poolLock.unlock();
        }
    }               
}

1.2
同步代码和同步方法是一样的,同步方法作用于整个方法,同步代码块作用于整个代码块而已。
1、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
2、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
3、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。

1.3
同一时刻,只能有一个线程获得某个资源的锁,访问该资源,其他线程无法访问它直至该资源被解锁。

class Counter {
	private static int id = 0;

	public static  void addId() {
		synchronized (Counter.class) {
			id++;
		}
		
	}

	public static synchronized void subtractId() {
		id--;
	}

	public static int getId() {
		return id;
	}
}

1.线程a获得id的锁,读取id的值id=0
2.线程a执行id++,结果id=1
3.线程a将结果存回id,id=1,id的锁被解锁资源释放
4.线程b获得id上的锁,读取id的值id=1
5.线程b执行id--即1-1=0,此时id=0
6.线程b将结果存回id,id现在为0,id的锁被解锁资源释放

1.4
用wait()、notify()来实现线程之间的协作,放到synchronized方法或者代码块是为了防止多个线程访问同一资源所引起的冲突。

2.交替执行
实验总结(不管有没有做出来)

答:
2.1
这题除了方法要用synchronized来修饰之外。还使用wait()和notify()来进行线程之间的协作。当任务1执行完后,改变flag为true,开始换任务2执行,任务2执行完后,改变flag为false,再换任务1执行,就这样实现交替执行。

public synchronized void run1(){
        while(getSize()>0){
            if(flag){
                try{
                    wait();
                }catch (InterruptedException e) {
                e.printStackTrace();
                    // TODO: handle exception
                }
            }
            System.out.println(Thread.currentThread().getName()+" finish "+str[i]);
                i++;
                flag=true;
                notify();
            
        }
      }
    public synchronized void run2(){
        while(getSize()>0){
            if(!flag){
                try{
                    wait();
                }catch (InterruptedException e) {
                e.printStackTrace();
                    // TODO: handle exception
                }
            }
                System.out.println(Thread.currentThread().getName()+" finish "+str[i]);
                i++;
                flag=flase;
                notify();
            
               }
            }

3.互斥访问
3.1 修改TestUnSynchronizedThread.java源代码使其可以同步访问。(关键代码截图,需出现学号)
3.2 进一步使用执行器改进相应代码(关键代码截图,需出现学号)
参考资料:Java多线程之Executor、ExecutorService、Executors、Callable、Future与FutureTask

答:
3.1

//201521123011
class Counter {
	private static int id = 0;

	public static synchronized void addId() {
		id++;
	}

	public static synchronized void subtractId() {
		id--;
	}

	public static int getId() {
		return id;
	}
}

3.2

//201521123011
int n=3;
List<Callable<Object>> listtask=new ArrayList<>();
ExecutorService exec=Executors.newCachedThreadPool();
for (int i = 0; i < n; i++) {
       task.add(Executors.callable(new Adder())); 
        }
for (int i = 0; i < n; i++) {
       task.add(Executors.callable(new Subtracter()));
        }
exec.invokeAll(task); 
System.out.println(Counter.getId());
System.out.println("main end");

4.线程间的合作:生产者消费者问题
4.1 运行MyProducerConsumerTest.java。正常运行结果应该是仓库还剩0个货物。多运行几次,观察结果,并回答:结果正常吗?哪里不正常?为什么?
4.2 使用synchronized, wait, notify解决该问题(关键代码截图,需出现学号)
4.3 选做:使用Lock与Condition对象解决该问题。

答:
4.1
不正常,会出现10个货物的情况

问题主要出在public synchronized void add(String t)public synchronized void remove()的方法上,需要加上wait()和notify()函数来实现线程之间的合作
4.2

//201521123011
public synchronized void add(String t) {
        while(repo.size()>=capacity){
            try{
                wait();
                System.out.println("仓库已满!无法添加货物。");
            }catch (InterruptedException e) {
                    e.printStackTrace();
                // TODO: handle exception
            }
        }
        repo.add(t);
        notify();
    }
    public synchronized void remove() {
         while(repo.size()<=0){
             try{
                 wait();
                 System.out.println("仓库无货!无法从仓库取货");
             }catch (InterruptedException e) {
                    e.printStackTrace();
                // TODO: handle exception
            }
         }
         repo.remove(0);    
        notify();
    }

4.3

private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();
    public synchronized void add(String t) {
        lock.lock();
        try{
            while(repo.size()>=capacity){
                try{
                    condition.await();
                    System.out.println("仓库已满!无法添加货物。");
                }catch (InterruptedException e) {
                       e.printStackTrace();
                    // TODO: handle exception
                }
            }
            repo.add(t);
            condition.signal();
        }finally {
            lock.unlock();
        }   
    }
    public synchronized void remove() {
        try{
             while(repo.size()<=0){
                 try{
                     condition.await();
                     System.out.println("仓库无货!无法从仓库取货");
                 }catch (InterruptedException e) {
                       e.printStackTrace();                    
                    // TODO: handle exception
                }
             }
             repo.remove(0);    
            condition.signal();
        }finally {
            lock.unlock();
        }   
    }

5.查询资料回答:什么是线程安全?(用自己的话与代码总结,写自己看的懂的作业)
答:
线程安全就是说多线程访问同一代码,不会产生不确定的结果。
线程安全一般都涉及到synchronized 就是一段代码同时只能有一个线程来操作。
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

3. 码云上代码提交记录

题目集:多线程(4-4到4-10)
3.1. 码云代码提交记录

3.2 截图多线程PTA提交列表

posted @ 2017-05-06 17:04  叫我小天才  阅读(196)  评论(0编辑  收藏  举报