多线程
2019年5月11日
10:25
多线程:多任务同时执行就是多线程,如果没有任务,就不需要使用多线程
进程和线程的区别:
进程:资源分配的最小单位
线程:cpu分配的最小单位
进程中包含一个或多个线程
线程的开启方法:
1) class Demo implements Thread类,重写run(),
创建子类对象 Demo demo=new Demo();
demo.start();
2)实现Runnable接口,重写run()方法,
用代理模式调用run();
实现的Runnable接口中没有.start方法,
只有在Thread方法中有.start,所以需要Thread对象代理实现run方法
3) 3.实现Callable接口,重写call()方法 (了解):代码比较复杂
线程的状态:线程有5个状态
1)、新生状态:new线程对象时,线程进入新生状态;
2)、就绪状态:调用start()方法,线程进入就绪状态,进入到就绪队列,进入就绪状态代表线程有能力执行,但是要等到cpu调用,分配时间片才能执行
3)、运行状态:当前cpu调度,分配时间片给就绪状态的线程,当前线程执行
4)、阻塞状态:sleep...如果线程一旦进入到阻塞状态不会直接进入运行状态,阻塞状态解除要进入到就绪状态
5)、终止状态:现成饭结束
线程一个旦结束,无法恢复,如果重新开启,也是一个新的线程
如何控制线程的终止:
1.调用stop(),destory(),已过时,不推荐
2.线程正常执行结束
3.添加标识控制
进入线程就绪状态的几种情况:
1.start()方法
2.yield 礼让线程
3.线程之前切换
4.解除阻塞状态,线程进入到就绪状态
进入线程阻塞状态的几种情况:
1.sleep方法
2.join方法
3.wait方法
4.IO操作
提到线程不得不提控制线程安全的锁synchronized
多个线程同时操作同一份资源的时候,可能会发生线程不安全问题
如:懒汉式单例----只能有一个对象
class SingleTon{
//1.私有的静态的该类的应用
private static SingleTon single=null;
//2.私有的构造器
private SingleTon(){}
//3.公共的静态的访问方式
public static SingleTon newInstance(){
if(single==null){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
single=new SingleTon();
}
}
return single;
}
}
当多个线程同时操作这个方法时,多个线程会同时进入这个方法,然而在判断条件--if(single==null)--如果为空,后有一个程序休眠,那么一定会有多个程序通过这个判断,第一个线程度过休眠继续执行并创建对象之后,if(single==null)这个条件才不成立,这时已经有其他线程进入方法内部了,会继续创建对象,这样的单例就毫无意义了。
这时便需要对这个方法进行加锁:
*****控制线程安全:加锁 synchronized 同步
同步方法: 在方法上面加锁
同步静态方法
同步成员方法
同步块: synchronized(){}
锁this,锁资源,锁类
锁的必须是不变的内容!!!!
锁的范围太大,效率低,锁的范围太小,容易锁不住
以上面单例代码为例:
对方法进行上锁:每个线程必须等当前线程执行完这个方法再进入
public synchronized static SingleTon newInstance(){
if(single==null){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
single=new SingleTon();
}
return single;
}
对类上锁
public static SingleTon newInstance(){
if(single==null){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//
synchronized (SingleTon.class) {
if(single==null){
single=new SingleTon();
}
}
}
return single;
}
值得注意的是锁的必须是不变的内容,但我们常会遇到共享资源仅为一个变量的时候,这个时候怎么办呢?如下列:模拟抢票
public class Web12306_03 implements Runnable{
//100张票资源
int tickets=100;
/*
* 买票
*/
@Override
public void run() {
while(true){
//停止条件
if(tickets<=0){
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"买了第"+tickets--);
}
}
public static void main(String[] args) {
Web12306_03 web=new Web12306_03();
//开启线程
new Thread(web,"A").start();
new Thread(web,"B").start();
new Thread(web,"C").start();
}
}
上列中,3条线程共享一个资源tickets,如果不上锁,则线程不安全,如果对类,方法上锁会占用太多资源,而tickets是一个变量不能被锁!!!这个时候我们可以将tickets变为一个引用,简单讲就是将tickets提取当方法外面,将tickets定义成一个外部类,并将需要的变量定义在这个外部类里面,这样我们调用这个变量的时候可以引用.变量,引用是不变的,这样就可以锁住了,如下列:
public class Web12306_04 implements Runnable{
//100张票资源
Ticket tickets=new Ticket();
/*
* 买票
*/
@Override
public void run() {
while(true){
//停止条件
synchronized (tickets) { //锁资源的地址,是对象肯定能锁住
if(tickets.num<=0){
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"买了第"+tickets.num--);
}
}
}
public static void main(String[] args) {
Web12306_04 web=new Web12306_04();
//开启线程
new Thread(web,"A").start();
new Thread(web,"B").start();
new Thread(web,"C").start();
}
}
class Ticket{
int num=100;
}