多线程
* 主线程:执行主(main)方法的线程,叫做主线程
* 程序执行的入口是main方法
* 程序从main方法开始执行,从上到下依次执行,这个程序就是一个单线程的程序
*
* java程序进入到内存中执行,就是一个进程
* JVM(java虚拟机),会先执行程序的入口main方法
* JVM会找操作系统开辟一条java程序通向cpu的路径
* cpu通过这个路径就可以执行main方法
* 这个路径有一个名字,叫做主线程(main线程)
* 实现多线程的第一种方式:将类声明为 Thread 的子类
*
* 实现步骤:
* 1.创建一个Thread类的子类
* 2.重写Thread类中的run方法,设置线程任务(开启线程要干什么事情)
* 3.创建Thread类的子类对象
* 4.调用Thread类中的方法start方法,开启一个新的线程执行run方法
public class Demo01Thread { public static void main(String[] args) { //3.创建Thread类的子类对象 MyThread mt = new MyThread(); //4.调用Thread类中的方法start方法,开启一个新的线程执行run方法 mt.start(); new MyThread().start(); //mt.run(); //单线程程序 for(int i=0; i<=20; i++){ System.out.println("main:"+i); } } } package cn.itcast.demo02.Thread; /* * 1.创建一个Thread类的子类 */ public class MyThread extends Thread{ //2.重写Thread类中的run方法,设置线程任务(开启线程要干什么事情) @Override public void run() { for(int i=0; i<=20; i++){ System.out.println("run:"+i); //System.out.println(0/0); } } }
* 设置线程的名称
* 1.使用Thread类中的方法setName
* void setName(String name) 改变线程名称,使之与参数 name 相同。
* 2.创建带参数构造方法,参数传递线程的名称;方法中调用父类的带参数构造方法,把名称传递过父类,让父类帮子类起名字
* Thread(String name) 分配新的 Thread 对象。
* 获取线程的名称:
* 1.可以通过Thread类中的方法getName获取
* String getName() 返回该线程的名称。
* 2.通过Thread类中的方法,先获取当前正在执行的线程,在通过线程中的方法getName获取线程名称
* static Thread currentThread() 返回对当前正在执行的线程对象的引用。
* start方法:
* 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
* 结果是两个线程并发地运行;当前线程(执行main方法的线程,主线程)和另一个线程(执行其 run 方法的线程,Thread-0,...)。
* 多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。
* 创建多线程的第二种方式:实现Runnable接口
*
* Thread类中的构造方法:传递的是Runnable接口的实现类对象
* Thread(Runnable target) 分配新的 Thread 对象。
*
* 实现步骤:
* 1.定义Runnable接口的实现类
* 2.重写Runnable接口中的run方法,设置线程任务
* 3.创建Runnable接口的实现类对象
* 4.创建Thread类对象,构造方法中传递Runnable接口的实现类对象
* 5.调用Thread类中的方法start,开启新线程,执行run方法
*
* 实现Runnable接口方式创建多线程的好处:
* 1.避免了单继承的局限性.实现了接口还可以继承其它的类
* 2.把设置线程任务和开启线程进行了解耦,增强程序的扩展性
* 实现Runnable接口,重写run方法:目的就是设置线程任务
* 把线程的运行交个Thread类
public class Demo01Thread { public static void main(String[] args) { //3.创建Runnable接口的实现类对象 RunnableImpl run = new RunnableImpl(); //4.创建Thread类对象,构造方法中传递Runnable接口的实现类对象 //Thread t = new Thread(run); Thread t = new Thread(new RunnableImpl2()); //5.调用Thread类中的方法start,开启新线程,执行run方法 t.start(); for (int i = 1; i <= 20; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } } package cn.itcast.demo05.Runnable; /* * 1.定义Runnable接口的实现类 */ public class RunnableImpl implements Runnable{ //2.重写Runnable接口中的run方法,设置线程任务 @Override public void run() { for (int i = 1; i <= 20; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } } package cn.itcast.demo05.Runnable; /* * 1.定义Runnable接口的实现类 */ public class RunnableImpl2 implements Runnable{ //2.重写Runnable接口中的run方法,设置线程任务 @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("HelloWorld"+i); } } }
* 多线程的匿名内部类的创建方式:
* 匿名:创建的对象没有名字
* 内部类:写在其他类内部的类
*
* 格式:
* new 父类/接口(){
* 重写父类/接口中的方法;
* };
* 匿名内部类的作用:把定义子类,重写父类/接口的方法,创建子类对象合成一步完成
* 匿名内部类的结果:就是一个子类对象
*
* new Thread(){ //重写Thread类中的run方法 @Override public void run() { for (int i = 0; i < 5; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } }
以上代码就是创建线程Thread的子类对象,就相当于 new MyThread();
public class Demo01Thread { public static void main(String[] args) { //父类:Thread new Thread(){ //重写Thread类中的run方法 @Override public void run() { for (int i = 0; i < 50; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } }.start(); System.out.println("-------------------"); //接口:Runnable //多态 接口 匿名实现类对象 Runnable r = new Runnable(){ @Override public void run() { for (int i = 0; i < 50; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } }; System.out.println("-------------------"); new Thread(r).start(); new Thread(new Runnable(){ @Override public void run() { for (int i = 0; i < 50; i++) { System.out.println(Thread.currentThread().getName()+"-->"+i); } } }).start(); } }
* Thread类中的方法sleep
* static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
* 让程序睡觉,睡醒了继续执行
public class Demo02Sleeep { public static void main(String[] args) { //秒表 for (int i = 1; i <=60; i++) { System.out.println(i); //让程序睡眠1秒=1000毫秒 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
* 实现多线程卖票:
* 创建3个线程,同时卖同一百张票
public class Demo01PayTicket { public static void main(String[] args) { //创建Runnable接口的实现类对象 RunnableImpl run = new RunnableImpl(); //创建3个线程,同时卖同一百张票 Thread t0 = new Thread(run); Thread t1 = new Thread(run); Thread t2 = new Thread(run); t0.start(); t1.start(); t2.start(); } } package cn.itcast.demo07.Synchronized; /* * 卖票案例出现了线程安全问题:出现重复的票和不存在的票 * * 解决线程安全问题的第一种方式:使用同步代码块 * 格式: * synchronized(锁对象){ * 可能出现线程安全问题的代码(访问了共享数据的代码) * } * 注意: * 必须要保证多个线程使用的锁对象是同一个锁对象 */ public class RunnableImpl implements Runnable{ //定义一个共享的票源 private int ticket = 100; //创建一个同步代码块的锁对象,可以是任意的对象 Object obj = new Object(); @Override public void run() { while(true){ //添加一个同步代码块 synchronized (obj) { if(ticket>0){ //为了提高出现问题的概率,增加一个睡眠 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //有票进行卖票操作 System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket--+"张票"); } } } } }
或者:
package cn.itcast.demo08.SynchronizedMethod; /* * 卖票案例出现了线程安全问题:出现重复的票和不存在的票 * * 解决线程安全问题的第二种方式:使用同步方法 * 就是一个方法,在方法上添加一个关键字synchronized修饰 * * 格式: * 修饰符 synchronized 返回值类型 方法名(参数列表){ * 可能出现线程安全问题的代码(访问了共享数据的代码) * } * */ public class RunnableImpl implements Runnable{ //定义一个共享的票源 private static int ticket = 100; //卖票 @Override public void run() { System.out.println("this:"+this);//RunnableImpl@67064 //让每个线程卖票重复执行 while(true){ //payTicket(); payTicketStatic(); } } /* * 静态的同步方法 * 静态的同步方法锁对象不能是this,this创建对象之后才会出现的 * 静态优先于非静态加载到内存中 * 静态方法的锁对象是本类的class属性(class文件对象,反射) * RunnableImpl.class */ public static /*synchronized*/ void payTicketStatic() { synchronized (RunnableImpl.class) { //判断是否还有票卖 if(ticket>0){ //为了提高出现问题的概率,增加一个睡眠 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //有票进行卖票操作 System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket--+"张票"); //ticket--; } } } /* * 定义一个同步方法 * 选中要抽出方法的代码,使用快捷键alt+shift+M,把代码抽取到一个方法中\ * 添加synchronized修饰 * 同步方法的的锁对象是现在类对象RunnableImpl(this) * this:当前的对象 */ public /*synchronized*/ void payTicket() { synchronized (this) { //判断是否还有票卖 if(ticket>0){ //为了提高出现问题的概率,增加一个睡眠 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //有票进行卖票操作 System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket--+"张票"); //ticket--; } } } }
或者:
package cn.itcast.demo09.Lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /* * 卖票案例出现了线程安全问题:出现重复的票和不存在的票 * * 解决线程安全问题的第一种方式:Lock锁,JDK1.5之后出现的新特性 * java.util.concurrent.locks.Lock接口 * 接口中的方法: * void lock() 获取锁。 * void unlock() 释放锁。 * Lock接口的实现类 * java.util.concurrent.locks.ReentrantLock类 implements Lock接口 * * 使用步骤: * 1.在成员位置创建一个Lock接口的实现类对象ReentrantLock * 2.在可能会出现线程安全问题的代码前,调用Lock中方法lock获取锁对象 * 3.在可能会出现线程安全问题的代码后,调用Lock中方法unlock释放锁对象 */ public class RunnableImpl implements Runnable{ //定义一个共享的票源 private int ticket = 100; //1.在成员位置创建一个Lock接口的实现类对象ReentrantLock Lock lock = new ReentrantLock();//多态 //卖票 @Override public void run() { //让每个线程卖票重复执行 while(true){ //2.在可能会出现线程安全问题的代码前,调用Lock中方法lock获取锁对象 lock.lock(); //判断是否还有票卖 if(ticket>0){ //为了提高出现问题的概率,增加一个睡眠 try { Thread.sleep(10); //有票进行卖票操作 System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket--+"张票"); } catch (InterruptedException e) { e.printStackTrace(); }finally { //无论程序是否出现异常,都会执行,用于释放资源 //3.在可能会出现线程安全问题的代码后,调用Lock中方法unlock释放锁对象 lock.unlock(); } } } } /*//卖票 @Override public void run() { //让每个线程卖票重复执行 while(true){ //synchronized (this) { //2.在可能会出现线程安全问题的代码前,调用Lock中方法lock获取锁对象 lock.lock(); //判断是否还有票卖 if(ticket>0){ //为了提高出现问题的概率,增加一个睡眠 try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } //有票进行卖票操作 System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket--+"张票"); //ticket--; } //} //3.在可能会出现线程安全问题的代码后,调用Lock中方法unlock释放锁对象 lock.unlock(); } }*/ }