程序:一些静态代码
进程:正在运行的程序
线程:一个进程的的一条执行路径 ,每个线程都会拥有独立的运行栈和程序计数器
一个java应用,至少3个线程,
- main()主线程
- gc()垃圾回收线程
- 异常处理线程,放生异常,会影响主线程
并行:CPU同时执行多个任务
并发:一个CUP同时执行多个任务
创建多线程的方式:
1. 第一种
- 继承Thread
- 重写run()方法
- 创建子类对象
-
2. 第二种
实现Runnable
- 实现run() ;方法
- 创建实现类对象
- 创造Thread类,并将实现类传递到构造器中
-
第三种
实现Callable接口 JDK 5.0新增
- 重写call()方法 ,执行到操作
- 创建Callable实现对象
- 创建Futuretask对象,将Callable实现对象传入构造方法
- 将Futuretask对象传入Thread()对象,并调用start
需要返回值就使用Futuretask对象,的get()方法,获取的就是call的返回值
第四种:线程池
提供指定数量的线程池:ExecutorService service = Executors.newFixedThreadPool(10); 线程池数量
- 设置线程池属性:ThreadPoolExecutor server = (ThreadPoolExecutor)service ;
- 指定线程的操作,实现Runnable(使用service.execute(实现类))或者Callable(使用service.submit(实现类))接口的实现类
关闭线程池:service.shutdown();
第三种比第二种好在哪:
call有返回值,可以抛出异常,被外面操作捕获.
callable支持泛型代码如下 :
**
* 线程的四种创建方式
*/
public class CreateThreadDemo {
public static void main(String[] args) {
// 第一种
MyThread1 t1 = new MyThread1();
t1.start();
MyThread1 t2 = new MyThread1();
t2.start();
// 第一种的匿名子类的匿名对象的创建
new Thread() {
@Override
public void run() {
super.run();
}
}.start();
// 第二种
MyThread2 thread2 = new MyThread2();
Thread t3 = new Thread(thread2);
t1.start();
Thread t4 = new Thread(thread2);
t2.start();
// 第三种
MyThread3 t = new MyThread3();
FutureTask futureTask = new FutureTask(t);
new Thread(futureTask).start();
try {
//get()返回值就是call方法的返回值
Object sun = futureTask.get();
System.out.println("总和为:" + sun);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
// 第四种
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service; //可以使用server1来设置线程池的属性
//service.submit(实现Callable的对象);
//service.execute(实现Runnable对象);
service.shutdown();
}
}
// 第一种
class MyThread1 extends Thread {
@Override
public void run() {
//要开启线程执行的语句
}
}
// 第二种
class MyThread2 implements Runnable {
@Override
public void run() {
//要开启线程执行的语句
}
}
// 第三种
class MyThread3 implements Callable {
@Override
public Object call() throws Exception {
// 语句
int sun = 0;
for (int i = 0; i < 20; i++) {
sun += i;
System.out.println(i);
}
return sun;
}
}
比较1和2的:优先使用第二种
- 方法1.如果继承Thread,那么他就无法再去继承其他的父类.
- 实现更适合处理多个线程共享数据的情况
- Thread 本身也是实现Runnable接口
Thread线程常见方法
- start(): 启动当前线程,调用run()方法
- run(): 线程要执行的操作
- currentThread(): 静态 放回当前执行的线程
- getName(): 获取当前线程的名字
- setName(): 设置线程的名字
- yield(): 释放当前cpu执行权
- join(): 在线程A中调用B的join(),A进入阻塞状态,等B执行完成,在执行A.
- stop(): 过时了,强制结束线程
- sleep(long millis): 静态 让线程睡眠millis休息毫秒,时间内是阻塞状态
- isAlive(): 判断当前线程是否存活
线程优先级
- MAX_PRIORITY:10
- MIN_PRIORITY:1
- NORM_PRIORITY:5 默认优先级
- setPriority(int p); 设置优先级
- getPriority(); 获取线程优先级
线程的几种状态
- 新建:Thread类及其子类声明对象并创建,线程处于新建状态
- 就绪:新建状态被start()后,等待CPU时间片,具备执行条件,没有分配到CPU资源
- 运行:获得CPU资源
- 阻塞:人为或者执行输入输出操作,让CPU并临时中止自己的执行
- 死亡:完成全部的工作或者线程提前强制中止或者出现异常导致结束
线程安全:(例题又一个简单的买票软件)
一个线程在操作共享数据是,其他线程也进来操作 <br />** 但是加了同步,只能一个线程操作,其他线程等待,相对于单线程,效率低**<br /> 解决问题:
1.监听器
1.synchronized(同步监视器){
//需要被同步的数据,操作共享数据的代码
}
- 同步监视器:锁, 任何一个类的对象,都可以当作锁
- 多个线程共用一个锁就可以解决安全问题
- 实现Runnable接口可以使用,this来当锁
- 继承Thread可以使用当前类来充当锁
synchronized解决安全问题,操作同步代码,但是只能有一个线程参与,相当于单线程的过程
2.同步方法
将操作共享数据的代码封装到方法中
- 实现Runnable在方法中使用synchronized关键字,锁this当前对象
继承Thread方法上加上static synchronized,锁还是当前类,类名.class
3.lock锁 JDK5.0新加
创建ReentrantLock对象
- 在需要同步代码前面加对象.lock();
结束加上lock.unlock();
lock和synchronized的区别:两者都可以解决线程安全问题,synchronized执行完对应的同步代码,自动释放锁,lock要手动开启,和手动结束.lock锁,JVM将花费更少的时间来调度线程,性能更好.有更好的扩展性(提供了更多的子类)
死锁(例题中有案例)
不同的资源分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就会形成线程的死锁
解决方法:专门的算法,减少同步资源的定义,尽量避免嵌套同步
线程的通信
- wait() 执行方法,但前线程进入阻塞状态,会释放锁
- notify:执行方法,会唤醒被wait()的一个线程,多个线程被wait,就唤醒优先级高的
- notifyAll():执行方法,唤醒所有被wait的线程
上面3个必须使用在同步代码和同步方法中,调用者必须是同步代码块和同步方法的监视器(锁)
他们定义在Object类中,因为监视器可以是任意对象,任意对象一定定义在Object中
sleep() 与 wait()异同
相同点:都可以让线程进入阻塞状态
不同点:sleep()定义在Thread, wait()定义在Object
sleep()任何情况都可以执行, wait()只能在同步代码块,和同步方法中.
sleep()不释放锁释放cup执行权, wait()释放锁释放cpu执行权,