#java线程
##创建线程的方式
###方法一、继承thread类
一、继承Thread类,重写其run方法,再调用start方法开启此线程,
start方法先启动当前线程,调用其run方法,
注意点:
1、直接调用run方法将不会开启线程,相当于直接创建对象调用方法
2、重复启动一个线程将会报异常,原因:在县城第一次启动时,状态值为0;启动后会修改
3、想要开启多个线程,需创建多个对象,调用其start方法
###方法二、实现Runnable接口
实现Runnable接口,重写run()方法
把实现了Runnable的类作为一个参数构造一个Thread类,
调用start()启动线程
class Window implements Runnable;
Window w1 = new Window();
Thread t1 = new Thread(w1);
t1.start();
###方法三、实现Callable接口
实现Callable接口,重写call()方法,
把实现了Runnable的类作为一个参数构造一个FutureTask类
将FutureTask类对象作为一个参数构造一个Thread类,再调用start()启动线程
####实现Callable接口更强大
1.call可以有返回值
2.可以抛出异常,被外面捕获
3.支持泛型
###方法四、使用线程池
好处:
1.提高响应速度
2.降低资源消耗
3.设置线程池属性,便于线程管理
可以设置线程池的大小、最大连接数量、超时时间
创建一个设定初始大小的线程池
class Number implements Runnable;
class Number1 implements Runnable
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);//创建线程池
service.execute(new Number());//适用于Runnable接口,执行线程
service.execute(new Number1());//适用于Runnable接口,执行线程
// service.submit();适用于Callable接口
service.shutdown();//关闭线程池
}
####获取返回值
把实现了Runnable的类作为一个参数构造一个FutureTask类,再调用其get方法获取返回值
class NumThread implements Callable{
@Override
public Object call() throws Exception {
Integer sum = 0;
for (int i =0;i<101;i++){
sum+=i;
System.out.println(i);
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
NumThread numT = new NumThread();
FutureTask futureTask = new FutureTask(numT);
new Thread(futureTask).start();
try {
//get()获取返回值
Object sum = futureTask.get();
System.out.println("总和为:"+sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
####线程方法:
测试Thread的常用方法
start()启动当前线程,调用其run方法
run(),重写Thread的方法,该线程所做的操作代码写在里面
currentThread(),静态方法返回当前线程的代码
this.yield();释放当前线程的CPU的执行权
join();在线程A中调用线程B的join方法,线程A进入阻塞状态,直到线程B执行完毕,线程A才结束阻塞状态
stop:强制结束线程的生命周期,已经过时。。
sleep(long sillitime)让指定的线程“睡眠”sillitime毫秒数,在sillitime指定的毫秒内,该线程为阻塞状态
isAlive()返回线程的当前状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t4ua10ym-1623132330225)(img.png)]
####线程的生命周期
新建,就绪,运行,阻塞,消亡
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gJ3uyJpA-1623132330228)(img_1.png)]
####线程同步问题
当多个线程同时操作一个共享数据,就会出现同步问题
#####方法一、同步代码块
private Integer piao = 1000;
Object o = new Object();
@Override
public void run() {
while (true) {
synchronized (o) {
if (piao > 0) {
System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + piao);
piao--;
} else {
break;
}
}
}
}
用synshronized(Object obj){代码块}
锁对象必须唯一,多个线程必须使用同一把锁
在实现Runnable接口的多线程中,可以考虑用this充当同步监视器
在继承Thread类的多线程中,可以考虑用this.class充当同步监视器
操作一个线程时,其他线程都等待,进入就绪状态
#####方法二、同步方法
当同步的代码块刚好全部在方法中是,我们可以把synchronized放在方法上
public synchronized void show(){
if (piao > 0) {
System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + piao);
piao--;
}
}
该方法默认以this充当同步监视器
用以解决继承Thread的线程的方法时需把方法设置为static,以this.class充当同步监视器
静态方法的同步方法的同步监视器:this.class
非静态方法的同步方法的同步监视器:this
#####解决方式三、lock锁
class Lock1 implements Runnable{
private Integer ticket = 100;
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try{
lock.lock();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"售票,票号为:"+ticket);
ticket--;
}
}finally {
lock.unlock();
}
}
}
}
###死锁
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源锁,就造成了死锁现象
不会出现异常,不会出现提示,
###线程通行
wait():使当前线程进入阻塞状态,并释放同步监视器
notify():唤醒一个wait()的线程,如果有多个,那就唤醒一个优先级高的
notifyAll():唤醒所有wait()的线程,
wait(),notify(),notifyAll()三个方法必须存在于同步代码块或者同步方法中
wait(),notify(),notifyAll()的调用者必须是同步代码块或者同步方法中的同步监视器
wait(),notify(),notifyAll()这三个方法定义在Object类中
##面试题
###lock锁和synchronize有什么异同
lock.lock()–加锁
lock.unlock()–解锁
相同:都可以解决线程的安全问题
不同:synchronize是自动释放锁,lock锁是需要自己开启和关闭同步监听器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kI5vL8Xa-1623132330231)(img_2.png)]
###sleep()和wait()的异同
####相同:
都是使当前线程进入阻塞状态
####不同:
声明不一样:sleep声明在Thread类中,wait声明在Object类中
调用要求不一样:sleep可以在任何需要的时候调用,wait必须在同步代码块或者同步方法中调用
是否释放同步监视器:sleep–不释放,wait–释放。