1.开启线程的方式
创建线程的3种方式(1.继承Thread类 2.实现Runnable接口 3.实现Callable接口)
继承Thread类:
- 写一个类,继承Thread类
- 重写run()方法
- 创建子类对象
- 调用子类对象的start()方法
public class MyThread extends Thread{
public void run(){
//重写父类方法
}
}
public class Demo{
public static void main(String[] args){
MyThread thread = new MyThread();
Thread.start();
}
}
实现Runnable接口
- 定义一个类MyRunnable实现Runnable接口
- 在MyRunnable类中重写run()方法
- 创建MyRunnable类的对象
- 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
- 启动线程
public class MyRunnable implements Runnable{
public void run(){
//线程启动后执行的代码
}
}
public class Demo{
psvm{
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
t1.start();
}
}
实现Callable接口
- 定义一个类MyCallable实现Callable接口
- 在MyCallable类中重写call()方法
- 创建MyCallable类的对象
- 创建Future的实现类FutureTask对象,把MyCallable对象作为构造方法的参数
- 创建Thread类的对象,把FutureTask对象作为构造方法的参数
public class MyCallable implements Callable<String>{
public String call() throws Exception {
//实现call方法
//返回值表示线程运行完毕之后的结果
return “…”;
}
}
public class Demo{
psvm{
//线程开启之后需要执行里面的call方法
MyCallable mc = new MyCallable();
//可以获取线程执行完毕之后的结果,也可以作为参数传递给Thread对象
FutureTask<String> ft = new FutureTask<>(mc);
//创建线程对象
Thread t1 = new Thread(ft);
//开启线程
t1.start();
//get();方法会阻塞,要先start(),再get()
}
}
2.什么情况下会有线程安全的问题
安全问题出现的条件:
- 是多线程环境
- 有共享数据
- 有多条语句操作共享数据,同时访问并且要改
3.线程安全的问题产生时,如何解决
- 同步代码块:synchronized(锁对象){}//锁对象可以是任意对象,必须多个线程共享一个
- 同步方法:synchronized 方法名() {} 锁是 this
- 同步静态方法:static sunchronized 方法名(){} 锁是:类名.class
- 可以采用它的实现类ReentrantLock来实例化Lock接口对象
Lock lock = new ReentrantLock();
lock.lock();//获取锁
lock.unlock();//释放锁,一般写在finally块中
同步方法:就是把synchronized关键字加到方法上
格式:
修饰符synchronized返回值类型方法名(方法参数){ }
同步代码块和同步方法的区别:
同步代码块可以锁住指定代码,同步方法是锁住方法中所有代码
同步代码块可以指定锁对象,同步方法不能指定锁对象
同步方法的锁对象是:this
4.生产者消费者模型
- 步骤:
先创建3个角色
生成者线程
消费者线程
共享资源(Desk类)
1.是否有汉堡的状态
2.锁对象
3.总共生产汉堡包的总数量 - 厨师线程:
死循环,实现同步(多线程同步之后才能相互通信),数量为0,就结束
//有汉堡,就等待
//没有,就生产,修改状态为有,通知吃货 - 吃货线程:
死循环,实现同步,数量为0,就结束
//有汉堡包: 吃, 修改状态为false,总数量–,通知厨师
//没有汉堡: 就等待
涉及线相关成员方法:
wait(),notify(),notifyAll()
注意:
wait(),notify(),notifyAll()只能在同步代码中使用.