线程概述:
线程是程序运行的基本执行单元。
一个进程可以有一个或多个线程。进程与进程之间不共享内存,而一个进程中的线程可以共享系统派给这个进程的内存空间。进程是系统分配资源的单位,线程是CPU调度和执行的单位。
核心概念:
1:任何一个Java程序都必须有一个主线程。一般这个主线程的名字为main()。只有在程序中建立另外的线程才能算是真正的多线程程序。也就是说一个程序必须拥有一个以上的线程。
2:线程会带来额外的开销,如CPU调度时间,并发控制开销。
3:对同一份资源进行操作时,会存在资源抢夺的问题,需要加入并发控制。
4:每个线程在自己的工作内存交互,内存控制不当会造成数据不统一。
线程的意义:
1:充分利用CPU资源 。
2:简化编程模型 。
3:简化异步事件处理。
4:使GUI更有效率 。
5:提高程序的执行效率。
建立线程的三种方法:
1:继承Thread类
Thread类的八个构造方法:
public Thread();
public Thread(Runnable target);
public Thread(String name);
public Thread(Runnable target,String name);
public Thread(ThreadGroup group,Runnable target);
public Thread(ThreadGroup group,String name);
public Thread(ThreadGroup group,Runnable target,String name);
public Thread(ThreadGroup group,Runnable target,String name,long stackSize);
/*Runnable target:实现了Runnable接口的实例。
String name:线程的名字,此名字可以建立Thread实例后通过Thread类的setName方法设置。如果不设置线程名,就会使用默认线程名:Thread-N,N是线程建立的顺序,是一个不重复整数。
ThreadGroup group:当前建立的线程的线程组。如果不建立将会加到一个默认的线程组。
long stackSize:线程栈的大小,这个值一般是CPU页面的整数倍
*/
Thread类里常用的几个方法:
Thread.currentThread();//方法:是Thread类的静态方法,返回当前正在执行的线程对象
Thread.setName();//方法:为当前Thread对象设置名字
Thread.getName();//方法:得到当前Thread对象的名字
Thread.sleep(target);//方法:使当前线程睡眠target毫秒
线程执行函数:Thread里的run()方法。
建立线程函数:Thread里的start()方法。相当于调用Windows的建立线程函数CreateThread()。当start()方法调用成功(进入就绪状态),会自动调用run()方法。
示例代码:
public class TestThread extends Thread{
public static void main(String[] args) {
//创建一个线程对象
TestThread testThread=new TestThread();
//待用start()方法开启线程
testThread.start();//线程开启不一定立即执行,而等待cpu调度
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("wzkdm");
}
}
}
2:实现Runnable接口
Runnable接口:标识一个Java类是否可作为线程类,此接口只有一个抽象方法run()。所以一个线程类的唯一标识就是这个类是否实现了Runnable接口的run()方法。
一般使用实现Runnable接口的方法,因为可以实现多个接口,而继承只能单继承。空出继承位可以留给更需要继承的类而不是Thread类。
使用Runnable接口创建线程步骤:
1:将实现Runnable接口实例化。
2:建立一个Thread对象,并将第一步实例化后的对象作为参数传入Thread类的构造方法,最后通过Thread类的start()方法建立。
public class TestThread2 implements Runnable{
public static void main(String[] args) {
//创建一个线程对象
TestThread2 testThread=new TestThread2();
//通过线程对象来开启线程,代理
new Thread(testThread).start();//线程开启不一定立即执行,而等待cpu调度
}
@Override
public void run() {
System.out.println("song");
}
}
通过两个案例来进一步了解多线程:
1:抢票案例:
主程序:
//问题:多个线程操作同一个资源的情况下,线程不安全,数据会紊乱
public class TestThread3{
public static void main(String[] args) {
BuyTicket ticket = new BuyTicket();
new Thread(ticket, "song1").start();
new Thread(ticket, "song2").start();
new Thread(ticket, "song3").start();
new Thread(ticket, "song4").start();
}
}
买票类:
public class BuyTicket implements Runnable{
private int ticketNums=10;
boolean flag=true;
@Override
public void run() {
while (flag){
buy();
}
}
private void buy(){
if(ticketNums<=0){
flag=false;
return;
}
//模拟延时
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNums--+"张票");
}
}
2:龟兔赛跑案例:
public class Race implements Runnable{
public static void main(String[] args) {
Race race=new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
//静态变量胜利者
private static String winner;
@Override
public void run() {
for (int i = 1; i <=100; i++) {
//模拟兔子休息
if(Thread.currentThread().getName().equals("兔子")&&i%10==0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag=gameOver(i);
//如果比赛结束就停止程序
if (flag){
break;
}
System.out.println(Thread.currentThread().getName()+"--->跑了"+i+"步");
}
}
//判断是否完成比赛
private boolean gameOver(int steps){
//判断是否有胜利者
if(winner!=null){
return true;
}else {
if(steps==100){
winner=Thread.currentThread().getName();
System.out.println("winner is"+winner);
return true;
}
}
return false;
}
}
3:实现Callable接口
Callable接口是可以让线程返回值的,和线程池一起使用,常规创建线程的方法是实现Runnable接口。
//①:需要返回值类型
//②:重写call方法(就不是run方法了),需要抛出异常
//③:创建目标对象
//④:创建执行服务:
ExecutorService ser=Executor.newFixedThreadPool(1);//1指的是线程池大小
//⑤:提交执行:
Future<Boolean> result1=ser.submit(t1);
//⑥:获取结果:
Boolean r1=result1.get();
//⑦:关闭服务:
ser.shutdownNow();
静态代理模式(多线程的设计模式):
静态代理:由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口、真实对象类、代理对象类等确定下来。
1:真实对象和代理对象实现同一个接口。
2:代理对象要代理真实角色。
3:代理对象可以做很多真实对象做不了的事情。
4:真实对象专注于做自己的事情。
通过结婚案例了解多线程的设计模式
主程序:
public class StaticProxy {
public static void main(String[] args) {
//wddingCompany类接收一个you对象,调用HappMarry方法。
new WeddingCompany(new You()).HappMarry();
}
}
同时实现的接口:
public interface Marry{
void HappMarry();
}
真实对象:
//你,主角
public class You implements Marry{
@Override
public void HappMarry(){
System.out.println("song要结婚了,好开心");
}
}
代理对象:
//婚庆公司,代理
public class WeddingCompany implements Marry{
private Marry target;
public WeddingCompany(Marry target){
this.target=target;
}
@Override
public void HappMarry() {
before();
this.target.HappMarry();
after();
}
private void before(){
System.out.println("结婚之前,布置现场");
}
private void after(){
System.out.println("结婚之后,收拾现场");
}
}