主线程:执行主方法的线程,就叫做主线程
单线程程序:程序从mani开始从上到下依次运行
程序从main方法开始运行,JVM运行main方法,会找操作系统
开辟一条通向cpu的执行路径,cpu可以通过这条路径来执行main方法
这条路径有一个名字叫主(main)线程
创建线程方式一继承Thread类
实现步骤:
1.创建Thread类的子类
2.重写Thread类中的run方法,设置线程的任务
3.创建Thread类的子类对象
4.调用Thread类中的start方法开启一个新的线程,执行run方法
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
结果是两个线程并发地运行;当前线程(main线程)和另一个线程(执行 run 方法的线程)。
多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。
打印的结果出现了随机性:
开启两个线程,对于cpu就选择权利
喜欢谁,就执行谁,所以就出现了随机性结果
1 public class MyThread extends Thread{ 2 /* 3 * 2.重写Thread类中的run方法,设置线程的任务 4 * 开启这个线程要干什么事情 5 */ 6 @Override 7 public void run() { 8 for (int i = 0; i < 50; i++) { 9 System.out.println("run..."+i); 10 } 11 } 12 } 13 public static void main(String[] args) { 14 //3.创建Thread类的子类对象 15 MyThread mt = new MyThread(); 16 //mt.run();//不会开启线程,还是单线程程序 17 //4.调用Thread类中的start方法开启一个新的线程,执行run方法 18 mt.start(); 19 20 new MyThread().start(); 21 22 for (int i = 0; i < 50; i++) { 23 System.out.println("main..."+i); 24 } 25 }
线程的名称:
主线程:"main"
开启的其它线程的名称:"Thread-0","Thread-1"....
获取线程的名称
1.Thread类中的方法getName
String getName() 返回该线程的名称。
2.Thread类中的静态方法,获取当前正在执行的线程
static Thread currentThread() 返回对当前正在执行的线程对象的引用。
设置线程的名称:
1.Thread类中的方法setName(String name)
void setName(String name) 改变线程名称,使之与参数 name 相同。
2.子类添加带参构造,调用父类Thread类的带参构造方法,传递线程的名称,让父类给线程起名字(让父亲给儿子起名字)
Thread(String name) 分配新的 Thread 对象。
创建线程方式—实现Runnable接口
实现步骤:
1.创建Runnable接口的实现类
2.重写Runnable接口中的run方法,设置线程任务
3.创建Runnable接口的实现类对象
4.创建Thread类对象,构造方法中传入Runnable接口的实现类
Thread(Runnable target) 分配新的 Thread 对象。
5.调用Thread类中的方法start,开启线程执行run方法
实现Runnable的好处
1.避免了类继承Thread类之后,无法继承其它的类(单继承的局限性)
2.把设置线程任务,和开启线程进行解耦,增强了扩展性
实现类的作用:就是设置线程任务
Thread类的作用:开启线程
好处:传递不同的实现类,实现类重写的方法不一样,可以调用不同的方法
线程的匿名内部类使用
匿名:没有名字
内部类:写在其他类内部的类(成员位置:成员内部类,局部位置(方法中):局部内部类)
匿名内部类的格式:
new 父类/接口(){
重写父类/接口中的方法;
};
多线程的父类:
Thread
Runnable
1 new Thread(){ 2 //重写run方法,设置线程任务 3 @Override 4 public void run() { 5 for (int i = 0; i < 20; i++) { 6 System.out.println(Thread.currentThread().getName()+":"+i); 7 } 8 } 9 }
以上一堆代码就是一个创建子类重写父类方法的过程
相当于: new MyThread().start();
程序出现了线程安全问题:卖了重复的票和不存在的票
解决方案:
第一种方式:可以使用同步代码块
synchronized(锁对象){
产生安全问题的代码;
访问了共享数据的代码;
}
注意:
必须要保证多个线程使用的是同一个锁对象
//在成员位置上创建一个锁对象(保证唯一)
1 Object obj = new Object(); 2 3 @Override 4 public void run() { 5 //让卖票重复执行 6 while(true){ 7 8 * 同步代码块 9 * 程序会频繁的判断锁,获取锁,释放锁,所以会降低速度 10 11 synchronized (obj) { 12 if(ticket>0){ 13 //为了提高安全问题的概率,让程序睡眠 14 try { 15 Thread.sleep(10); 16 } catch (InterruptedException e) { 17 e.printStackTrace(); 18 } 19 //卖票ticket-- 20 System.out.println(Thread.currentThread().getName()+"...卖第"+ticket--+"张票"); 21 } 22 } 23 } 24 }
程序出现了线程安全问题:卖了重复的票和不存在的票
解决方案:
第二种方式:同步方法
使用步骤:
1.把可能出现安全问题的代码抽取到一个方法中
2.把方法增加一个关键字synchronized
修饰符 synchronized 返回值类型 方法名(参数){
可能出现安全问题的代码;
访问了共享数据的代码;
}
同步方法使用的锁对象是什么?
使用的就是本类对象new RunnableImpl()-->叫this
静态的同步方法,使用时什么锁对象?
使用的是本类对象的class属性(反射)
RunnableImpl.class
1 *@Override 2 public void run() { 3 //让卖票重复执行 4 while(true){ 5 payTicket2(); 6 } 7 } 8 9 10 * 静态的同步方法 11 12 public static synchronized void payTicket2(){ 13 synchronized (RunnableImpl.class) { 14 if(ticket>0){ 15 //为了提高安全问题的概率,让程序睡眠 16 try { 17 Thread.sleep(10); 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21 //卖票ticket-- 22 System.out.println(Thread.currentThread().getName()+"...卖第"+ticket--+"张票"); 23 } 24 } 25 } 26 27 28 29 * 抽取出一个同步方法 30 * 快捷键:alt+shift+m 31 32 public ynchronized void payTicket1() { 33 synchronized (this) { 34 //System.out.println(this);//[email protected] 35 if(ticket>0){ 36 //为了提高安全问题的概率,让程序睡眠 37 try { 38 Thread.sleep(10); 39 } catch (InterruptedException e) { 40 e.printStackTrace(); 41 } 42 //卖票ticket-- 43 System.out.println(Thread.currentThread().getName()+"...卖第"+ticket--+"张票"); 44 } 45 } 46 }
程序出现了线程安全问题:卖了重复的票和不存在的票
*
* 解决方案:
* 第三种方式:使用Lock接口,JDK1.5之后出现的
*
* java.util.concurrent.locks.Lock接口
* 方法:
* void lock() 获取锁。
* void unlock() 释放锁。
* 接口的实现类:ReentrantLock
*
* 实现步骤:
* 1.在成员位置创建一个Lock接口的实现类对象ReentrantLock
* 2.在可能出现线程安全问题的代码前,调用lock方法获取锁
* 3.在可能出现线程安全问题的代码后,调用unlock方法释放锁
*
*1.在成员位置创建一个Lock接口的实现类对象ReentrantLock
Lock l = new ReentrantLock();
1 @Override 2 public void run() { 3 //让卖票重复执行 4 while(true){ 5 //2.在可能出现线程安全问题的代码前,调用lock方法获取锁 6 l.lock(); 7 if(ticket>0){ 8 //为了提高安全问题的概率,让程序睡眠 9 try { 10 Thread.sleep(10); 11 //卖票ticket-- 12 System.out.println(Thread.currentThread().getName()+"...卖第"+ticket--+"张票"); 13 } catch (InterruptedException e) { 14 e.printStackTrace(); 15 }finally { 16 //3.在可能出现线程安全问题的代码后,调用unlock方法释放锁 17 l.unlock(); 18 } 19 } 20 }