多线程
线程创建方式一
继承Thread类。
/**
*
* 线程的创建方式一:
* A、继承了Thread类 重写run方法
* B、start开启线程 start()--->JVM--->run();
* C、线程和线程之间是竞争关系,共同争抢CPU的资源 所以每次执行结果不一定相同
*/
public class TestA {
public static void main(String[] args) {
DemoThread thread = new DemoThread("兔子");
DemoThread thread2 = new DemoThread("乌龟");
thread.setPriority(10);//设置线程的优先级 范围是【1-10】 数字越大优先级越高
thread2.setPriority(10);
//Thread.currentThread().setPriority(1);//设置主线程的优先级
//System.out.println( thread.getPriority());//获得线程的优先级 默认是5
//System.out.println( thread2.getPriority());
thread.start();//启动兔子的线程
thread2.start();//启动乌龟的线程
//main主线程的代码
System.out.println("main线程");
}
}
class DemoThread extends Thread{
private String name;
public DemoThread() {
}
public DemoThread(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 1; i < 100; i++) {
System.out.println(name+"跑了"+i+"米");
}
}
}
//run() 线程体,线程要完成的任务
//start() 启动线程,线程进入就绪队列,等待获取CPU并执行
线程的创建方式二
实现Runnable接口。
/**
*
* 线程的创建方式二
* 【1】实现了Runnable接口 重写run方法
* 【2】开始线程的时候 Thread tuzi = new Thread(demoRunnable,"兔子");
* tuzi.start();
* 优点:
* 【1】解决了java中单继承的特点
* 【2】可以实现县城之间的数据共享
*/
public class TestB {
public static void main(String[] args) {
DemoRunnable demoRunnable = new DemoRunnable();
//兔子线程
Thread tuzi = new Thread(demoRunnable,"兔子");
Thread wugui = new Thread(demoRunnable,"乌龟");
tuzi.start();
wugui.start();
}
}
class DemoRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"跑了"+i+"米");
}
}
}
两种方式的优缺点
方式一:继承Thread类
缺点:Java单继承,无法继承其他类;优点:代码稍微简单
方式二:实现Runnable接口
优点:还可以去继承其他类,便于多个线程共享同一个资源;缺点:代码略有繁琐。实际开发中,方式二使用更多一些
Thread的属性和方法
currentThread(); //返回对当前正在执行的线程对象的引用
getName(); //返回线程的名称
getPriority(); //返回线程的优先级
run(); //如果该线程是使用独立的Runnable运行对象构造的,则调用该Runnable对象的run方法;否则,该方法不执行任何操作并返回。
setName(); //改变线程名称,使之参数name相同
setPriority(); //改变线程的优先级
start(); //使该线程开始执行;Java虚拟机调用该线程的run方法。
线程的创建方式三
JDK1.5后推出了第三种定义线程的方式,实现Callable接口。改该方式最大的变化是可以有返回值,并且可以抛出检查异常。
/**
*
* 线程的实现方式三:
* 【1】implements Callable<> 重写call()
* 【2】 DemoCallable demoCallable = new DemoCallable();
* FutureTask futureTask = new FutureTask(demoCallable);
* Thread thread1 = new Thread(futureTask);
* thread1.start();
* 优点:
* 【1】JDK1.5以后提供的,并发线程实现方式是java.util.concurrent包下的
* 【2】call()方法可以获得返回值
* 【3】call()方法是可以抛出异常的
*
*/
public class TestC {
public static void main(String[] args) throws ExecutionException, InterruptedException {
DemoCallable demoCallable = new DemoCallable();
FutureTask futureTask = new FutureTask(demoCallable);
FutureTask futureTask2 = new FutureTask(demoCallable);
Thread thread1 = new Thread(futureTask,"线程A");
Thread thread2 = new Thread(futureTask2,"线程B");
thread1.start();
thread2.start();
System.out.println("线程A是否取消"+futureTask.isCancelled());
futureTask.cancel(true);
System.out.println("线程A是否取消"+futureTask.isCancelled());
//接收线程方法的返回值 等待线程运行结束才可以获得返回值
if (!futureTask.isCancelled()&& !futureTask2.isCancelled()){
System.out.println(futureTask.get());//获得方法的返回值
System.out.println(futureTask2.get());
}
}
}
class DemoCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int i = new Random().nextInt(10);
System.out.println(Thread.currentThread().getName()+"产生的随机数为:"+i);
return i;
}
}
与Runnable相比,Callable功能更强大写
- 方法名不同
- 可以有返回值,支持泛型的返回值
- 可以抛出检查异常
- 需要借助FutureTask,比如获取返回结果
线程的生命周期
-
join()
阻塞指定线程等到另一个线程完成以后再继续执行(插队,谁调用的谁先执行,其他线程等待)
-
sleep()
线程停止运行一段时间,让出CPU,将处于阻塞状态
如果调用了sleep方法之后,没有其他等待执行的线程,这个时候当前线程不会马上恢复执行
-
yield()
让当前真该执行线程暂停,不是阻塞线程。而是将线程转入就绪状态
如果调用了yield方法之后,没有其他等待执行的线程,这个时候当前线程就会马上恢复执行
-
setDaemon()
可以将指定的线程设置成后台线程
创建后台线程的线程结束时,后台线程也随之消亡
只能在线程启动之前把他设为后台线程
-
interrupt()
并没有直接中断线程,而是需要被中断线程自己处理(信号灯)
-
stop()
结束线程,不推荐使用
线程同步
同步代码块锁
• synchronized (锁){ 同步代码 }
-
必须是引用数据类型,不能是基本数据类型
-
在同步代码块中可以改变同步监视器对象的值,不能改变其引用
-
尽量不要String和包装类Integer做同步监视器.如果使用了,只要保证代码块中不对其进行任何操作也没有关系
-
一般使用共享资源做同步监视器即可
-
也可以创建一个专门的同步监视器,没有任何业务含义
建议使用final修饰同步监视器
同步方法锁
• private synchronized void aa(int amt) {}
使用synchronized修饰的方法就是同步方法,锁代表的就是this。
注意:不推荐使用synchronized修饰run()方法
同步静态方法
方法使用static和synchronized同时修饰的方法成为同步静态方法,锁是类的字节码对象ti.getClass()
注意:同一个类new出的不同对象的类的字节码是相同的
线程同步 优点和缺点
· 优点:安全
· 缺点:效率低下 ,可能出现死锁
死锁
- 多个线程共享多个资源
ed修饰的方法就是同步方法,锁代表的就是this。
注意:不推荐使用synchronized修饰run()方法
同步静态方法
方法使用static和synchronized同时修饰的方法成为同步静态方法,锁是类的字节码对象ti.getClass()
注意:同一个类new出的不同对象的类的字节码是相同的
线程同步 优点和缺点
· 优点:安全
· 缺点:效率低下 ,可能出现死锁
死锁
- 多个线程共享多个资源
- 多个线程都需要其他线程的资源,每个线程又不愿放弃自己的资源