多线程
一、什么是线程?
线程是一个进程(应用程序)中的执行场景。一个进程可以启动多个线程。多线程并发执行可以提高程序的效率, 可以同时完成多项工作。
二、多线程的作用?
多线程不是为了提高执行速度,而是提高应用程序的使用率。
线程和线程共享“堆内存和方法区内存”,栈内存是独立的,一个线程一个栈。
可以给现实世界中的人类一种错觉:感觉多个线程在同时执行。
三、实现
1)继承Thread类
public class MyThread extends Thread{
public void run(){ //重写
}
MyThread myThread=new MyThread ();
myThread.start(); //start()使该线程开始执行,JVM调用run()
//匿名内部类
new Thread(){ //继承Thread类
public void run(){
}
}.start();
2)实现Runnable接口
public class MyThread implements Runnable{
public void run(){
}
}
MyThread myThread=new MyThread ();
Thread thread=new Thread(myThread);
thread.start();
//匿名内部类
new Thread(new Runnable(){ //实现Runnable接口
public void run(){
}
}).start();
3)实现Callable接口 (有返回值Future)
public interface Callable<V> {
V call() throws Exception;
}
public class SomeCallable<V> extends OtherClass implements Callable<V> {
@Override
public V call() throws Exception {
// TODO Auto-generated method stub
return null;
}
}
Callable<V> oneCallable = new SomeCallable<V>();
//由Callable<Integer>创建一个FutureTask<Integer>对象:
FutureTask<V> oneTask = new FutureTask<V>(oneCallable);
//注释:FutureTask<Integer>是一个包装器,它通过接受Callable<Integer>来创建,它同时实现了Future和Runnable接口。
//由FutureTask<Integer>创建一个Thread对象:
Thread oneThread = new Thread(oneTask);
oneThread.start();
//至此,一个线程就创建完成了。
四、什么是线程池?
事先将多个线程放入一个容器,需要的时候去取,而不用new。根据系统环境,配置线程的数量,达到最佳运行效果。
五、为什么使用线程池?
- 降低资源损耗。减少线程的创建和销毁,每个工作线程重复利用。
- 减少响应时间。任务到达时,任务不用等待线程创建就可执行。
- 提高线程的可管理性。线程时稀缺资源,无限制的创建,会消耗系统资源,还会降低系统稳定性,使用线程池可以进行统一管理调配。
六、线程相关方法
- object.wait() :会导致线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
- object.notify() :唤醒等待的线程,这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用
- thread.join():当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续
- Thread.currentThread():获取当前线程的对象.
- Thread.sleep(毫秒,纳秒):休眠线程.
sleep和wait的区别:
sleep方法在同步方法或同步代码块中,不释放锁
wait方法在同步方法或同步代码块中,释放锁
1,这两个方法来自不同的类分别是Thread和Object
2,最主要是sleep方法没有释放锁,而wait方法释放了锁,使得敏感词线程可以使用同步控制块或者方法。
3,wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在
任何地方使用
synchronized(x){
x.notify()
//或者wait()
}
4,sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常
七、同步synchronized
synchronized 修饰方法时锁定的是调用该方法的对象。它并不能使调用该方法的多个对象在执行顺序上互斥
1)同步代码块
//使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块
//多个同步代码块如果使用相同的锁对象, 那么他们就是同步的
class XX{
Demo d = new Demo();
public static void print1() {
synchronized(d){ //锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象
//
}
}
public static void print2() {
synchronized(d){
//
}
}
}
2)同步方法
//使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的
class XX{
public static void print1() {
synchronized(Printer.class){ //锁对象可以是任意对象,但是被锁的代码需要保证是同一把锁,不能用匿名对象
//
}
}
/*
* 非静态同步函数的锁是:this
* 静态的同步函数的锁是:字节码对象
*/
public static synchronized void print2() {
//
}
}
八、线程安全
多线程并发操作同一数据时, 就有可能出现线程安全问题,使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作
Vector是线程安全的,ArrayList是线程不安全的
StringBuffer是线程安全的,StringBuilder是线程不安全的
Hashtable是线程安全的,HashMap是线程不安全的
九、ThreadLocal
ThreadLocal类为每一个线程都维护了自己独有的变量拷贝。每个线程都拥有了自己独立的一个变量。
ThreadLocal是采用哈希表的方式来为每个线程都提供一个变量的副本
ThreadLocal保证各个线程间数据安全,每个线程的数据不会被另外线程访问和破坏
十、线程的五个状态
创建、就绪、运行、阻塞、死亡