线程
程序:完成特定任务,用某种语言编写的一段静态代码
进程:正在运行的程序,进程作为资源分配的单位,系统在运行时为每个进程分配了不同的内存域
线程:程序内部的一条执行路径,线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器
并行:多个CPU执行不同的任务,例如多个人执行不同的任务
并发:一个CPU(采用时间片的方式)同时执行多个不同的任务,多个人做同一件事,比如一千个人秒杀一台手机。
多线程的创建方式(四种)
①继承Thread类
1.创建一个继承于Thread类的子类
2.在子类中重写Thread的run方法,该方法里面写线程执行的操作
3.创建该子类的对象
4.用该子类对象调用start()方法,该方法是父类中的方法
public class Thread1Test {
public static void main(String[] args) {
/**
* 一个已经执行start的线程不能用同一个线程启动第二次,需要新创建一个线程来启动
* */
MyThread1 myThread1=new MyThread1();
MyThread1 myThread2=new MyThread1();
myThread1.start();
myThread2.start();
System.out.println("hello");
}
}
class MyThread1 extends Thread{
/**
* 线程逻辑业务
* */
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
if(i%2==0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
Thread里面的方法
①run():线程子类重写Thread的方法,该方法里面放线程里面需要执行的逻辑业务
②start():线程子类对象启动线程的方法
③currentThread():静态方法,获取当前线程
④getName():获取当前线程的名字
⑤setName():设置当前线程的名字
⑥yield():释放当前cpu执行该线程的执行权
⑦join():在线程a中调用线程b的方法,此时线程a进入了阻塞状态,只有等线程b执行完后,线程a才 阻塞。
⑧sleep():让进程休眠一段时间,进入阻塞状态
public class ThreadMethodTest {
public static void main(String[] args) {
MyThread12 myt1=new MyThread12();
MyThread13 myt2=new MyThread13();
myt1.setName("线程一");
myt2.setName("线程二");
myt1.start();
myt2.start();
for (int i = 0; i <50 ; i++) {
/**
* 调用join方法
* */
if(i==20){
try {
myt1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(i==30){
try {
myt2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("主线程*********"+i);
}
}
}
class MyThread12 extends Thread{
@Override
public void run() {
for (int i = 0; i <100 ; i++) {
/**
* 调用sleep方法,让进程进入阻塞状态
* */
if(i==50){
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(i%2!=0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
class MyThread13 extends Thread{
@Override
public void run() {
for (int i = 0; i <50 ; i++) {
if(i%2==0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
线程调度
调度策略
①时间片
②抢占式:高优先级的线程抢占CPU
线程优先级等级
MAX_PRIORITY:10
NORM_PRIORITY:5
MIN_PRIORITY:1
线程优先级的获取和设置
getPriority():Thread.currentThread.getPriority()
setPriority()
②实现Runnable接口
1.创建一个实现runnable接口的类
2.重写接口中的run()方法
3.创建线程子类的对象
4.将该对象以参数的形式传入Thread构造器当中,并创建Thread对象
5.用Thread对象调用start()方法
public class Thread2Test {
public static void main(String[] args) {
//第三步:创建线程子类对象
WindowSail win1=new WindowSail();
//第四步:创建Thread对象,并将线程子类对象以参数形式传入Thread构造器中
Thread th1 = new Thread(win1);
Thread th2 = new Thread(win1);
th1.setName("窗口1");
th2.setName("窗口2");
th1.setPriority(Thread.MAX_PRIORITY);
//第五步:调用start方法
th1.start();
th2.start();
}
}
//第一步:实现runnable接口
class WindowSail implements Runnable{
private int ticket=100;
//第二步:重写run方法
@Override
public void run() {
while (true){
if(ticket>0){
System.out.println(Thread.currentThread().getName() + "取票:" + ticket);
ticket--;
}else {
break;
}
}
}
}
Thread.class
//run()方法
@Override
public void run() {
if (target != null) {
target.run();
}
}
//Thread中的构造方法
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
比较创建线程的两种方式
开发中:优先选择→实现Runnable接口的方式
原因:①、实现方式没有类的单继承的局限性
②、实现的方式更适合来处理多个线程有共享数据的情况
联系:都用到了Thread类,并且Thread实现了Runnable接口
两种方式都需要重写run()方法,将线程要执行的逻辑声明在run()中
③实现Callable
1)相比Runnable于run方法,该重写方法call()有返回值
2)可以抛异常,被外面的操作捕获
3)可以支持泛型
public class CallableThreadTest {
public static void main(String[] args) {
//3.创建Callable实现类的对象
CallableWindow calw=new CallableWindow();
//4.创建FutureTask对象,并将实现类对象以参数形式传入FutureTask的构造器中
FutureTask futureTask1 = new FutureTask(calw);
FutureTask futureTask2 = new FutureTask(calw);
//5.将FutureTask对象以参数形式传入Thread构造器中,并调用Start方法
new Thread(futureTask1).start();
new Thread(futureTask2).start();
try {
//get获得的返回值就是FutureTask构造器参数Callable实现类重写方法call的返回值
Object o = futureTask1.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
//1.创建实现Callable接口的实现类
class CallableWindow implements Callable{
private int ticket=100;
//2.重写call方法
@Override
public Object call() throws Exception {
while (true) {
synchronized(this){
if(ticket>0){
System.out.println(Thread.currentThread().getName() + "正在出票" + ticket);
ticket--;
}else {
break;
}
}
}
return null;
}
}
④创建线程池方式
public class ThreadPoolTest {
public static void main(String[] args) {
//1. 提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
//设置线程池的属性
// System.out.println(service.getClass());
// service1.setCorePoolSize(15);
// service1.setKeepAliveTime();
//2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
service.execute(new NumberThread());//适合适用于Runnable
service.execute(new NumberThread1());//适合适用于Runnable
// service.submit(Callable callable);//适合使用于Callable
//3.关闭连接池
service.shutdown();
}
}
线程生命周期
解決线程安全的方法→同步机制
方式一:同步代码块
synchronzied(同步监视器){
//需要被同步的代码
}
说明:1。操作共享数据的代码,即为需要被同步的代码
2.共享数据:多个线程共同操作的变量。比如抢票中的车票就是共享数据
3.同步监视器,俗称锁,任何一个类的对象,都可以充当锁。
要求:多个线程必须要使用同一把锁,也就是用作锁的实例对象只能创建一次
public class ThreadSynchronized {
public static void main(String[] args) {
WindowSynchronized winS11=new WindowSynchronized();
winS11.start();
WindowSynchronized winS12=new WindowSynchronized();
winS12.start();
//*****
WindowSynchronized winS2=new WindowSynchronized();
Thread thread1=new Thread(winS2);
Thread thread2=new Thread(winS2);
thread1.start();
thread2.start();
}
}
class WindowSynchronized extends Thread{
private static int ticket=100;
/**
* 这里由于继承Thread类方式如果多线程调用的话是创建多个线程子类对象,那么object就不唯一了
* 而锁是需要唯一,因此需要用static,这个锁可以是任何一个对象,并不一定要object比如可以创
* 建一个Dog对象
* */
private static Object object=new Object();
@Override
public void run() {
while (true){
/**
* 这里除了可以用object这个锁之外,还可以用WindowSynchronized.class,这里用到了
* 反射知识,Class clazz=WindowSynchronized.class,这也是个对象,即类本身也是对
* 象
* synchronized(WindowSynchronized.class){}
* */
synchronized (object){
if(ticket>0){
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + ticket);
ticket--;
}else {
break;
}
}
}
}
}
/**
* 实现Runnable接口方式
* */
class WindowSynchronized2 implements Runnable{
private int ticket=100;
/**
* 这里不用static是由于在调用时候只创建了WindowSynchronized2一次对象,所以对象锁是唯一的
* */
private Object object=new Object();
@Override
public void run() {
while (true){
/**
* 这里也可以用object方式
* */
synchronized (this){
if(ticket>0){
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + ticket);
ticket--;
}else {
break;
}
}
}
}
}
方式二:同步方法
1.同步方法仍然需要用到同步监视器,知识不需要我们显示的声明
2.非静态的同步方法,同步监视器是this
静态的同步方法,同步监视器是当前类本身
public class SynchronizedMethod {
public static void main(String[] args) {
WindowRunnable winr=new WindowRunnable();
Thread th1=new Thread(winr);
Thread th2=new Thread(winr);
th1.start();
th2.start();
//Thread方法
WindowThread wint=new WindowThread();
WindowThread wint1=new WindowThread();
wint.start();
wint1.start();
}
}
/**
* 实现Runnable方式的同步方法
* */
class WindowRunnable implements Runnable{
private int ticket=100;
@Override
public void run() {
while (true){
sail();
}
}
/**
* 这里的同步监视器(锁)就是this,当前对象
* */
private synchronized void sail(){
if(ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + ticket);
ticket--;
}
}
}
/**
* 继承Thread类的方式
* */
class WindowThread extends Thread{
private static int ticket=100;
@Override
public void run() {
while (true){
sail();
}
}
/**
* 这里的同步监视器是WindowThread.class类对象,而且这里一定要有static
* */
private static synchronized void sail(){
if(ticket>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + ticket);
ticket--;
}
}
}
方式三:Lock锁
class WindowLock implements Runnable{
private int ticket=100;
private ReentrantLock lock=new ReentrantLock();
//第二步:重写run方法
@Override
public void run() {
while (true){
/**
* 这个lock一定是紧跟try前面,并且调用lock方法之后一定要调用unlock方法,并且
* 这个unlock方法一定是在finally第一行
* */
lock.lock();
try {
if(ticket>0){
if(ticket==20){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "取票:" + ticket);
ticket--;
}else {
break;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
同步机制解决单例模式(懒汉式)线程安全
class Bank{
private static Bank instance=null;
/**
* 同步代码块方式
* */
public static Bank getInstance(){
if(instance==null){
synchronized (Bank.class){
if(instance==null){
instance=new Bank();
}
}
}
return instance;
}
/**
* 同步方法方式
* */
public static synchronized Bank getInstance1(){
if(instance==null){
instance=new Bank();
}
return instance;
}
}
线程通信间使用到的两个方法
1.wait()
一旦调用了wait方法,那么当前线程就会进入阻塞状态,并且释放同步监视器(锁)
调用notify()方法会唤醒被wait的线程,如果多个线程被wait(),那么会唤醒优先级高的那个
调用notifyAll()方法会唤醒所有被wait的方法
2.sleep()
一旦执行此方法,线程会进入阻塞状态
wait()和slee()方法的异同
同:这两个方法都可以使当前线程进入阻塞状态
不同:1)两个方法的位置不同,wait()是在java.lang.Object中,而sleep()是在Thread中。
2)调用的要求不同,sleep可以在任何场景下被调用,但是wait()只能在同步代码或者同步方法
中被调用
3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep不会释放 同步监视器,而wait()一旦被调用就会释放当前线程的所拥有的锁,被其它线程所获得,其它 线程调用notify后会将wait的线程所唤醒,结束阻塞状态。
生产者消费者模型
public class ProductConsumerTest {
public static void main(String[] args) {
Product product=new Product();
Producer producer=new Producer(product);
Consumer consumer=new Consumer(product);
Thread thpro=new Thread(producer);
Thread thcon=new Thread(consumer);
thpro.setName("生产者");
thcon.setName("消费者");
thpro.start();
thcon.start();
}
}
class Product{
private int productor=0;
public synchronized void productShow() {
if(productor<20){
productor++;
System.out.println(Thread.currentThread().getName() + "在生产第" + productor + "个产品");
//唤醒消费者,说明可以开始消费了
notify();
}else {
/**
* 这里是当生产者生产的产品超过了20个,那么就要进入阻塞状态,不能继续生产了,只有等着
* 消费者消费一个之后那么就还可以生产,由于此时生产者进入了阻塞状态,那么只有靠消费者
* 调用notify来唤醒生产者,反过来,生产者唤醒消费者同样原理
* */
try {
wait(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void consumerShow() {
if(productor>0){
System.out.println(Thread.currentThread().getName() + "正在消费第" + productor + "个产品");
productor--;
notify();
}else {
try {
wait(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer implements Runnable{
Product product;
public Producer(Product product) {
this.product = product;
}
@Override
public void run() {
System.out.println("生产者开始生产产品了");
while (true) {
product.productShow();
}
}
}
class Consumer implements Runnable{
Product product;
public Consumer(Product product) {
this.product = product;
}
@Override
public void run() {
System.out.println("消费者开始消费产品了");
while (true){
product.consumerShow();
}
}
}
死锁问题
多线程间都在等待对方的和自己的同步资源,它不会报异常,只是所有线程都处于阻塞状态。
解决方法
1.专门的算法、原则
2.尽量减少同步资源的定义
3.尽量避免嵌套同步