Java多线程

线程同步

假设创建并启动100个线程,每个线程都往同一个账户中添加一个便士。非同步状态下得到的结果是2。

package com.halin.demo;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class AccountWithoutSync {

    private static Account account = new Account();
    
    public static void main(String[] args) {
        
        ExecutorService executor = Executors.newCachedThreadPool();
        
        for (int i = 0; i < 100; i++) {
            executor.execute(new AddAPennyTask());
        }
        
        executor.shutdown();
        
        while (!executor.isTerminated()) {
            
        }
        
        System.out.println("What is balance ? " + account.getBalance());
        
    }
    
    private static class AddAPennyTask implements Runnable {
        public void run() {
            account.deposit(1);
        }
    }
    
    private static class Account {
        
        private static Lock lock = new ReentrantLock();
        private int balance = 0;
        
        public int getBalance() {
            return balance;
        }
        
        public void deposit(int amount) {
            
//            lock.lock();
            
            int newBalance = balance + amount;
            
            try {
                Thread.sleep(5);
            }
            catch (InterruptedException ex) {
                
            }
            
            balance = newBalance;
            
//            lock.unlock();
            
        }
        
    }
    
}
View Code

要实现同步,可在deposit方法前添加关键字synchronized来实现一个隐式锁,也可以直接添加显式锁来lock()和unlock()。显式锁对同步具有状态的线程更加直观和灵活。只有一个许可的信号量也可以用来模拟一个相互排斥的锁。

private static class Account {
    
    private static Semaphore semaphore = new Semaphore(1);
    private int balance = 0;
    
    public int getBalance() {
        return balance;
    }
    
    public void deposit(int amount) {
        
        try {
            semaphore.acquire();
            int newBalance = balance + amount;
            Thread.sleep(5);
            balance = newBalance;
        }
        catch (InterruptedException ex) {
            
        }
        finally {
            semaphore.release();
        }
        
    }
    
}
View Code

 

线程间协作

假设创建并启动两个任务,一个用来向账户中存款,另一个从同一个账户提款。当提款的数额大于账户的当前余额时,提款线程必须等待。不管什么时候,只要向账户心存入一笔资金,存储线程必须通知提取线程重新尝试。如果余额仍未达到提款的数额,提取线程必须继续等待存入更多的资金。

package com.halin.demo;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadCooperation {

    private static Account account = new Account();
    
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        executor.execute(new DepositTask());
        executor.execute(new WithdrawTask());
        executor.shutdown();
        System.out.println("Thread 1\t\tThread 2\t\tBalance");
    }
    
    public static class DepositTask implements Runnable {
        public void run() {
            try {
                while (true) {
                    account.deposit((int) (Math.random() * 10) + 1);
                    Thread.sleep(1000);
                }
            }
            catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }
    
    public static class WithdrawTask implements Runnable {
        public void run() {
            while (true) {
                account.withDraw((int) (Math.random() * 10) + 1);
            }
        }
    }
    
    public static class Account {
        
        private static Lock lock = new ReentrantLock();
        
        private static Condition newDeposit = lock.newCondition();
        
        private int balance = 0;
        
        public int getBalance() {
            return balance;
        }
        
        public void withDraw(int amount) {
            lock.lock();
            try {
                while (balance < amount) {
                    newDeposit.await();
                }
                balance -= amount;
                System.out.println("\t\t\tWithdraw " + amount + "\t\t" + getBalance());
            }
            catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            finally {
                lock.unlock();
            }
        }
        
        public void deposit(int amount) {
            lock.lock();
            try {
                balance += amount;
                System.out.println("Deposit " + amount + "\t\t\t\t\t" + getBalance());
                newDeposit.signalAll();
            }
            finally {
                lock.unlock();
            }
        }
        
    }
    
}
View Code

 

生产者消费者模型

假设使用缓冲区存储证书。缓冲区大小受限。缓冲区提供write(int)方法将一个int值添加到缓冲区,还提供方法read()从缓冲区中读取和删除一个int值。为了同步这个操作,使用具有两个条件的锁:notEmpty(即缓冲区非空)和notFull(即缓冲区未满)。当一个任务向缓冲区添加一个int时,如果缓冲区是满的,那么任务将会等待notFull状态。当任务从缓冲区中删除一个int时,如果缓冲区是空的,那么任务将等待notEmpty状态。

package com.halin.demo;

import java.util.LinkedList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConsumerProducer {

    private static Buffer buffer = new Buffer();
    
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        executor.execute(new ProducerTask());
        executor.execute(new ConsumerTask());
        executor.shutdown();
    }
    
    private static class ProducerTask implements Runnable {
        public void run() {
            try {
                int i = 1;
                while (true) {
                    System.out.println("Producer writes " + i);
                    buffer.write(i++);
                    Thread.sleep((int)(Math.random() * 10000));
                }
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }
    
    private static class ConsumerTask implements Runnable {
        public void run() {
            try {
                int i = 1;
                while (true) {
                    System.out.println("\t\t\tConsumer reads " + buffer.read());
                    Thread.sleep((int)(Math.random() * 10000));
                }
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }
    
    private static class Buffer {
        
        private static final int CAPACITY = 1;
        private LinkedList<Integer> queue = new LinkedList<Integer>();
        private static Lock lock = new ReentrantLock();
        private static Condition notEmpty = lock.newCondition();
        private static Condition notFull = lock.newCondition();
        
        public void write(int value) {
            lock.lock();
            try {
                while (queue.size() == CAPACITY) {
                    System.out.println("Wait for notFull condition");
                    notFull.await();
                }
                queue.offer(value);
                notEmpty.signal();
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
        
        public int read() {
            int value = 0;
            lock.lock();
            try {
                while (queue.isEmpty()) {
                    System.out.println("\t\tWait for notEmpty condition");
                    notEmpty.await();
                }
                value = queue.remove();
                notFull.signal();
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            } finally {
                lock.unlock();
            }
            return value;
        }
        
    }
    
}
View Code

ArrayBlockingQueue已现实同步,可代替Buffer,减少人工编码。

 

避免死锁

有时两个或多个线程需要锁定几个共享对象,这时可能引起死锁。也就是说,每个线程已经锁定一个对象,正在等待锁定另一个对象。考虑有两个线程和两个锁的情形。线程1锁定了locker1,等待locker2。线程2锁定了locker2,等待locker1。每个线程都等待另一个线程释放自己需要的锁,结果导致两个线程都无法继续运行。

package com.halin.demo;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Deadlock {

    public static byte[] locker1 = new byte[0];
    public static byte[] locker2 = new byte[0];
    public static void main(String[] args) 
    {
        ExecutorService executor = Executors.newFixedThreadPool(100);
        for(int i = 0; i < 50; i++){
            executor.execute(new DeadlockTask1());
            executor.execute(new DeadlockTask2());
        }
        executor.shutdown();
        while(!executor.isTerminated()){
        
        }
        System.exit(0);
    }

    private static class DeadlockTask1 implements Runnable
    {
        public void run()
        {
            synchronized(locker1){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println("DeadlockTask1 获得锁 locker1");
                synchronized(locker2){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.out.println("DeadlockTask1 获得锁 locker2 没有死锁!");
                }
            }
        }
    }

    private static class DeadlockTask2 implements Runnable
    {
        public void run()
        {
            synchronized(locker2){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println("DeadlockTask2 获得锁 locker2");
                synchronized(locker1){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.out.println("DeadlockTask2 获得锁 locker1 没有死锁!");
                }
            }
        }
    }
    
}
View Code

输出为:

DeadlockTask1 获得锁 locker1
DeadlockTask2 获得锁 locker2

如果Thread.sleep(1000)的话,可能就不会死锁,因为处理太快了。

解决方法很简单,为共享资源的锁排序,按序加锁。先锁定locker1,再锁定locker2。

package com.halin.demo;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class LockInOrder {

    public static byte[] locker1 = new byte[0];
    public static byte[] locker2 = new byte[0];
    public static void main(String[] args) 
    {
        ExecutorService executor = Executors.newFixedThreadPool(100);
        for(int i = 0; i < 50; i++){
            executor.execute(new DeadlockTask1());
            executor.execute(new DeadlockTask2());
        }
        executor.shutdown();
        while(!executor.isTerminated()){
        
        }
        System.exit(0);
    }

    private static class DeadlockTask1 implements Runnable
    {
        public void run()
        {
            synchronized(locker1){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println("DeadlockTask1 获得锁 locker1");
                synchronized(locker2){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.out.println("DeadlockTask1 获得锁 locker2 没有死锁!");
                }
            }
        }
    }

    private static class DeadlockTask2 implements Runnable
    {
        public void run()
        {
            synchronized(locker1){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println("DeadlockTask2 获得锁 locker2");
                synchronized(locker2){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.out.println("DeadlockTask2 获得锁 locker1 没有死锁!");
                }
            }
        }
    }
    
}
View Code

 

转载于:https://www.cnblogs.com/RussellWestbrook/p/4256127.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值