JUC=java.util.concurrent
线程基础知识复习
进程:运行着的程序。资源分配的基本单位
线程:独立运行的基本单位
waitting不见不散
terminated过期不候
并发:高并发。抢票、秒杀…多个线程同时抢同一个资源
并行:多个事情可以同时做,一个人同时处理多件事。
SaleTicket-Lock
package com.atguigu.thread1018;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Ticket//资源类 类 = 实例变量+实例方法
{
private int number = 30;
//Lock implementations provide more extensive locking operations
//than can be obtained using synchronized methods and statements.
private Lock lock = new ReentrantLock(); //List list = new ArrayList();父类引用指向子类对象实现多态
public void sale()//对资源类的操作。加synchronized
{
lock.lock();
try
{
if(number > 0)
{
System.out.println(Thread.currentThread().getName()+"\t 卖出第:"+(number--)+"\t 还剩下: "+number);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
/**
* 第一个case
* @Description: 卖票程序复习线程知识 ,三个售票员 卖出 30张票
* @author zzyy
* @date 2018年3月15日
* 1 多线程编写套路------上
* 1.1 线程 操作(实例方法) 资源类
* 1.2 高内聚 低耦合(把对资源的操作通通放在自己身上,)
*/
public class SaleTicket
{
public static void main(String[] args)
{
Ticket ticket = new Ticket();
//Thread(Runnable target, String name) Allocates a new Thread object.
new Thread(() -> { for (int i = 1; i <=40; i++) ticket.sale(); }, "AA").start();
new Thread(() -> { for (int i = 1; i <=40; i++) ticket.sale(); }, "BB").start();
new Thread(() -> { for (int i = 1; i <=40; i++) ticket.sale(); }, "CC").start();
new Thread(() -> { for (int i = 1; i <=40; i++) ticket.sale();} , "your thread name") .start();
/*new Thread(new Runnable() {
@Override
public void run()
{
for (int i = 1; i <=40; i++)
{
ticket.sale();
}
}
}, "AA").start();
new Thread(new Runnable() {
@Override
public void run()
{
for (int i = 1; i <=40; i++)
{
ticket.sale();
}
}
}, "BB").start();
new Thread(new Runnable() {
@Override
public void run()
{
for (int i = 1; i <=40; i++)
{
ticket.sale();
}
}
}, "CC").start(); */
}
}
//多线程操作资源的三种方式
//1 class MyThread implements Runnable
//2 匿名内部类
//3 lambda Express
LambdaExpression
package com.atguigu.thread1018;
@FunctionalInterface
interface Foo
{
//public void sayHello();
//public void sayH886();
public int add(int x,int y);//有返回值和参数的时候
default int div(int x,int y)//默认方法实现
{
return x/y;
}
public static int sub(int x, int y)//静态方法实现
{
return x - y;
}
}
/**
*
* @Description: Lambda Express
* @author zzyy
* @date 2018年3月15日
* 1 拷贝中括号+写死右箭头+落地大括号
* 2 一个接口里面有且仅有一个方法的接口(才是函数式接口),才可以使用Lambda Express
* 3 @FunctionalInterface
* 4 default默认方法实现
* 5 静态方法实现
*/
public class LambdaDemo
{
public static void main(String[] args)
{
// Foo foo = new Foo() {
// @Override
// public void sayHello()
// {
// System.out.println("******hello 1018");
// }
//
// @Override
// public void sayH886()
// {
//
// }
// };
// foo.sayHello();
// Foo foo = () -> { System.out.println("******hello 1018 lambda"); };//假设接口Foo里面有且只有一个方法sayHello()
// foo.sayHello();
Foo foo = (x,y) -> { return x + y; };//
int result = foo.add(3, 15);
System.out.println("*****result: "+result);
result = foo.div(10, 2);
System.out.println("*****result: "+result);
Foo.sub(10, 3);
}
}
NotifyWaitDemo-Condition
package com.atguigu.thread1018;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class ShareData
{
private int number = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void increment() throws InterruptedException
{
lock.lock();
try
{
while(number != 0)
{
condition.await();//this.wait();
}
//2 干活
++number;
System.out.println(Thread.currentThread().getName()+"\t"+number);
//3 通知
condition.signalAll();//this.notifyAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException
{
lock.lock();
try
{
while(number == 0)
{
condition.await();//this.wait();
}
//2 干活
--number;
System.out.println(Thread.currentThread().getName()+"\t"+number);
//3 通知
condition.signalAll();//this.notifyAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
/*public synchronized void increment() throws InterruptedException
{
//1 判断
//if(number != 0)
while(number != 0)
{
this.wait();// A......C......
}
//2 干活
++number;
System.out.println(Thread.currentThread().getName()+"\t"+number);
//3 通知
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException
{
//1 判断
//if(number == 0)
while(number == 0)
{
this.wait();
}
//2 干活
--number;
System.out.println(Thread.currentThread().getName()+"\t"+number);
//3 通知
this.notifyAll();
} */
}
/**
*
* @Description:
* 现在两个线程,
* 可以操作初始值为零的一个变量,
* 实现一个线程对该变量加1,一个线程对该变量减1,
* 交替,来10轮,变量初始值为零。
* 生产者消费者-pingpong-母题
* @author zzyy
* @date 2018年3月15日
* 1 多线程编写套路------上
* 1.1 线程 操作(实例方法) 资源类
* 1.2 高内聚 低耦合
*
* 2 多线程编写套路------下
* 2.1 判断
* 2.2 干活
* 2.3 通知
*/
public class NotifyWaitDemo
{
public static void main(String[] args)
{
ShareData sd = new ShareData();
new Thread(() -> {
for (int i = 1; i <=10; i++)
{
try
{
Thread.sleep(200);
sd.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "AA").start();
new Thread(() -> {
for (int i = 1; i <=10; i++)
{
try
{
Thread.sleep(300);
sd.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "BB").start();
new Thread(() -> {
for (int i = 1; i <=10; i++)
{
try
{
Thread.sleep(400);
sd.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "CC").start();
new Thread(() -> {
for (int i = 1; i <=10; i++)
{
try
{
Thread.sleep(500);
sd.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "DD").start();
}
}
加上另外的两个线程
加上休眠后
它不具备重新来回来再判断一次的能力,把if改为while
说出object类中常用的五个方法hascode() getClass() toString() wait() notify()
问题得到解决
Lock_8
package com.atguigu.thread1018;
import java.util.concurrent.TimeUnit;
class Phone// Phone.class
{
public static synchronized void sendSMS() throws Exception
{
TimeUnit.SECONDS.sleep(4);
System.out.println("--------------sendSMS");
}
public synchronized void sendEmail() throws Exception
{
System.out.println("--------------sendEmail");
}
public void openPC()
{
System.out.println("--------------openPC");
}
}
/**
*
* @Description: 8锁
* @author zzyy
* @date 2018年3月15日
* 笔记见后
* 1 标准访问,请问先打印短信还是email?(两个线程同时访问同一个资源,谁先谁后是不一定的;在两个线程之间加上Thread.sleep(100);就能确定先后了)
* 也就是说如果一个实例对象的**非静态同步方法**获取锁后,该实例对象的**其他非静态同步方法**必须等待获取锁的方法释放锁后才能获取锁,
* 锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
* 所有的 **非静态同步方法**用的都是同一把锁——实例对象本身,
* 2 sendSMS()睡觉4秒钟,请问先打印短信还是email?(刚开始等了4秒,然后先短信后email)(同一个同学同一时刻只能使用手机的一个功能!)
* 也就是说如果一个实例对象的**非静态同步方法**获取锁后,该实例对象的**其他非静态同步方法**必须等待获取锁的方法释放锁后才能获取锁,
* 锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
* 所有的 **非静态同步方法**用的都是同一把锁——实例对象本身,
* 3 新增普通方法openPC,请问先打印短信还是openPhone?(先开机【因为这个没有同步方法啊】,等了4秒,然后打印短信)
* 加个普通方法后发现和同步锁无关
*
* 4 有两部手机,请问先打印短信还是email?(先打印邮件,等了4秒,然后打印短信)
* 可是**别的实例对象的非静态同步方法**因为跟该实例对象的非静态同步方法用的是不同的锁,所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
* 换成两个对象后,不是同一把锁了,情况立刻变化。
所有的 **非静态同步方法**用的都是同一把锁——实例对象本身,
*
* 5 两个静态同步方法,同一部手机,请问先打印短信还是email?(等了4秒,先打印短信,然后打印邮件)
* 6 两个静态同步方法,2部手机,请问先打印短信还是email?(等了4秒,先打印短信,然后打印邮件)
* 但是一旦一个**静态同步方法**获取锁后,**其他的静态同步方法**都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!
* 所有的**静态同步方法**用的也是同一把锁——类对象本身,
*
*
* 7 1个静态同步方法,1个普通同步方法,同一部手机,请问先打印短信还是email?(先打印邮件,等了4秒,打印短信)
* 8 1个静态同步方法,1个普通同步方法,2部手机,请问先打印短信还是email?(先打印邮件,等了4秒,打印短信)
* 这两把锁是两个不同的对象,所以**静态同步方法**与**非静态同步方法**之间是不会有竞态条件的。
*
* ---------------------------------------------------
* * 笔记
1 一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,
其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法
2锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
*
* 3加个普通方法后发现和同步锁无关
*
4换成两个对象后,不是同一把锁了,情况立刻变化。
锁定的是当前对象this,类锁,锁定的是整个class,是两个不同的对象
5都换成静态同步方法后,情况又变化!因为静态的话锁定的是Class,而不是具体的某个对象,所以说这里无论是一部手机还是两部手机都一样!
所有的非静态同步方法用的都是同一把锁——实例对象本身,
也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,
可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,
所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
所有的静态同步方法用的也是同一把锁——类对象本身,
这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。
但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,
*/
public class Lock_8
{
public static void main(String[] args) throws Exception
{
Phone phone = new Phone();
Phone phone2 = new Phone();
new Thread(() -> {
try
{
phone.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
}, "A").start();
Thread.sleep(100);
new Thread(() -> {
try
{
//phone.sendEmail();
//phone.openPC();
phone2.sendEmail();
} catch (Exception e) {
e.printStackTrace();
}
}, "B").start();
}
}
/**
* 笔记
一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,
其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法
锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
加个普通方法后发现和同步锁无关
换成两个对象后,不是同一把锁了,情况立刻变化。
都换成静态同步方法后,情况又变化
所有的非静态同步方法用的都是同一把锁——实例对象本身,
也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,
可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,
所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
所有的静态同步方法用的也是同一把锁——类对象本身,
这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。
但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,
还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!
*/
一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一一个线程去访问这些synchronized方法
锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
加个普通方法后发现和同步锁无关
换成两个对象后,不是同一把锁了,情况立刻变化。
都换成静态同步方法后,情况又变化
所有的 非静态同步方法用的都是同一把锁——实例对象本身,
也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,
可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
所有的静态同步方法用的也是同一把锁——类对象本身,
这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。
但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!
seqLoad
类的加载机制:从父到子,模板先有,后有实例。静态先行,加载,只有一次。
3父静态
6子静态
2父代码块
1父构造方法
5子代码块
4子构造方法
2父代码块
1父构造方法
5子代码块
4子构造方法
21
static
静态 属于全部类的 类模板 每一个对象都一样的那个部分
可以用在这些上面:
变量
方法
类
ThreadAccess-Condition顺序调度
package com.atguigu.thread1018;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class ShareResource
{
private int number = 1;//1 - A 2 - B 3 - C.....
private Lock lock = new ReentrantLock();
private Condition c1 = lock.newCondition();
private Condition c2 = lock.newCondition();
private Condition c3 = lock.newCondition();
public void print5(int totalLoop)
{
lock.lock();
try
{
//1 判断
while(number != 1)
{
c1.await();
}
//2 干活
for (int i = 1; i <=5; i++)
{
System.out.println(Thread.currentThread().getName()+"\t"+i+"\t totalLoop:"+totalLoop);
}
//3 通知
number = 2;
c2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print10(int totalLoop)
{
lock.lock();
try
{
//1 判断
while(number != 2)
{
c2.await();
}
//2 干活
for (int i = 1; i <=10; i++)
{
System.out.println(Thread.currentThread().getName()+"\t"+i+"\t totalLoop:"+totalLoop);
}
//3 通知
number = 3;
c3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void print15(int totalLoop)
{
lock.lock();
try
{
//1 判断
while(number != 3)
{
c3.await();
}
//2 干活
for (int i = 1; i <=15; i++)
{
System.out.println(Thread.currentThread().getName()+"\t"+i+"\t totalLoop:"+totalLoop);
}
//3 通知
number = 1;
c1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
/**
*
* @Description:
* 多线程之间按顺序调用,实现A->B->C。线程之间的通信机制!
* 三个线程启动,要求如下:
*
* AA打印5次,BB打印10次,CC打印15次
* 接着
* AA打印5次,BB打印10次,CC打印15次
* ......来10轮
* @author zzyy
* @date 2018年3月17日
*/
public class ThreadOrderAccess
{
public static void main(String[] args)
{
ShareResource sr = new ShareResource();
new Thread(() -> {
for (int i = 1; i <=10; i++)
{
sr.print5(i);
}
}, "AA").start();
new Thread(() -> {
for (int i = 1; i <=10; i++)
{
sr.print10(i);
}
}, "BB").start();
new Thread(() -> {
for (int i = 1; i <=10; i++)
{
sr.print15(i);
}
}, "CC").start();
}
}
synchronized做不到像这样的顺序调度
A5次,B10次,C15次 各10轮
ReadWriteLockDemo
package com.atguigu.thread1018;
import java.util.concurrent.locks.ReentrantReadWriteLock;
class MyQueue
{
private Object obj;
private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
public void writeObj(Object obj)
{
rwLock.writeLock().lock();
try
{
this.obj = obj;
System.out.println(Thread.currentThread().getName()+"\t"+obj);
} catch (Exception e) {
e.printStackTrace();
} finally {
rwLock.writeLock().unlock();
}
}
public void readObj()
{
rwLock.readLock().lock();
try
{
System.out.println(Thread.currentThread().getName()+"\t"+obj);
} catch (Exception e) {
e.printStackTrace();
} finally {
rwLock.readLock().unlock();
}
}
}
/**
*
* @Description: 一个线程写入,100个线程读取
* @author zzyy
* @date 2018年3月17日
*/
public class ReadWriteLockDemo
{
public static void main(String[] args) throws InterruptedException
{
MyQueue q = new MyQueue();
new Thread(() -> {
q.writeObj("ClassName1018");
}, "writeThread").start();
Thread.sleep(100);
for (int i = 1; i <=100; i++)
{
new Thread(() -> {
q.readObj();
},String.valueOf(i)).start();
}
}
}
进一步加深锁的细粒度的控制
读锁可共享,写锁是排他的。
实现读写分离
CallableDemo-Callable\FutureTask\get
package com.atguigu.thread1018;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class MyThread implements Callable<Integer>
{
@Override
public Integer call() throws Exception
{
System.out.println("**********call() ****");
//Thread.sleep(4000);
return 1018;
}
}
/**
*
* @Description: Callable接口获得多线程
* @author zzyy
* @date 2018年3月17日
* 笔记结论见最后
*
*
*/
public class CallableDemo
{
public static void main(String[] args) throws InterruptedException, ExecutionException
{
FutureTask<Integer> ft = new FutureTask<Integer>(new MyThread());//代理的桥梁结构
new Thread(ft, "AA").start();
new Thread(ft, "BB").start();
System.out.println(Thread.currentThread().getName()+"***********我是上课主线程");
Integer result01 = ft.get();
System.out.println("******result01: "+result01);
}
}
/**
*
*
在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给Future对象在后台完成,
当主线程将来需要时,就可以通过Future对象获得后台作业的计算结果或者执行状态。
一般FutureTask多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。
仅在计算完成时才能检索结果;如果计算尚未完成,则阻塞 get 方法。一旦计算完成,
就不能再重新开始或取消计算。get方法而获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态,
然后会返回结果或者抛出异常。
只计算一次
get方法放到最后
*/
第三种获得线程的方法
面向接口编程
找一个类同时实现了这两个接口------多态
即实现了Runnable接口,又关联了Callable接口
FutureTask能够成为一个适配器的功能
Callable这个线程的功能:
把难题空着,先把会的题做了,然后最后返回去把难题做了,加起来就是最终的结果
为什么两个线程却只打印一次?
用这个线程一次,可以做很多事。除非你再new一个futureTask
如果get放前面,就会堵塞,所以尽量把get放到最后面
可以使用lamda表达式
CountDownLatchDemo
package com.atguigu.thread1018;
import java.util.concurrent.CountDownLatch;
import com.atguigu.enums.CountryEnums;
/**
* Talk is cheap,show me your code
*
* @Description: CountDownLatch
* @author zzyy
* @date 2018年3月17日
*让一些线程阻塞直到另一些线程完成一系列操作后才被唤醒。
*
* CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,这些线程会阻塞。
* 其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞),
* 当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行。
*
* 解释:5个同学陆续离开教室后值班同学才可以关门。
* 也即 秦灭6国,一统华夏
* main主线程必须要等前面5个线程完成全部工作后,自己才能开干
*/
public class CountDownLatchDemo
{
public static void main(String[] args) throws InterruptedException
{
//秦灭6国,一统华夏
CountDownLatch cdl = new CountDownLatch(6);
for (int i = 1; i <=6; i++)
{
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"\t 国被灭");
cdl.countDown();
},CountryEnums.forEachCountryEnums(i).getRetMsg()).start();
}
cdl.await();
System.out.println(Thread.currentThread().getName()+"*********秦灭6国,一统华夏");
System.out.println();
System.out.println();
System.out.println(CountryEnums.ONE);
System.out.println(CountryEnums.ONE.getRetCode());
System.out.println(CountryEnums.ONE.getRetMsg());
}
public static void testCloseDoor() throws InterruptedException
{
CountDownLatch cdl = new CountDownLatch(6);
for (int i = 1; i <=6; i++)
{
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"\t 下自习离开教室");
cdl.countDown();
}, String.valueOf(i)).start();
}
cdl.await();
System.out.println(Thread.currentThread().getName()+"*********班长走人");
}
}
不加countdownlatch
枚举的作用
CyclicBarrierDemo
package com.atguigu.thread1018;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
*
* @Description: TODO(这里用一句话描述这个类的作用)
* @author zzyy
* @date 2018年3月3日
* CyclicBarrier
* 的字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是,
* 让一组线程到达一个屏障(也可以叫同步点)时被阻塞,
* 直到最后一个线程到达屏障时,屏障才会开门,所有
* 被屏障拦截的线程才会继续干活。
* 线程进入屏障通过CyclicBarrier的await()方法。
*
* 集齐7颗龙珠就可以召唤神龙
*/
public class CyclicBarrierDemo
{
private static final int NUMBER = 7;
public static void main(String[] args)
{
//CyclicBarrier(int parties, Runnable barrierAction)
CyclicBarrier cb = new CyclicBarrier(NUMBER, () -> { System.out.println("******召唤神龙");} ) ;
for (int i = 1; i <=NUMBER; i++)
{
int tempInt = i;
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName()+"\t 收集到第:"+tempInt+"\t 龙珠");
cb.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}, String.valueOf(i)).start();
}
}
}
人都走了才关灯,人到齐了才开会
一个是做减法、一个是做加法
SemaphoreDemo
争车位
package com.atguigu.thread1018;
import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
*
* @Description: TODO(这里用一句话描述这个类的作用)
* @author zzyy
* @date 2018年3月17日
*
* 在信号量上我们定义两种操作:
* acquire(获取) 当一个线程调用acquire操作时,它要么通过成功获取信号量(信号量减1),
* 要么一直等下去,直到有线程释放信号量,或超时。
* release(释放)实际上会将信号量的值加1,然后唤醒等待的线程。
*
* 信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。
*/
public class SemaphoreDemo
{
public static void main(String[] args)
{
Semaphore semaphore = new Semaphore(3);//模拟3个停车位
for (int i = 1; i <=6; i++) //模拟6部汽车
{
new Thread(() -> {
try
{
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"\t ***抢占了车位");
TimeUnit.SECONDS.sleep(new Random().nextInt(8));
System.out.println(Thread.currentThread().getName()+"\t 离开了车位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
6部汽车抢3个停车位
停5秒钟
现在停随机时间后
ExecutorsDemo-线程池
package com.atguigu.thread1018;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
*
* @Description: 第4种获得多线程的方式,线程池
* @author zzyy
* @date 2018年3月3日
*
* SSS
* Collections Arrays Executors
*/
public class ExecutorsDemo
{
public static void main(String[] args)
{
ScheduledExecutorService service = Executors.newScheduledThreadPool(5);
ScheduledFuture<Integer> result = null;
try
{
for (int i = 1; i <=15; i++)
{
result = service.schedule(() -> {
System.out.print(Thread.currentThread().getName());
return new Random().nextInt(10);
}, 2, TimeUnit.SECONDS);
System.out.println(" ********result: "+result.get());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
service.shutdown();
}
}
public static void testThreadPool()
{
//ExecutorService service = Executors.newFixedThreadPool(5);//一池5线程
//ExecutorService service = Executors.newSingleThreadExecutor();//一池1线程
ExecutorService service = Executors.newCachedThreadPool();//一池N线程
Future<Integer> result = null;
try
{
for (int i = 1; i <=15; i++)
{
result = service.submit( () -> {//callable接口可以使用lamda表达式
Thread.sleep(400);
System.out.print(Thread.currentThread().getName());
return new Random().nextInt(10);
} );
System.out.println(" ********result: "+result.get());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
service.shutdown();
}
}
}
所以直接可以用lamda表达式
一池五线程
一池一线程
一池多线程
带调度片的线程
每两秒钟提交一次!
code面试题
NotSafe
package com.atguigu.thread1018;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
/**
*
* @Description: 集合类不安全
* @author zzyy
* @date 2018年3月17日
*/
public class NotSafeDemo
{
public static void main(String[] args)
{
Map<String,String> map = new ConcurrentHashMap<String,String>();
for (int i = 1; i <=30; i++)
{
new Thread(() -> {
map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,4));
System.out.println(map);
},String.valueOf(i)).start();
}
}
public static void SetNotSafe()
{
Set<String> set = new CopyOnWriteArraySet<String>();
for (int i = 1; i <=30; i++)
{
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0,4));
System.out.println(set);
},String.valueOf(i)).start();
}
}
/**
CopyOnWrite容器即写时复制的容器。往一个容器添加元素的时候,不直接往当前容器Object[]添加,而是先将当前容器Object[]进行Copy,
复制出一个新的容器Object[] newElements,然后新的容器Object[] newElements里添加元素,添加完元素之后,
再将原容器的引用指向新的容器 setArray(newElements);。这样做的好处是可以对CopyOnWrite容器进行并发的读,
而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器
*/
public static void ListNotSafe()
{
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();//new ArrayList<String>();
// list = Arrays.asList("a","b","c");
// list.forEach(System.out::println);
for (int i = 1; i <=30; i++)
{
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0,4));
System.out.println(list);//[21as,23ad,lojf]
},String.valueOf(i)).start();
}
}
}
List线程不安全
ConcurrentModificationException
并发修改异常