/**
* 什么是多线程呢?即就是一个程序中有多个线程同时执行.
* 1.单线程程序:即,若有多任务只能依次执行.当上一个线程结束,下一个线程开始.
* 2.多线程程序:即,多个任务同时执行,线程会抢占CPU
* 原理: 1.分时调度,所有线程轮流使用CPU的使用权,平均分配每个线程占用时间
* 2.抢占式调度,优先让优先高级线程使用CPU,如果线程优先级相同,那么会随机选择一个
* -----------------------------------------------------
* 主线程:执行主(main)方法的线程;JVM执行java程序,从main方法开始执行,JVM会找操作系统,开辟一条java程序通向CPU的通道,这个通道叫主(main)线程.
* 怎么创建线程呢?
* java.lang.Thread类:描述线程,也叫线程类
* 实现步骤:
* 1.创建一个Thread类的子类,继承Thread类
* 2.重写Thread类中run方法 3.创建Thread的子类对象
* 4.调用子类继承自Thread类的start方法,开启线程,执行run
* 注意点:使用线程开始执行:java虚拟机调用该线程的run方法;
* 如果是两个线程并发地运行:当前线程(main主线)和另一个线程(新开的线程,执行其run方法).
* 多次启动一个线程是非法的
* 获取线程的名字:
* Thread.currentThread().getName();
* 改变线程的名字:
* void setName(String name)
*/
/*
* 创建一个Thread类的子类,继承Thread类
*/
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("run-->"+i);
}
}
}
//测试类
public class DemoMainThread {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();//开启一个新线程,执行run方法
//new MyThread().start();//也可以使用匿名对象
for (int i = 0; i < 20; i++) {
System.out.println("main-->"+i);
}
}
}
/**
* Runnable接口,是Thread线程类 implements Runnable
* Thread类的构造方法:
* Thread(Runnable tag) 分配新的Thread对象
* 实现步骤:
* 1.定义Runnable接口的实现类 -> DemoRunnableImpl01
* 2.重写Runnable接口中的方法run方法,设置线程任务
* 3.创建Runable接口的实现类对象
* 4.创建一个Thread类对象,构造方法中传递Runnable接口实现类对象
* 5.调用Thread类中的start方法
* 实现Runnable接口的好处:
* 1.避免了单继承的局限性,一个类只能有一个父类,可以有多个接口
* 2.把设置线程任务,和开始线程进行分离( 解耦 ),增强了扩展性
*/
public class DemoRunnableImpl01 implements Runnable{
@Override
public void run() {
//2.重写Runnable接口中的方法run方法,设置线程任务
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
//测试类
public class DemoThread02 {
public static void main(String[] args) {
//3.创建Runable接口的实现类对象
DemoRunnableImpl01 impl01 = new DemoRunnableImpl01();
//4.创建一个Thread类对象,构造方法中传递Runnable接口实现类对象
Thread thread = new Thread(impl01);
thread.start();
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
多线程买票案例
/*
*一个买电影票的案例来说明多线程
*/
public class Demo01BuyTicket implements Runnable{
//定义一个共享票源
private int ticket = 101;
@Override
public void run() {
while(true){
if(ticket > 0){
try {
Thread.sleep(10);//提高出现问题机率
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在买第"+ticket--+"张票");
}
}
}
}
重票和负票解决方案
/**
*第一种解决方案:使用同步代码块
*格式:
* synchronized(锁对象){
* 可能会产生安全问题的代码
* }
*注意:锁对象可以是任意的对象,但是必须保证多个线程使用同一个锁对象
*
*/
public class Demo01BuyTicket02 implements Runnable{
//定义一个共享票源
private int ticket = 101;
//定义锁对象
Object object = new Object();
@Override
public void run() {
while(true){
synchronized (object) {
if(ticket > 0){
try {
Thread.sleep(10);//提高出现问题机率
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在买第"+ticket--+"张票");
}
}
}
}
}
/**
*第二种解决方案:使用同步方法
*格式:
* 修饰符 synchronized 返回类型 方法名(参数列表){
* 可能会产生安全问题的代码
* }
*注意:锁对象可以是任意的对象,但是必须保证多个线程使用同一个锁对象
*
*/
public class Demo01BuyTicket01 implements Runnable{
//定义一个共享票源
private int ticket = 101;
//定义锁对象
Object object = new Object();
@Override
public void run() {
while(true){
payTicket();
}
}
private synchronized void payTicket() {
if(ticket > 0){
try {
Thread.sleep(10);//提高出现问题机率
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在买第"+ticket--+"张票");
}
}
}
/**
*第三种解决方案:Lock锁 这是 jdk1.5
*java.util.concurrent.locks.Lock接口
* Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
* 我们可以看到什么时候获取锁,什么时候释放锁,处理异常更加方便 *
* Lock接口中的抽象方法
* void lock() 获取锁。
* void unlock() 释放锁。 *
* java.util.concurrent.locks.ReentrantLock implments Lock *
* 使用步骤:
* 1.在成员位置创建一个Lock接口的实现类对象
* 2.在可能会产生安全问题的代码前调用Lock接口中方法lock获取锁对象
* 3.在可能会产生安全问题的代码后调用Lock接口中方法unlock释放锁对象 *
*/
public class Demo01BuyTicket03 implements Runnable{
//定义一个共享票源
private int ticket = 101;
//1.在成员位置创建一个Lock接口的实现类对象
Lock lock = new ReentrantLock();
@Override
public void run() {
while(true){
lock.lock();
if(ticket > 0){
try {
Thread.sleep(10);//提高出现问题机率
System.out.println(Thread.currentThread().getName()+"正在买第"+ticket--+"张票");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
Demo01BuyTicket buyTicket = new Demo01BuyTicket();
Thread th01 = new Thread(buyTicket);
Thread th02 = new Thread(buyTicket);
Thread th03 = new Thread(buyTicket);
th01.start();
th02.start();
th03.start();
}
}
多线程出现重票负票原理
/**
* 产生线程安全的问题:卖票出现了重复和不存在的票
* 当主方法执行,3个线程一起抢夺CPU的执行权,谁抢到了谁执行
* 假如
* th01线程抢到了CPU的执行权,进入if语句中执行,刚进去就失去了执行权
* th02线程抢到了CPU的执行权,进入if语句中执行,刚进去就失去了执行权
* th03线程抢到了CPU的执行权,进入if语句中执行,刚进去就失去了执行权
* 当
* th01线程抢到了CPU的执行权,卖第1张票 ticket-- = 0
* th02线程抢到了CPU的执行权,卖第0张票 ticket-- = -1
* th03线程抢到了CPU的执行权,卖第-1张票 ticket-- = -2
* 执行完了,ticket > 0 才可以卖,所以就出现了重复和不存在的票
*/