实现Runnable接口创建多线程,synchronized保证数据同步:
@Test
public void runnableSale() {
int threadNum = 4;
//@Test不同于main方法,需要下面的同步工具等待子线程执行完成
//启用同步工具类对子线程计数,为了让主线程启动子线程后,等待子线程完成对应的工作
CountDownLatch countDownLatch = new CountDownLatch(threadNum);
Set<Integer> set=new HashSet<>();
//开启子线程,为了让他们的锁一致,多个线程需要同一个对象
MyTask myTask = new MyTask(countDownLatch,set);
for (int i = 0; i < threadNum; i++) {
new Thread(myTask).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
class MyTask implements Runnable {
private final Set<Integer> set;
private CountDownLatch countDownLatch;
private int ticket = 100;
public MyTask(CountDownLatch countDownLatch, Set<Integer> set) {
this.countDownLatch = countDownLatch;
this.set=set;
}
@Override
public void run() {
while (ticket > 0) {
sale();
try {
Thread.currentThread().sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
countDownLatch.countDown();
}
//两种synchronized方式都可以保证同步
public synchronized void sale() {
System.out.println(Thread.currentThread().getName() + "::::::::出售第" + ticket);
if(set.contains(ticket)){
System.err.println("重复出票:"+ticket);
}else{
set.add(ticket);
}
ticket--;
}
public void sale1() {
synchronized (MyTask.class) {
System.out.println(Thread.currentThread().getName() + "::::::::出售第" + ticket--);
}
}
}
使用定长线程池获取线程,Lock类的方式保证数据同步:
public static void main(String[] args) {
//定长线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(20);
MyTaskInThreadPool myTaskInThreadPool = new MyTaskInThreadPool();
for (int i = 0; i < 100; i++) {
fixedThreadPool.execute(myTaskInThreadPool);
}
}
static class MyTaskInThreadPool implements Runnable{
private int ticket = 100;
private Lock ticketLock =new ReentrantLock();
//买票方法
public void sale() {
//重入锁
ticketLock.lock();
try {
System.out.println(Thread.currentThread().getName() + "::::::::出售第" + ticket);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
--ticket;
}finally {
//保证释放锁
ticketLock.unlock();
}
}
@Override
public void run() {
sale();
}
}
使用继承类Thread创建多线程,使用Lock.tryLock()保证数据安全:
public static void main(String[] args) {
Lock lock = new ReentrantLock();//必须让所有线程使用同一个lock
Set<Integer> set=new HashSet<>();
for (int i = 0; i < 10; i++) {
new MyTaskExtends(lock, set).start();//开启10个线程执行
}
}
class MyTaskExtends extends Thread {
private final Set<Integer> set;
// private int ticket=100;//创建一个对象就有100张票,相当于每开启一个线程,都会从第100张票开始出售。改用下面的做法
private static int ticket=100;//静态变量是所有对象都共享,100张票。几个线程,卖的都是一个票。
private Lock lock;
public MyTaskExtends(Lock lock, Set<Integer> set) {
this.lock = lock;
this.set=set;
}
@Override
public void run() {
// boolean b = lock.tryLock();
while (ticket > 0) {
boolean b = false;
try {
b = lock.tryLock(10, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ticket <= 0) {
System.out.println("ticket<0,线程返回"+set.toString());
return;
}
if (b) {
try {
System.out.println(Thread.currentThread().getName() + ":::::出售票:" + ticket);
//利用set集合判断是否重复售票
if(set.contains(ticket)){
System.err.println("重复售票:"+ticket);
}else {
set.add(ticket);
System.out.println("添加:"+ticket);
}
ticket--;
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {//释放锁时,必须保证锁是获取到了的
System.out.println(Thread.currentThread().getName() + ":::::释放锁");
lock.unlock();
}
} else {
System.out.println(Thread.currentThread().getName() + ":::::没有获取到锁");
}
}
}
}
使用FutureTask和Callable创建有返回值的子线程
public static void main(String[] args) {
//使用Lambda表达式创建Callable对象
//使用FutureTask类来包装Callable对象
Integer taskNo=100;
FutureTask<Object> future = new FutureTask<>((Callable<Object>) () -> {
return "this is callable";
});
//实质上还是以Callable对象来创建并启动线程
new Thread(future,"有返回值的线程").start();
try {
System.out.println("子线程的返回值:" + future.get());//get()方法会阻塞,直到子线程执行结束才返回
} catch (Exception e) {
e.printStackTrace();
}
}
实现Callable接口创建有返回值的线程,使用synchronized保证数据安全
public static void main(String[] args) {
Set<Integer> set=new HashSet<>();
MyTaskFuture myTaskFuture = new MyTaskFuture(set);
List<FutureTask> list=new ArrayList<>(50);
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);//创建10个执行人
for (int i = 0; i < 110; i++) {//模拟110个并发量,异步提交执行抢票
FutureTask ft=new FutureTask(myTaskFuture);//添加执行的任务
list.add(ft);
fixedThreadPool.submit(ft);//提交执行任务给10个执行人
}
System.out.println("主线程异步提交执行计算,去做其他事情!");
System.out.println("主线程阻塞,等待异步执行完成的数据");
for (FutureTask ft : list) {
try {
String res = String.valueOf(ft.get());
System.out.println(res);
if(res.contains("error")){
System.err.println(res);
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
// 关闭线程池
fixedThreadPool.shutdown();
}
class MyTaskFuture implements Callable{
private final Set<Integer> set;
int ticket=100;
public MyTaskFuture(Set<Integer> set) {
this.set = set;
}
@Override
public String call() throws Exception {
while (ticket>0){
synchronized (MyTaskFuture.class){
if(ticket>0){
if(set.contains(ticket)){
// System.out.println("重复售票:"+ticket);
return Thread.currentThread().getName()+"error重复售票"+ticket;
}else {
set.add(ticket);
}
// System.out.println(Thread.currentThread().getName()+":::::售票:"+ticket);
return Thread.currentThread().getName()+"成功售票:"+ticket--;
}else {
return Thread.currentThread().getName()+"售票结束,你没抢到票!";
}
}
}
return Thread.currentThread().getName()+"售票结束,你没抢到票";
}
}
线程及线程池参考:https://blog.youkuaiyun.com/BigBug_500/article/details/90294569