一、 JUC简介
在 Java 5.0 提供了 java.util.concurrent (简称 JUC )包
二、线程安全
什么是线程安全
当多个线程同时共享同一个全局变量或静态变量,做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。
线程安全的解决方式
使用多线程之间同步或使用锁(lock)可以解决线程安全问题。
//使用同步代码块
synchronized(同一个对象){
可能会发生线程冲突问题
}
注意:在同步代码块中,多个线程必须使用的是同一把锁,即同一个对象。
一般情况下,在使用Runnable实现的线程类中,我们会使用this作为锁对象。
在使用Thread继承的线程类中,一般会使用其Class对象(Class对象在JVM中只会创建一次)。
//使用Lock解决线程安全
class Ticket
{
private int number = 30;
//创建锁
Lock lock = new ReentrantLock();
public void sale() {
//上锁
lock.lock();
try {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出第:\t" + (number--) + "\t 还剩下:" + number);
}
} finally {
//解锁
lock.unlock();
}
}
}
public class SaleTicket
{
public static void main(String[] args)//main所有程序的入口
{
//创建资源对象
Ticket tc = new Ticket();
//创建AA线程、
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 40; i++) {
//卖票
tc.sale();
}
}
}, "AA").start();
//创建BB线程、
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 40; i++) {
//卖票
tc.sale();
}
}
}, "BB").start();
//创建CC线程、
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 40; i++) {
//卖票
tc.sale();
}
}
}, "CC").start();
}
}
三、多线程的创建方式
1 继承Thread类
①定义子类继承Thread类。
②子类中重写Thread类中的run方法。
③创建Thread子类对象,即创建了线程对象。
④调用线程对象start方法启动线程,默认调用run方法。
注意:如果只是调用run方法,则此时会在调用该方法的线程中来执行,而不是另启动一个线程。
2 实现Runnable接口
①定义子类,实现Runnable接口。
②子类中重写Runnable接口中的run方法。
③通过Thread类含参构造器创建线程对象,将Runnable接口的子类对象作为实际参数传递给
Thread类的构造方法中。
④调用Thread类的start方法启动线程,其最终调用Runnable子类接口的run方法。
两种方式的区别:
继承Thread:线程代码存放Thread子类run方法中。
实现Runnable:线程代码存在接口的子类的run方法中。
实现Runnable接口避免了单继承的局限性,多个线程可以共享同一个接口子类的对象,非常适合多个相同线程来处理同一份资源。
优先使用实现接口的方式!
3 使用Callable接口
class MyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+" Come in call");
//睡5秒
TimeUnit.SECONDS.sleep(5);
//返回200的状态码
return 200;
}
}
public class CallableTest {
public static void main(String[] args) throws InterruptedException, ExecutionException {
MyThread myThread = new MyThread();
FutureTask<Integer> futureTask = new FutureTask<>(myThread);
new Thread(futureTask, "未来任务").start();
System.out.println("主线程结束!");
Integer integer = futureTask.get();
System.out.println(integer);
}
}
4、使用线程池
JDK 5.0起提供了ExecutorService 和 Executors来实现线程池