并发中锁的使用 和 常用并发类介绍

1.锁(synchronized)
当synchronized放在方法中时锁的是this对象,当不同对象时会出现问题(解决方法:可在方法名字加static关键字,这样锁的就是类了)

//锁的是对象,适用于多个线程使用同一个对象
    public synchronized void show(){
        
    }
//锁的是对象对应的类,适用于不同线程使用同一类的不同对象
    public static synchronized void show(){

    }

2.在并发编程中常用的并发包
(1)CopyOnWriteArrayList类,可以运用于多线程编程中,线程安全,使用例子如下:

public class Test3_1{
    public static void main(String[] args) throws InterruptedException {
        List<Integer> list = new CopyOnWriteArrayList<>();
        //定义线程t1,t2 并传入起始值 结束值 和所需的list对线
        Thread t1 = new MyThread(1,10000,list);
        Thread t2 = new MyThread(10001,20000,list);
        t1.start();
        t2.start();
        //等待线程t1结束
        t1.join();
        //等待线程t2结束
        t2.join();
        System.out.println(list);
        System.out.println(list.size());
    }
}

class MyThread extends Thread{
    int startNumber;
    int endNumber;
    List<Integer> list;

    public MyThread(int startNumber, int endNumber, List<Integer> list) {
        this.startNumber = startNumber;
        this.endNumber = endNumber;
        this.list = list;
    }

    @Override
    public void run() {
        for(int i=startNumber;i<=endNumber;i++){
            list.add(i);
        }
    }
}

打印结果为:20000
如果用普通的Arraylist可能会出现数组越界异常

(2)CopyOnWriteArraySet类,
参照上述List代码,此类线程安全,如果用普通的set类的话线程不安全,输出的值可能会小于20000;
(3)ConCurrentHasmMap类,
和上述CopyOnWriteArraySet类差不多。
(4)CountDownLatch类,可以控制线程执行的顺序,使一个线程等待其他线程各自执行完毕后再执行。
例子代码如下:


public class Test3_4 {
    public static void main(String[] args) {
        CountDownLatch start = new CountDownLatch(1);//在这传入的值1是一个计数器,当1变为0时,线程可继续执行,否则会一直阻塞在那里。
        CountDownLatch end = new CountDownLatch(1);
        new Thread(new A(start, end), "A").start();
        new Thread(new B(start, end), "B").start();
    }
}

class A implements Runnable {
    CountDownLatch start;
    CountDownLatch end;

    public A(CountDownLatch start, CountDownLatch end) {
        this.start = start;
        this.end = end;
    }

    @Override
    public void run() {
        System.out.println("开始计算");
        start.countDown();      //代表将start中的count-1,在本案例中 count的值是1,减去1后变为0,表示执行完此代码后被start阻塞的线程可继续执行。
        try {
            end.await();        //通过await方法将此处阻塞,只有当end中的count值为0时,该线程方可继续执行
            System.out.println("计算完毕");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class B implements Runnable {
    CountDownLatch start;
    CountDownLatch end;

    public B(CountDownLatch start, CountDownLatch end) {
        this.start = start;
        this.end = end;
    }

    @Override
    public void run() {
        try {
            start.await();//通过start的await方法将该线程在此处阻塞,只有当start中的count值为0时,线程方可继续往下执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            sum += i;
        }
        System.out.println("结果为:" + sum);
        end.countDown(); //将end中的count值-1
    }
}

打印:
开始计算
结果为:5050
计算完毕

(5)CyclicBarrier类
该类的构造方法为CyclicBarrier(int parties, Runnable barrierAction){}
其中parties是表示到达次屏障处的线程数量为这个值时,直线barrierAction线程。例子代码如下:

public class Test3_5 {
    public static void main(String[] args) {
        //定义CyclicBarrier类对象,使到达屏障线程数为3时,执行Four 线程
        CyclicBarrier cb = new CyclicBarrier(3, new Four());
        //创建计算所有数和的线程,并执行
        new Thread(new Compute(1, 10000, 0, cb), "计算所有数和").start();
        //创建计算偶数和的线程,并执行
        new Thread(new Compute(1, 10000, 1, cb), "计算偶数和").start();
        //创建计算奇数和的线程,并执行
        new Thread(new Compute(1, 10000, -1, cb), "计算奇数和").start();

    }
}

class Compute implements Runnable {
    int startNumber;
    int endNumber;
    //定义类型type,值为1代表计算偶数,-1 代表奇数,0代表所有数
    int type;
    CyclicBarrier cb;

    public Compute(int startNumber, int endNumber, int type, CyclicBarrier cb) {
        this.startNumber = startNumber;
        this.endNumber = endNumber;
        this.type = type;
        this.cb = cb;
    }

    @Override
    public void run() {
        int result;
        if (type == -1) {
            result = oddSum(startNumber, endNumber);
        } else if (type == 1) {
            result = evenSum(startNumber, endNumber);
        } else {
            result = allSum(startNumber, endNumber);
        }
        System.out.println(Thread.currentThread().getName() + "执行完毕,结果值为:" + result);
        try {
            //计算完毕,线程进入阻塞状态
            cb.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }

    public int oddSum(int startNumber, int endNumber) {
        int sum = 0;
        for (int i = startNumber; i <= endNumber; i++) {
            if (i % 2 == 1) {
                sum += i;
            }
        }
        return sum;
    }

    public int evenSum(int startNumber, int endNumber) {
        int sum = 0;
        for (int i = startNumber; i <= endNumber; i++) {
            if (i % 2 == 0) {
                sum += i;
            }
        }
        return sum;
    }

    public int allSum(int startNumber, int endNumber) {
        int sum = 0;
        for (int i = startNumber; i <= endNumber; i++) {
            sum += i;
        }
        return sum;
    }
}

class Four implements Runnable {

    @Override
    public void run() {
        System.out.println("线程计算完毕");
    }
}

打印:
计算偶数和执行完毕,结果值为:25005000
计算所有数和执行完毕,结果值为:50005000
计算奇数和执行完毕,结果值为:25000000
线程计算完毕

(6)Semaphore类,
改类的作用是可以设置最大线程执行数,例如一个饭馆中只有三张桌子,也就是最多三拨人可以同时吃饭,但是来这饭馆吃饭的假设有十波人,该线程的作用就是可以让先到的三波人先吃饭,剩下的七波人等待,当吃饭的三拨人中有一波吃好时,等待的七波人中方可有一波继续吃饭。
改线程的构造器方法有:
Semaphore(int permits) //permits指的是最大执行线程数
Semaphore(int permits, boolean fair) //permits指的是最大执行线程数,fair为true时,可以优先执行等待时间最长的线程。
方法:
acquire() 可以将线程放入队列中,如果当前执行线程数小于permits值,则线程可继续执行,当前执行线程数+1,否则该线程等待。
release() 一个线程执行完毕后释放,当前执行线程数-1,也就是说可以有新线程进入队列
代码如下:

public class Test3_6 {
    public static void main(String[] args) {
        Semaphore s = new Semaphore(3);
        for (int i = 1; i <= 10; i++) {
            new Thread(new Visitor(s),i+"号游客 ").start();
        }
    }
}
class Visitor implements Runnable{
    Semaphore s;

    public Visitor(Semaphore s) {
        this.s = s;
    }

    @Override
    public void run() {
        try {
            s.acquire();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Calendar calendar = Calendar.getInstance();
        int minute = calendar.get(Calendar.MINUTE);
        int second = calendar.get(Calendar.SECOND);
        System.out.println(Thread.currentThread().getName()+""+minute+"分"+second+"秒 进入参观");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        calendar = Calendar.getInstance();
        minute = calendar.get(Calendar.MINUTE);
        second = calendar.get(Calendar.SECOND);
        System.out.println(Thread.currentThread().getName()+""+minute+"分"+second+"秒 参观完毕");
        s.release();
    }
}

打印:
2号游客 50分18秒 进入参观
3号游客 50分18秒 进入参观
1号游客 50分18秒 进入参观
2号游客 50分20秒 参观完毕
4号游客 50分20秒 进入参观
1号游客 50分20秒 参观完毕
3号游客 50分20秒 参观完毕
5号游客 50分20秒 进入参观
6号游客 50分20秒 进入参观
4号游客 50分22秒 参观完毕
7号游客 50分22秒 进入参观
5号游客 50分22秒 参观完毕
8号游客 50分22秒 进入参观
6号游客 50分22秒 参观完毕
9号游客 50分22秒 进入参观
7号游客 50分24秒 参观完毕
10号游客 50分24秒 进入参观
8号游客 50分24秒 参观完毕
9号游客 50分24秒 参观完毕
10号游客 50分26秒 参观完毕

(7)Exchanger类
该类非常的简单,就是交换两个线程之间的值
代码如下:

public class Test3_7 {
    public static void main(String[] args) {
        Exchanger<String> e = new Exchanger<>();
        //定义A线程 ,将需要的信息输入,并执行
        new MThread(e, "一条娱乐新闻", "A").start();
        //定义B线程 ,将需要的信息输入,并执行
        new MThread(e, "一条体育新闻", "B").start();
    }
}

class MThread extends Thread {
    Exchanger<String> e;
    String msg;

    public MThread(Exchanger<String> e, String msg, String name) {
        super(name);
        this.e = e;
        this.msg = msg;
    }

    @Override
    public void run() {
        try {
            //打印当前线程名,将本线程中需要表达的信息传入e中,然后输入从另一个线程中获取到的内容。
            System.out.println(Thread.currentThread().getName() + e.exchange(msg));
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
    }
}

打印:
B一条娱乐新闻
A一条体育新闻

其中体育新闻是线程B中的值,娱乐新闻是A中的值,他们交换数据值了,所以打印结果如上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值