多线程回顾
一,多线程的创建方式
1)继承Thread类创建线程
2)实现Runnable接口创建线程
3)使用Callable和Future创建线程
1. ------------------------继承Thread类创建线程---------------------
通过继承Thread类来创建并启动多线程的一般步骤如下
1】d定义Thread类的子类,并重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。
2】创建Thread子类的实例,也就是创建了线程对象
3】启动线程,即调用线程的start()方法
代码实例
public class MyThread extends Thread{//继承Thread类
public void run(){
//重写run方法,中间写业务,for循环啥的
}
}
public class Main {
public static void main(String[] args){
new MyThread().start();//创建并启动线程
}
}
2. ------------------------实现Runnable接口创建线程---------------------
通过实现Runnable接口创建并启动线程一般步骤如下:
1】定义Runnable接口的实现类,一样要重写run()方法,这个run()方法和Thread中的run()方法一样是线程的执行体
2】创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象
3】第三部依然是通过调用线程对象的start()方法来启动线程
代码实例:
public class MyThread2 implements Runnable {//实现Runnable接口
public void run(){
//重写run方法
}
}
public class Main {
public static void main(String[] args){
//创建并启动线程
MyThread2 myThread=new MyThread2();
Thread thread=new Thread(myThread);
thread().start();
//或者 new Thread(new MyThread2()).start();
}
}
3. ------------------------使用Callable和Future创建线程---------------------
和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大。
》call()方法可以有返回值
》call()方法可以声明抛出异常
Java5提供了Future接口来代表Callable接口里call()方法的返回值,并且为Future接口提供了一个实现类FutureTask,这个实现类既实现了Future接口,还实现了Runnable接口,因此可以作为Thread类的target。在Future接口里定义了几个公共方法来控制它关联的Callable任务。
代码实例:
就是创建,提交执行,获取结果,关闭。
实现的方法是callable,后面填要返回的类型,自定义。
4. 略,太长了
2,多线程的状态
3,多线程一些方法
- thread.sleep() 线程睡眠 如果填(1000)为一秒
- thread.yield() 线程礼让,让另一个线程先执行,不一定成功,全看cpu心情。
- thread.join() 线程强制执行(插队),就是买票时候的插队,填到哪插到哪。
- thread.state() 查看线程状态
线程的优先级:
例子:
守护线程:
god是守护线程
4,多线程同步
方法有:
1. synchronized关键字
2. 重入锁( lock(),unlock() )
3. 特殊域变量(volatile)
4. wait() 和 notify()
5. ThreadLocal管理变量
- synchronized关键字
- 第一种:
synchronized(非匿名的任意对象,要锁住共享资源){ //锁的是变量
代码
}
- 第二种:
public synchronized void test(){
代码
}
当同步方法是非静态方法的时候,obj锁是它自己this,this被系统隐式处理
当同步方法是静态static的时候,obj锁是本类.class
- 重入锁( lock(),unlock() )
锁的产生条件:
创建:
reentrant /riː’entrənt/
adj. 再进去的;凹角的
n. 凹角;再进入
-
特殊域变量(volatile)
a.volatile关键字为域变量的访问提供了一种免锁机制
b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新
c.因此每次使用该域就要重新计算,而不是使用寄存器中的值
d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
//需要同步的变量加上volatile
private volatile int account = 100;
含有volatile的集合,也可以实现同步,线程安全:
4. wait() 和 notify()
wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。
- ThreadLocal管理变量
如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。
一些方法:
ThreadLocal() : 创建一个线程本地变量
get() : 返回此线程局部变量的当前线程副本中的值
initialValue() : 返回此线程局部变量的当前线程的"初始值"
set(T value) : 将此线程局部变量的当前线程副本中的值设置为value
//只改Bank类,其余代码与上同
public class Bank{
//使用ThreadLocal类管理共享变量account
private static ThreadLocal<Integer> account = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue(){
return 100;
}
};
public void save(int money){
account.set(account.get()+money);
}
public int getAccount(){
return account.get();
}
}
5,线程池
其中:
- Executors.newCachedThreadPool(); //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE
- Executors.newSingleThreadExecutor(); //创建容量为1的缓冲池
- Executors.newFixedThreadPool(int); //创建固定容量大小的缓冲池
- newFixedThreadPool:创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue;
- newSingleThreadExecutor:将corePoolSize和maximumPoolSize都设置为1,也使用的LinkedBlockingQueue;
- newCachedThreadPool:将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程。
示例: