线程创建
三种创建方式:
1.继承Thread类
>自定义线程类继承Thread类
>重写run()方法,编写执行体
>创建线程对象,调用start()方法启动线程
不建议使用:避免单继承局限性
//创建线程方式一: 继承Thread类,重写run()方法,调用start开启线程
//总结:注意,线程开启不一定立即执行,有cpu调度执行
public class TestThread1 extends Thread{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("thread" + i);
}
}
public static void main(String[] args) {
//main线程,主线程
//创建一个线程对象
TestThread1 testThread1 = new TestThread1();
//调用start()方法开启线程
testThread1.start();
for (int i = 0; i < 1000; i++) {
System.out.println("main" + i);
}
}
}
2.实现Runnable接口
>定义MyRunnable类实现Runnable接口
>实现run()方法,编写线程执行体
>创建线程对象,调用start()方法启动线程
推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
public class TestThread3 implements Runnable {
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("thread" + i);
}
}
public static void main(String[] args) {
//main线程,主线程
//创建Runnable接口的实现类对象
TestThread3 testThread3 = new TestThread3();
//创建线程对象,通过线程对象来开启我们的线程,代理
// Thread thread = new Thread(testThread3);
// thread.start();
new Thread(testThread3).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main" + i);
}
}
}
3.实现Callable接口
>实现Callable接口,需要返回值类型
>重写call方法,需要抛出异常
>创建目标对象
>创建线程池:ExecutorService executorService = Executors.newFixedThreadPool(1);
>提交执行:Future<Boolean> submit = executorService.submit(testCallable);
>获取结果(会阻塞主线程):Boolean aBoolean = submit.get();
>关闭线程池:executorService.shutdown();
public class TestCallable implements Callable<Boolean> {
@Override
public Boolean call() {
//call方法线程体
for (int i = 0; i < 200; i++) {
System.out.println("thread" + i);
}
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//main线程,主线程
//创建接口的实现类对象
TestCallable testCallable = new TestCallable();
//创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(1);
//提交执行线程
Future<Boolean> submit = executorService.submit(testCallable);
//获取结果--get()方法会阻塞主线程
Boolean aBoolean = submit.get();
//关闭线程池
executorService.shutdown();
for (int i = 0; i < 1000; i++) {
System.out.println("main" + i);
}
}
}
线程状态
线程停止
>推荐线程自己停止,自定义中断标识符
//测试停止线程
//1.建议线程正常停止--》利用次数,不建议死循环
//2.建议使用标志位-->设置一个标志位
//3.不要使用stop或者destroy等过时的方法
public class TestStop implements Runnable{
//1.设置一个标识位
private boolean flag =true;
@Override
public void run() {
int i =0;
while (flag){
System.out.println("run...Thread"+i++);
}
}
//2.设置一个公开的方法停止线程,转换标志位
public void myStop(){
this.flag=false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main"+i);
if(i==900){
testStop.myStop();
System.out.println("线程停止了");
}
}
}
}
线程休眠
>Thread.sleep();
>sleep(时间)指定当前线程阻塞的毫秒数
>sleep存在异常InterruptedException
>sleep时间到达后线程进入就绪状态
>sleep可以模拟网络延时,倒计时等
>每一个对象都有一个锁,sleep不会释放锁
public static void main(String[] args) {
Date startTime = new Date(System.currentTimeMillis());
while (true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
startTime=new Date(System.currentTimeMillis());//更新当前时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程礼让
>Thread.yield();
>礼让线程,将线程从运行状态转为就绪状态
>让cpu重新调度,礼让不一定成功!看cpu心情
线程强制执行
>thread.join();
>用于在当前线程A中添加别的线程B,这时线程A被阻塞,处于Blocked状态,线程B开始执行,当线程B执行完以后,线程A才能进行执行
public static void main(String[] args) throws InterruptedException {
Thread threadJoin = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 1000; i++) {
System.out.println("线程插入进来了" + i);
}
}
});
threadJoin.start();
for (int i = 0; i < 1000; i++) {
if(i==200){
threadJoin.join();
}
System.out.println("main" + i);
}
}
线程优先级
>thread.setPriority(int); 设置线程优先级方法
>线程的优先级用数字表示,范围从1~10
>默认优先级都是5
> 优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了,这都是看CPU的调度
>优先级设置在start()之前
守护(daemon)线程
>thread.setDaemon(boolean); true表示设置为守护线程
>线程分为用户线程和守护线程,正常的线程都是用户线程
>虚拟机必须确保用户线程执行完毕
>虚拟机不用等待守护线程执行完毕
>如后台记录操作日志、监控内存、垃圾回收
线程同步
>线程同步是指多线程通过特定的设置(如互斥量,事件对象,临界区)来控制线程之间的执行顺序(即所谓的同步)也可以说是在线程之间通过同步建立起执行顺序的关系,如果没有同步,那线程之间是各自运行各自的!
synchronized
>普通同步方法(实例方法),锁是当前实例对象 ,进入同步代码前要获得当前实例的锁
静态同步方法,锁是当前类的class对象 ,进入同步代码前要获得当前类对象的锁
同步方法块,锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁
//obj 锁定的对象
synchronized(obj)
{
// todo
}
死锁
>所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。 因此我们举个例子来描述,如果此时有一个线程A,按照先锁a再获得锁b的的顺序获得锁,而在此同时又有另外一个线程B,按照先锁b再锁a的顺序获得锁。
Lock(锁)
>java.util.concurrent.locks.Lock 是一个类似于synchronized 块的线程同步机制。但是 Lock比 synchronized 块更加灵活。Lock是个接口,有个实现类是ReentrantLock
>Lock是显示锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁出了作用域自动释放
>Lock只有代码块锁,synchronized有代码块和方法锁
>使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更号的扩展性(提供更多的子类)
class A{
//定义lock锁
private final ReentrantLock lock=new ReentrantLock();
public void m(){
lock.lock();//加锁
try {
}finally {
lock.unlock();//解锁
//如果代码有异常,要将unlock()写入final语句块
}
}
}
wait()和notify(),notifyAll( )
>如果一个线程调用了 object.wait() 方法,那么它就会进入 object 对象的等待队列(wait set)。
这个等待队列中,可能会有多个线程,因为系统运行多个线程同时等待某一个对象。
当 object.notify() 方法被调用时,它就会从这个等待队列中随机选择一个线程,并将其唤醒。
这里希望大家注意的是,这个选择是不公平的,并不是先等待的线程就会优先被选择,这个选择完全是随机的。
线程池
>ExecutorService:线程池接口
>void execute(Runnale):执行任务命令,没有返回值,一般用来执行Runnale
>Future<T> submit(Callable<T>):执行任务,有返回值,一般用来执行Callable
>void shutdown():关闭线程池
>Executors:工具类、线程池的工厂,用于创建并返回不同类型的线程池
>提前创建好多个线程,放入线程池,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。