Java学习笔记(尚硅谷,多线程)

程序&进程&线程

  • 程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。

  • 进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期
    如:运行中的QQ,运行中的MP3播放器
    程序是静态的,进程是动态的
    进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域

  • 线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。
    若一个进程同一时间并行执行多个线程,就是支持多线程的,线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
    一个进程中的多个线程共享相同的内存单元/内存地址空间
    它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资源可能就会带来安全的隐患。

多线程创建的两种方法

一、创建Thread类的子类对象

  1. 创建一个继承于 Thread 类的子类
  2. 重写 Thread 类的 run()
  3. 创建 Thread 类的子类的对象
  4. 通过此对象调用 start()

注意:
直接调用 run() 方法不是多线程
一个已经 start() 的线程不能再启动一个 start()

public class OtherTest {
    public static void main(String[] args) {
        MyThread myThread = new MyThread("分线程");
        myThread.start();

        Thread.currentThread().setName("主线程");
        for (int i = 0; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":\t" + Thread.currentThread().getPriority() + ":\t" + i);
            }
        }
    }
}

class MyThread extends Thread {
    public MyThread() {
    }

    public MyThread(String name) {
        super(name);
    }

    public void run() {
        for (int i = 0; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ":\t" + Thread.currentThread().getPriority() + ":\t" + i);
            }
        }
    }
}

二、实现Runnable接口

  1. 创建一个实现了Runnable接口的类
  2. 实现类去实现Runnable的抽象方法:run()
  3. 创建实现类的对象
  4. 将实现类的对象作为参数传递到Thread类的构造器中,创建Thread类的对象
  5. 通过Thread类的对象调用start():①启动线程②调用当前线程的run()–>调用的runnable类型的target的run()
public class OtherTest {
    public static void main(String[] args) {
        Windows windows = new Windows();
        Thread t1 = new Thread(windows);
        Thread t2 = new Thread(windows);
        Thread t3 = new Thread(windows);

        t1.setName("售票处1:");
        t2.setName("售票处2:");
        t3.setName("售票处3:");

        t1.start();
        t2.start();
        t3.start();
    }
}

class Windows implements Runnable {
    // 不需要使用static修饰,因为t1,t2,t3都是用该类的同一对象创建的
    private int ticket = 100;

    public void run() {
        while (true) {
            if (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + "卖出号码为" + ticket + "的票");
                ticket--;
            } else {
                break;
            }
        }
    }
}

Thread类的常用方法

 1. start()//启动当前线程,调用当前线程的run()
 2. run()//通常需要重写Thread类的此方法,将创建的线程要执行的操作声明在此方法中
 3. currentThread()//静态方法,返回执行当前代码的线程
 4. getName()//获取当前线程的名称
 5. setName()//设置当前线程的名称
 6. Thread.currentThread().setName("主线程");//设置主线程的名称
 7. yield()//释放当前CPU的执行权,但可能CPU又会将执行权分配给当前线程
 8. join()//在线程a中调用线程b的join(),此时线程a就进入阻塞状态,只有等线程b完全执行完以后才执行线程a
 9. sleep()//让当前线程“睡眠”指定的millitime毫秒,在指定的millitime毫秒时间内,当前线程是阻塞状态
 10.isalive()//判断当前线程是否存活 

线程的优先级

MAX_PRIORITY:10 //最高的优先级
MIN _PRIORITY:1 //最低的优先级
NORM_PRIORITY:5 //一般情况下的默认优先级
getPriority() :返回线程优先值
setPriority(int newPriority) :改变线程的优先级

线程的同步

1、同步代码块

synchronized (同步监视器(锁)) {
	// 需要被同步的代码(操作共享数据的代码)
}
// 任何对象都可以充当锁(要求:所有线程都共用同一把锁)
public class OtherTest {
    public static void main(String[] args) {
        Windows windows = new Windows();
        Thread t1 = new Thread(windows);
        Thread t2 = new Thread(windows);
        Thread t3 = new Thread(windows);

        t1.setName("售票处1:");
        t2.setName("售票处2:");
        t3.setName("售票处3:");

        t1.start();
        t2.start();
        t3.start();
    }
}

class Windows implements Runnable {
    // 不需要使用static修饰,因为t1,t2,t3都是用该类的同一对象创建的
    private int ticket = 100;
    Object o = new Object();

    @Override
    public void run() {
        while (true) {
        	// 继承方式不能使用synchronized (this)方式同步,因为他们的this不是同一个对象
        	// 若要继承方式想要使用同步代码块,则可以将同步监视器声明为static
            synchronized (o) {	//synchronized (this) // synchronized (Windows.class)
                if (ticket > 0) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "卖出号码为" + ticket + "的票");
                    ticket--;
                } else {
                    break;
                }
            }
        }
    }
}

2、同步方法

private synchronized void method(){}	// 同步监视器:this
// 继承方式,此时的同步监视器:类.class
private static synchronized void method(){}
  1. 同步方法仍然涉及到同步监视器,只是不需要显式的声明
  2. 非静态的同步方法,同步监视器是:this
  3. 静态的同步方法,同步监视器是:类.class

3、解决“懒汉式”线程不安全的问题

class Bank {
    private Bank() {}

    private static Bank bank = null;

    public static Bank getBank() {
        // 这种方式效率较高
        if (bank == null) {
            synchronized (Bank.class) {
                if (bank == null) {
                    bank = new Bank();
                }
            }
        }
        return bank;
    }
}

死锁

理解:不同的线程分别占用对方所需要的同步资源不放弃,都在等待对方放弃自己所需要的同步资源,就形成了线程的死锁

说明:

  1. 出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
  2. 我们使用同步时,要避免死锁的出现
StringBuffer s1 = new StringBuffer();
StringBuffer s2 = new StringBuffer();
new Thread(new Runnable() {
   @Override
   public void run() {
       synchronized (s1) {
           s1.append('a');
           s2.append(1);
           try {
               Thread.sleep(100);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           synchronized (s2) {
               s1.append('b');
               s2.append(2);
               System.out.println(s1);
               System.out.println(s2);
           }
       }
   }
}).start();
new Thread(new Runnable() {
   @Override
   public void run() {
       synchronized (s2) {
           s1.append('c');
           s2.append(3);
           try {
               Thread.sleep(100);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           synchronized (s1) {
               s1.append('d');
               s2.append(4);
               System.out.println(s1);
               System.out.println(s2);
           }
       }
   }
}).start();

3、Lock锁

Lock是一个接口,ReentrantLock是它的一个实现类

private ReentrantLock lock = new ReentrantLock();	// 实例化ReentrantLock

@Override
public void run() {
	try {
		lock.lock();	// 调用锁定方法lock()
		// 需要同步的代码
	} finally {
		lock.unlock();	// 调用解锁方法unlock()
	}
}

使用优先顺序:Lock、同步代码块、同步方法
同步的缺点:操作同步代码时,只能一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。

线程的通信

wait();//让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁。“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)
notify();//唤醒当前对象上的等待线程
notifyAll();//唤醒所有线程
  1. 以上三个方法都只能使用在同步代码块或同步方法中
  2. 以上三个方法调用者必须是同步代码块或同步方法的同步监视器
  3. 以上三个方法都定义在java.lang.Object类中
class Windows implements Runnable {
    private int num = 1;
	private Object obj = new Object();
	
    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                obj.notify();//唤醒线程
                if (num <= 100) {
                    try {
                        Thread.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":" + num);
                    num++;
                    try {
                        obj.wait();//让当前线程处于阻塞状态,释放同步锁
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                else {
                    break;
                }
            }
        }
    }
}

JDK5.0新增的多线程创建的两种方法

1. 实现Callable接口

1.创建一个实现Callable的实现类
2.实现call(),将此线程需要执行的操作声明在call()3.创建Callable接口实现类的对象
4.将此Callable接口实现类的对象作为参数传递到FutureTask类的构造器中,创建FutureTask类的对象
5.FutureTask类的对象作为参数传递到Thread类的构造器中,创建Thread类对象,并调用start()
6.获取Callablecall()的返回值(可选)
public class ThreadNew {
    public static void main(String[] args) {
    	// 3.创建Callable接口实现类的对象
        NumThread numThread = new NumThread();
		// 4.将此Callable接口实现类的对象作为参数传递到FutureTask构造器中,创建FutureTask的对象
        FutureTask futureTask = new FutureTask(numThread);
        // 5.将FutureTask类的对象作为参数传递到Thread类的构造器中,创建Thread类对象,并调用start()
        new Thread(futureTask).start();
		// 6.获取Callable中call()的返回值(可选)
        try {
            // get()返回值即为FutureTask构造器参数Callable实现类重写的call()返回值
            Object sum = futureTask.get();
            System.out.println(sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
// 1.创建一个实现Callable的实现类
class NumThread implements Callable {
    @Override
    // 2.实现call(),将此线程需要执行的操作声明在call()中
    public Object call() throws Exception {	// 求100以内偶数和
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(i);
                sum += i;
            }
        }
        return sum;// 自动转换为包装类
    }
}

2. 使用线程池

在这里插入图片描述

public class ThreadPool {
    public static void main(String[] args) {
        // 1、提供指定线程数量的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        // 设置线程池的属性
        ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
        service1.setCorePoolSize(15);	// 设置核心池的大小

        // 2、执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
        service.execute(new NumberThread());    // 适合用于Runnable
        service.execute(new NumberThread1());   // 适合用于Runnable
        // service.submit();    // 适合用于Callable

        // 关闭连接池
        service.shutdown();
    }
}

class NumberThread implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            if (i % 2 == 0) {
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}

class NumberThread1 implements Runnable {
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            if (i % 2 != 0) {
                System.out.println(Thread.currentThread().getName() + ": " + i);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值