1.基本概念:程序、进程、线程
程序:为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
进程:程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程——生命周期
线程:是一个程序内部的一条执行途径。
并行与并发:
并行:多个CPU同时执行多个任务
发:一个CPU(采用时间片)同时执行多个任务
2.线程的创建和使用
多线程的创建,方式一:继承于Thread类
-
1.创建一个继承于Thread类的子类
-
2.重写Thread类的run()–>将此线程执行的操作声明在run()中
-
3.创建Thread类的子类的对象
-
4.通过此对象调用start()
//1.创建一个继承于Thread类的子类 class MyThread extends Thread{ //2.重写Thread类的run() @Override public void run() { for (int i = 0; i < 100; i++) { if(i%2==0){ System.out.println(Thread.currentThread().getName()+" "+i); } } } } public class ThreadTest { public static void main(String[] args) { //3.创建Thread类的子类的对象 MyThread t1 = new MyThread(); //4.通过此对象调用start():①启动当前线程 ②调用当前线程的run()-->调用了Runnable类型的的target t1.start(); //问题1:我们不能通过直接调用run()的方式启动线程 // t1.run(); //问题2:再启动一个线程,遍历100以内的偶数,不可以start()多次 // t1.start(); for (int i = 0; i < 100; i++) { if(i%2==0){ System.out.println(Thread.currentThread().getName()+" "+i+"**"); } } } }
创建多线程的方式二:实现Runnable接口
-
1.创建一个实现了Runnable接口的类
-
2.实现类去实现Runnable中的抽象方法:run()
-
3.创建实现类的对象
-
4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
-
5.通过Thread类的对象调用start()
// 1.创建一个实现了Runnable接口的类 class MThread implements Runnable{ //2.实现类去实现Runnable中的抽象方法:run() @Override public void run() { for (int i = 0; i < 100; i++) { if(i%2==0){ System.out.println(Thread.currentThread().getName()+":"+i); } } } } public class ThreadTest1 { public static void main(String[] args) { //3.创建实现类的对象 MThread m1 = new MThread(); //4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象 Thread t1 = new Thread(m1); //5.通过Thread类的对象调用start() t1.start(); //再启动一个线程,遍历100以内的偶数 Thread t2 = new Thread(m1); t2.start(); } }
比较创建线程的两种方式:
-
开发中:优先选择Runnable接口的方式
-
1.实现的方式没有类的单继承性的局限
-
2.实现的方式更适合来处理多个线程有共享数据的情况
-
联系:两种方法都需要重写run(),将线程要执行的逻辑声明在run()中。
测试Thread中的常用方法:
-
1.start():启动当前线程、调用当前线程的run()
-
2.run():通常需要重写thread类中的此方法,将创建的线程对象执行的操作声明在此方法中
-
3.currentThread():静态方法,返回执行当前代码的线程
-
4.getName():获取当前进程的名字
-
5.setName():设置当前进程的名字
-
6.yield():释放当前cpu的执行权
-
7.join():在线程a中调用线程b的join(),此时线程a进入阻塞状态,知道线程b完全执行完以后,线程a才会不阻塞
-
8.sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒
线程的优先级:
-
1.MAX_PRIORITY 10
-
MIN_PRIORITY 1
-
NORM_PRIORITY 5 -->默认优先级
-
2.如何获取和设置当前线程的优先级:
-
getPriority():获取线程的优先级
-
setPriority():设置线程的优先级
-
说明:高优先级的线程要抢占低优先级线程cpu的执行权,但是只是从概率上来讲,
-
并不意味着高优先级的程序执行完以后、底优先级的才会进行
线程通信:wait()/notify()/notifyAll():此三个方法定义在object类中
class HelloThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i%2==0){
// try {
// sleep(10);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
System.out.println(Thread.currentThread().getName()+":"+
Thread.currentThread().getPriority()+":"+i);
}
}
}
public HelloThread(String name){
super(name);
}
}
public class ThreadMethodTest {
public static void main(String[] args) {
HelloThread h1 = new HelloThread("Thread 1");
h1.setPriority(Thread.MAX_PRIORITY);
h1.start();
Thread.currentThread().setName("主线程");
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
for (int i = 0; i < 100; i++) {
if(i%2==0){
System.out.println(Thread.currentThread().getName()+":"+
Thread.currentThread().getPriority()+":"+i);
}
// if(i==20){
// try {
// h1.join();
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
// }
}
// System.out.println(h1.isAlive());
}
}
3.线程的生命周期
说明:
1.生命周期关注两个概念:状态、相应的方法
2.关注:状态a——>状态b:那些方法执行(回调方法)
4.线程的同步
背景:
1.问题:买票过程中,出现类重票、错票——>出现了线程的安全问题
2.出现原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票
3.解决方法:当一个线程a在操作ticket的时候,其他线程不能参与进来。直到线程a操作完ticket时,其他线程才可以开始操作ticket
2.Java解决方案:同步机制
在Java中,我们通过同步机制,来解决线程的安全问题
方式一:同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
- 说明:1.操作共享数据的代码,即为需要被同步的代码
- 2.共享数据:多个线程共同操作的变量
- 3.同步监视器,俗称:锁。任何一个类的对象都可以当锁
- 要求:多个线程必须要共用一把锁
- 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器
- 在继承Thread类创建多线程的方式中,慎用this来充当同步监视器,可以考虑使用当前类
方式二:同步方法
关于同步方法的总结:
- 1.同步方法仍然涉及到同步监视器,只是不需要我们显示的声明
- 2.非静态的同步方法,同步监视器是this
- 静态的同步方法,同步监视器是当前类本身
方式三:Lock锁:
synchronized和lock的异同
- 不同点:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
- lock需要手动的启动同步lock(),结束同步也需要手动实现unlock()
使用的优先顺序:312
利弊:
同步的方式,解决了线程的安全问题–好处
操作同步代码时,只能有一个线程参与,其他线程等待,相当于是一个单线程的过程,效率低
使用同步机制将单例模式中的懒汉式改写为线程安全的
class Bank{
private Bank(){}
private static Bank instance=null;
public static synchronized Bank getInstance(){//同步监视器Bank.class
// synchronized (Bank.class) {
// if(instance==null){
// instance=new Bank();
// }
// return instance;
// }
//判断来节省时间
if(instance==null){
synchronized (Bank.class){
if(instance==null){
instance=new Bank();
}
}
}
return instance;
}
}
5.线程的通信
1.线程通信涉及到的三个方法
- 1.wait():当前线程就进入阻塞状态,并释放同步监视器。
- 2,notify():唤醒被wait的一个线程(优先级高的先唤醒)
- 3.notifyALl():唤醒全部wait()线程
- 说明:
- 1.wait()、notify()、notifyAll()三个方法必须使用在同步代码块或同步方法中
- 2.wait()、notify()、notifyAll()三个方法的调用这必须是在同步代码块或同步方法中同步监视器
- 3.wait()、notify()、notifyAll()定义在Java.lang.Object类中
2.小结释放锁的操作
3.小结不会释放锁的操作
6.JDK5.0新增线程创建方式
方式一:
创建线程的方式三:实现Callable接口。–>JDK5.0新增
//1.创建一个实现Callable的实现类
class NumThread implements Callable{
//2.实现call方法,将此线程需要执行的操作声明在call()中
@Override
public Object call() throws Exception {
int sum=0;
for (int i = 0; i <=100; i++) {
if(i%2==0){
// System.out.println(i);
sum+=i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
//3.创建Callable接口实现类的对象
NumThread n1=new NumThread();
//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask对象
FutureTask futureTask = new FutureTask(n1);
//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()
new Thread(futureTask).start();
try {
//6.获取Callable中call方法的返回值
//get()返回值即为FutureTask构造器参数Callable实现类的重写的call()的返回值
Object sum=futureTask.get();
System.out.println(sum);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
}
说明:
- call()可以有返回值的
- call()可以抛出异常,被外面的操作捕获,获取异常的信息
- Callable时支持泛型的
方式二:
使用线程池
class NumberThread implements Runnable{
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if(i%2==0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
//1.提供指定线程数量的线程池
ExecutorService service= Executors.newFixedThreadPool(10);
//设置线程池的属性
System.out.println(service.getClass());
//2.执行指定的线程操作,提供一个实现Runnable或Callable接口实现类的对象
service.execute(new NumberThread());//Runnable
service.submit(new NumberThread());//Callable
service.shutdown();
}
}
说明:
1.提高响应速度(减少了创建新线程的时间)
2.降低资源消耗(重复利用线程池中线程,不需要每次都创建)
3.便于线程管理
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没任务时最多保持多长时间后会终止