JavaSE(九)
一、实现多线程的两个方式
- 使用继承的方式
public class test{
public static void main(String[] args){
MyThread myThread = new Mythread();
//启动一个分支线程,开辟新的栈空间,启动线程
myThread.start();
}
}
public class MyThread extends Thread{
//JVM线程调度机制,会自动调用run方法
public void run(){
}
}
- 实现Runnable接口
public class test{
public static void main(String[] args){
//创建一个可以运行的对象
Runnable r = new Runnable();
//将可运行的对象封装成一个线程对象
Thread t = new Thread(r);
//启动线程
t.start();
}
}
public class MyRunnable implements Runnable{
//JVM线程调度机制,会自动调用run方法
public void run(){
}
}
- 实现Callable接口
//创建一个“未来任务类”对象,需要给一个Callable接口的实现类对象
FutureTask task = new FutureTask(new Callable(){
//call相当与run方法,只不过有返回值,也可以抛异常
public Object call() throws Exception{
........
........
return obj;
}
});
//创建线程对象
Thread t = new Thread(task);
//启动线程
t.start();
//获取t线程的返回结果,但是导致当前线程阻塞,需要等到t线程运行完才有返回值
Object obj = task.get();
二、多线程常用方法
- 线程的名字
MyThread t = new MyThread();
//修改线程的名字
t.setName("tttt");
//获取线程的名字,默认的名字为Thread-0、Thread-1......
t.getName();
- 获取当前线程对象
//返回值t就是当前线程对象
Thread t = Thread.currentThread();
//获取名字
t.getName();
- 线程的sleep方法
//令当前线程进入阻塞状态5000毫秒(5秒)
Thread.sleep(1000*5);
- run方法当中的异常不能throws,只能try,catch,因为子类不能比父类抛出更多的异常
- interrupt()
//中断t线程的睡眠,会使sleep报异常
t.interrupt()
- 强行终止线程
//强行终止t线程(已过时)
t.stop();
7.合理终止线程
class MyRunable implements Runnable{
//打一个布尔标记
boolean run = true;
public void run(){
.......
.......
.......
//在外部修改run,来终止线程的执行
if(run){
}else{
return;
}
}
}
- 常见的线程调度模型
- 抢占式调度模型
哪个线程的优先级比较高,抢到的cpu时间片的概率就高一些(java) - 均分式调度模型
平均分配cpu时间片,每个线程占用的cpu时间片时间长度一样
- 抢占式调度模型
- 调度相关方法
Thread currentThread = Thread.currentThread();
currentThread.start();
//获取优先级
currentThread.getPriority();
//修改线程的优先级,默认5,最高10,最小1
currentThread.setPriority(10);
//让位,当前线程暂停,回到就绪状态,让给其他线程
Thread.yield();
//合并线程
currentThread.join();//将currentThread合并到当前线程中,当前线程阻塞,currentTread线程执行,直到结束
- synchronized
// 1. 同步代码块
//线程需要抢到线程共享对象的对象锁才能执行同步代码块
synchronized(共享对象){
//同步代码块
}
// 2. 在实例方法上
//表示该方法是线程安全的
public synchronized void dosome(){
}
// 3. 在静态方法上(找类锁)
public static synchronized void dosome(){
}
- 怎么解决线程安全问题
- 尽量使用局部变量来代替静态变量和实例变量
- 创建多个对象
- 使用synchronized
- 守护线程
//启动线程之前,将线程设置为守护线程
t.setDaemon(true);
三、定时器
//创建定时器对象
Timer timer = new Timer();
//Timer timer = new Timer(ture);守护线程的方式
timer.schedule(new LogTimeTask,第一次执行的时间,间隔执行的时间);
//定时任务类,TimerTask类实现了Runnable接口
class LogTimerTask extends TimerTask{
public void run(){
}
}
四、生产者和消费者模式
- wait()和notify() (建立在synchronized线程同步的基础之上)
Object obj = new Object();
//使在obj对象上活动的线程等待,并且释放obj的对象锁
obj.wait();
//使在obj对象上等待的线程唤醒
obj.notify();
- 模仿生产者和消费者
public class ThreadTest16 {
public static void main(String[] args) {
// 创建1个仓库对象,共享的。
List list = new ArrayList();
// 创建两个线程对象
// 生产者线程
Thread t1 = new Thread(new Producer(list));
// 消费者线程
Thread t2 = new Thread(new Consumer(list));
t1.setName("生产者线程");
t2.setName("消费者线程");
t1.start();
t2.start();
}
}
// 生产线程
class Producer implements Runnable {
// 仓库
private List list;
public Producer(List list) {
this.list = list;
}
@Override
public void run() {
// 一直生产(使用死循环来模拟一直生产)
while(true){
// 给仓库对象list加锁。
synchronized (list){
if(list.size() > 0){ // 大于0,说明仓库中已经有1个元素了。
try {
// 当前线程进入等待状态,并且释放Producer之前占有的list集合的锁。
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 程序能够执行到这里说明仓库是空的,可以生产
Object obj = new Object();
list.add(obj);
System.out.println(Thread.currentThread().getName() + "--->" + obj);
// 唤醒消费者进行消费
list.notifyAll();
}
}
}
}
// 消费线程
class Consumer implements Runnable {
// 仓库
private List list;
public Consumer(List list) {
this.list = list;
}
@Override
public void run() {
// 一直消费
while(true){
synchronized (list) {
if(list.size() == 0){
try {
// 仓库已经空了。
// 消费者线程等待,释放掉list集合的锁
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 程序能够执行到此处说明仓库中有数据,进行消费。
Object obj = list.remove(0);
System.out.println(Thread.currentThread().getName() + "--->" + obj);
// 唤醒生产者生产。
list.notifyAll();
}
}
}
}