线程
从软硬件实现多条执行流程的技术
多线程的创建
继承Thread类
优点:编码简单
缺点:无法继承其他类了,不利于扩展
public class Thread_1 {
//3.创建线程对象
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();//直接调用run方法会被当成普通方法执行,调用start方法才是启动一个新的的线程
//主线程,要把子线程任务放到主线程之前,否则就是单线程了
for (int i = 0; i < 3; i++) {
System.out.println("主线程"+i);
}
}
}
//1.继承Thread类
class MyThread extends Thread {
//2.重写run方法
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("子线程"+i);
}
}
}
实现Runnable接口
优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强
缺点:编程多一层对象包装,如果线程有执行结果不可以直接返回
public class Thread_2 {
public static void main(String[] args) {
// 2.创建MyRunnable对象
Runnable runnable = new MyRunnable();
// 3.把MyRunnable对象交给Thread处理
Thread thread = new Thread(runnable);
// 4.调用线程对象的start方法启动线程
thread.start();
//主线程
for (int i = 0; i < 3; i++) {
System.out.println("主线程"+i);
}
}
}
class MyRunnable implements Runnable{
@Override
// 1.定义一个线程任务类MyRunnable实现Runnable接口,重写run方法
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("子线程"+i);
}
}
}
//简化版
public class Thread_2 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("子线程"+i);
}
}
}).start();
//主线程
for (int i = 0; i < 3; i++) {
System.out.println("主线程"+i);
}
}
}
实现Callable接口
优点:可以得到线程执行的结果
缺点:代码相对较多
public class Thread_3 {
public static void main(String[] args) {
Callable call = new MyCallable(100);
//把Callable对象交给Futuretask对象,可以交给thread处理,并获得结果
FutureTask<String> task = new FutureTask<String>(call);
new Thread(task).start();
//主线程
Callable call2 = new MyCallable(200);
FutureTask<String> task2 = new FutureTask<String>(call2);
new Thread(task2).start();
try {
//通过get方法获取数据
String s = task.get();
System.out.println("第一个"+s);
} catch (Exception e) {
e.printStackTrace();
}
try {
String s1 = task2.get();
System.out.println("第二个"+s1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
class MyCallable implements Callable<String>{
private int n;
public MyCallable(int n) {
this.n = n;
}
@Override
public String call() throws Exception {
n+=1;
return "结果为"+n;
}
}
线程常用方法
setName(),getName(),currentThread()
public class ThreadDemo01 {
// main方法是由主线程负责调度的
public static void main(String[] args) {
Thread t1 = new MyThread("1号");
// t1.setName("1号");
t1.start();
System.out.println(t1.getName());
Thread t2 = new MyThread("2号");
// t2.setName("2号");
t2.start();
System.out.println(t2.getName());
// 哪个线程执行它,它就得到哪个线程对象(当前线程对象)
// 主线程的名称就叫main
Thread m = Thread.currentThread();
System.out.println(m.getName());
m.setName("最牛的线程");
for (int i = 0; i < 5; i++) {
System.out.println( m.getName() + "输出:" + i);
}
}
}
public class MyThread extends Thread{
public MyThread() {
}
public MyThread(String name) {
// 为当前线程对象设置名称,送给父类的有参数构造器初始化名称
super(name);
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println( Thread.currentThread().getName() + "输出:" + i);
}
}
}
线程的休眠
public class ThreadDemo02 {
// main方法是由主线程负责调度的
public static void main(String[] args) throws Exception {
for (int i = 1; i <= 5; i++) {
System.out.println("输出:" + i);
if(i == 3){
// 让当前线程进入休眠状态
// 段子:项目经理让我加上这行代码,如果用户愿意交钱,我就注释掉。
Thread.sleep(3000);
}
}
}
}
线程同步
为了解决线程安全问题,让多个线程实现先后依次访问共享资源
加锁:把共享资源上锁,每次只能一个线程进入访问,完毕以后解锁,然后其他线程才能进来
方式一:同步代码块
把出现线程安全问题的核心代码上锁,用synchronized环绕,不推荐,一把锁住了所有线程
会影响其他无关线程的执行
规范:1.建议使用共享资源作为锁对象
2.实例方法建议使用this作为锁对象
3.静态方法用字节码(类名.class)作为锁对象
synchronized (this) {
// 2、判断余额是否足够
if (this.money >= money) {
// 钱够了
System.out.println(name + "来取钱,吐出:" + money);
// 更新余额
this.money -= money;
System.out.println(name + "取钱后,余额剩余:" + this.money);
} else {
// 3、余额不足
System.out.println(name + "来取钱,余额不足!");
}
方式二:同步方法
给核心方法上锁,每次只能一个线程进入方法,执行后解锁,其它线程再次进入
public synchronized void drawMoney(double money)
方式三:同步锁
private final Lock lock = new ReentrantLock();
public void drawMoney(double money) {
// 1、拿到是谁来取钱
String name = Thread.currentThread().getName();
// 2、判断余额是否足够
// 小明 小红
lock.lock(); // 上锁
try {
if(this.money >= money){
// 钱够了
System.out.println(name+"来取钱,吐出:" + money);
// 更新余额
this.money -= money;
System.out.println(name+"取钱后,余额剩余:" + this.money);
}else{
// 3、余额不足
System.out.println(name+"来取钱,余额不足!");
}
} finally {
lock.unlock(); // 解锁
}
}
线程池
可以复用线程的技术,反复创建线程的开销会很大,会严重影响系统性能
问题:
谁代表线程池?
ExecutorService接口
实现类:
临时线程什么时候创建?
新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程
什么时候会开始拒绝任务?
核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始任务拒绝
线程池处理Runnable任务
ExecutorService pool = new ThreadPoolExecutor(3, 5 ,6, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5) , Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy() );
// 2、给任务线程池处理。
Runnable target = new MyRunnable();
pool.execute(target);
线程池处理Callable任务
ExecutorService pool = new ThreadPoolExecutor(3, 5 ,6, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5) , Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy() );
// 2、给任务线程池处理。
Future<String> f1 = pool.submit(new MyCallable(100));
System.out.println(f1.get());
Executors得到线程池对象的常用方法
定时器
方式一:Timer定时器
Timer timer = new Timer(); // 定时器本身就是一个单线程。
// 2、调用方法,处理定时任务
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行AAA~~~" + new Date());
}
}, 0, 2000);
方式二:ScheduledExecutorServiced定时器
// 1、创建ScheduledExecutorService线程池,做定时器
ScheduledExecutorService pool = Executors.newScheduledThreadPool(3);
// 2、开启定时任务
pool.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "执行输出:AAA ==》 " + new Date());
并发:CPU分时轮询的执行线程
并行:同一时刻同时在执行