1.进程与线程的区别
-
线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
-
一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线
-
进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段,数据集,堆等)及一些进程级的资源(如打开文件和信
号等),某进程内的线程在其他进程不可见;
- 调度和切换:线程上下文切换比进程上下文切换要快得多
2.创建线程的三种方法:
1.创建线程方式一:继承Thread类,重写run方法,调用start开启线程
public class Demo1 extends Thread{
@Override
public void run(){
//run方法线程体
for (int i = 0; i <500 ; i++) {
System.out.println("这是run方法"+i);
}
}
public static void main(String[] args) {
//main主线程,主线程
//创建一个线程对象
Demo1 t1 = new Demo1();
t1.start();
//调用start开启线程
for (int i = 0; i <500 ; i++) {
System.out.println("这是main方法"+i);
}
}
}
运行结果:
如果将t1.start()改成 t1.run(),则不是多线程,必须调用start()才会开启线程
注意:线程开启不一定立即执行,由cpu统一调度
2.方式二 实现Runnable
定义一个类,实现Runnable接口,
实现run()方法,编写线程执行体
创建线程对象,调用start()方法启动线程
/**
* 创建线程方式2,重写run方法,执行线程需要丢入Runnable接口实现类,调用start
*/
public class Demo2 implements Runnable{
@Override
public void run(){
//run方法线程体
for (int i = 0; i <500 ; i++) {
System.out.println("这是run方法"+i);
}
}
public static void main(String[] args) {
//创建一个unnable接口实现类对象
Demo2 t2 = new Demo2();
//创建线程对象,通过线程对象来开启我们的线程,代理
Thread thread = new Thread(t2);
thread.start();
// new Thread(t2).start();
for (int i = 0; i <500 ; i++) {
System.out.println("这是main方法"+i);
}
}
}
方式一与方式二对比:
3.方式三 实现Callable接口
1.实现Callable接口,需要返回值类型
2.重写call方法,需要抛出异常
3.创建目标对象
4.创建执行服务: ExecutorService ser = Executors.newFixedThreadPool(1);
5.提交执行: Future result1 = ser.submit(t1);
6.获取结果: boolean r1 = result1.get()
7.关闭服务: ser.shutdownNow();
import java.util.concurrent.*;
public class Demo5 implements Callable {
@Override
public Boolean call(){
//重写call方法
for (int i = 0; i <100 ; i++) {
System.out.println("这是call方法"+i);
}
return true;
}
public static void main(String[] args) {
//创建目标对象
Demo5 t1 = new Demo5();
//创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(1);
//提交执行: 开启线程
Future<Boolean> result1 = ser.submit(t1);
for (int i = 0; i <100 ; i++) {
System.out.println("这是main方法"+i);
}
//获取结果:
try {
boolean r1 = result1.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
//关闭服务: 必须关闭服务程序才结束
ser.shutdownNow();
}
}
小结:
继承Thread类
◆子类继承Thread类具备多线程能力
◆启动线程:子类对象. start()
◆不建议使用:避免OOP单继承局限性
实现Runnable接口
◆实现接口Runnable具有多线程能力
◆启动线程:传入目标对象+ Thread对象.start()
◆推荐使用:避免单继承局限性,灵活方便,方便
多个线程操作同一对象
模拟购买车票例子
**
* 多个线程操作同一对象
* 模拟购买车票例子
*/
public class Demo3 implements Runnable{
//票数
private int ticketNums = 10;
@Override
public void run() {
while (true){
if(ticketNums <= 0){
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--->拿到了第"+ticketNums-- +" 张票");
}
}
public static void main(String[] args) {
Demo3 ticket = new Demo3();
new Thread(ticket,"小明").start();
new Thread(ticket,"黄牛").start();
new Thread(ticket,"小红").start();
}
}
存在问题:当多个线程操作同一个资源的情况下,线程不安全,数据紊乱
多线程例子:模拟龟兔赛跑:
//模拟龟兔赛跑
public class Demo4 implements Runnable{
//胜利者
private static String winner;
@Override
public void run(){
//run方法线程体
for (int i = 0; i <=1000 ; i++) {
//模拟兔子休息
if (Thread.currentThread().getName().equals("兔子") && i%10 ==0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否完成,如果比赛结束,就停止线程
if(gameover(i)){
break;
}
System.out.println(Thread.currentThread().getName()+"--->跑了"+i+" 步");
}
}
//判断比赛是否完成
public boolean gameover(int steps){
if(winner!=null){
return true;
}
if (steps == 1000) {
winner = Thread.currentThread().getName();
System.out.println("winner is " + winner);
return true;
}
return false;
}
public static void main(String[] args) {
Demo4 race = new Demo4();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
3.静态代理模式
public class StaticProfxy {
public static void main(String[