一.多线程
--1,概念
现有的程序,都是只有一个人在干活(单线程程序)--程序的执行效率低
为了提高程序的执行效率,我们可以让多个人共同去完成这一个任务(多线程程序)
--2,进程和线程的区别
--进程--指的是正在运行的程序,有的程序依赖一个进程,有的程序可以依赖很多进程
--线程--是操作系统能够调度的最小单位,进程包含线程.
--一个进程的运行至少依赖一个线程--单线程程序--效率低
--一个进程的运行依赖多个线程--多线程程序--效率高
--3,并发和并行的区别
--4,线程的状态/线程的生命周期
--刚new出来的线程是新建状态
--可运行状态,等待CPU的调度
--运行状态,正式开始执行任务
--理想状态,线程进入结束状态
--实际情况里,线程会以各种方式进入阻塞状态.
二.多线程编程
--1,继承Thread类
--线程是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程
--创建对象
Thread()
分配新的 Thread 对象。
Thread(Runnable target)
分配新的 Thread 对象。
Thread(Runnable target, String name)
分配新的 Thread 对象。
Thread(String name)
分配新的 Thread 对象。
--常用方法
static Thread currentThread()
返回对当前正在执行的线程对象的引用。
long getId()
返回该线程的标识符。
String getName()
返回该线程的名称。
void run()
如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;
void setName(String name)
改变线程名称,使之与参数 name 相同。
static void sleep(long millis)
在指定的毫秒数内让当前正在执行的线程休眠(
void start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
void stop()
Thread.stop 来终止线程
--测试
package cn.tedu.thread;
//测试 多线程编程
public class Test2_Thread {
public static void main(String[] args) {
//4,测试--让多线程启动起来
MyThread t1 = new MyThread();//--新建状态
MyThread t2 = new MyThread();
//6,设置线程名称
t1.setName("灭霸");
t2.setName("猪猪侠");
//5,启动线程!!!
t1.start();//--可运行状态--等待CPU调度
t2.start();
// t1.run();//把业务当做普通的方式执行.没有多线程(抢占)效果
// t2.run();
}
}
//1,模拟多线程编程--extends Thread
class MyThread extends Thread{
//2,把所有业务,写在重写的run()里
@Override
public void run() {//--运行状态
for (int i = 0; i < 100; i++) {
//3,getName()线程名称 Thread-0 Thread-1 --getId()线程标志
System.out.println(getId() + super.getName()+"==="+i);
}
}//--结束状态
}
--2,实现Runnable接口
--Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为 run 的无参数方法。
--常用方法
void run()
使用实现接口 Runnable 的对象创建一个线程时,启动该线程将导致在独立执行的线 程中调用对象的 run 方法。
--测试
package cn.tedu.thread;
//测试 Runnable
public class Test3_Runnable {
public static void main(String[] args) {
//5,测试--让线程启动起来
MyRunnable target = new MyRunnable();
Thread t1 = new Thread(target);//绑定接口实现类和Thread的关系
//6,启动线程
t1.start();
//如何模拟多线程?
Thread t2 = new Thread(target);
t2.start();
}
}
//1,模拟多线程编程--implements Runnable
class MyRunnable implements Runnable{
//2,实现了Runnable接口,就要把接口里的所有抽象方法都重写,否则就包含着抽象方法是一个抽象类
//3,在多线程编程中,需要把所有的业务统统放在run()里
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//4,打印100次线程名称
//Thread类里的静态currentThread()返回当前正在执行任务的线程对象的引用
System.out.println(Thread.currentThread().getName()+"==="+i);
}
}
}
--同步锁
--1,概述
把有可能出现问题的代码包起来,一次只让一个线程执行。通过sychronized关键字实现同 步。当多个对象操作共享数据时,可以使用同步锁解决线程安全问题。
--2,语法:
--使用关键字synchronized表示同步.使多个线程需要排队访问共享资源,而不是发生抢占现象.
--实现了同步后,好处是:共享数据安全了.坏处是:效率低了.--牺牲了效率,实现了安全.
--synchronized可以修饰方法,也可以修饰代码块.
--同步方法--使用的锁对象是this
synchronized public void eat(){}
--同步代码块--锁对象可以任意
synchronized(对象){
需要同步的代码;
}
--以后的数据有没有多线程的数据安全隐患???--多线程编程中--有多条语句操作了共享数据
--implements Runnable
package cn.tedu.thread;
//解决 线程安全隐患
public class Test1_Lock {
public static void main(String[] args) {
//4,测试
MyTickets2 target = new MyTickets2();
Thread t1 = new Thread(target);
Thread t2 = new Thread(target);
Thread t3 = new Thread(target);
Thread t4 = new Thread(target);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class MyTickets2 implements Runnable{
//需求:让四个窗口,一起卖出100张票
int tickets = 100 ;
Object obj = new Object();
String s = "jack";
//目前程序中,由于在多线程编程中,出现了多个线程抢占资源而造成的数据错乱.
//加锁来解决数据安全隐患问题.考虑以下两个问题:
//1,锁的位置:把会有问题的代码锁起来,从问题源头开始,到结束为止,都包起来
//2,锁的对象:代码块里使用锁,需要考虑锁对象是谁?可以是任意对象,只要是同一个对象就行
//3,同步锁也可以直接锁方法,默认是用的锁对象是this
//4,如果共享资源是 一个 静态资源,锁对象必须是 类名.class!!!
// synchronized public void run() {
@Override
public void run() {
while (true) {
// synchronized (new Object()) {//还有问题,四个线程就产生了4个对象!!
// synchronized (obj) {//从头到尾都是只有一个对象!!!
// synchronized (s) {//从头到尾都是只有一个对象!!!
synchronized (this) {//从头到尾都是只有一个对象!!!
if (tickets > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//3,打印线程名称--Thread.currentThread()
System.out.println(Thread.currentThread().getName() + "==" + tickets--);
} else {
break;//合理的出口!!
}
}
}
}
}
--extends Thread
package cn.tedu.thread;
//模拟多线程售票 -- 继承Thread
public class Test4_Tickets {
public static void main(String[] args) {
//4,启动线程
MyTickets t1 = new MyTickets();
MyTickets t2 = new MyTickets();
MyTickets t3 = new MyTickets();
MyTickets t4 = new MyTickets();
t1.start();
t2.start();
t3.start();
t4.start();
//--问题1:总共需要卖出100张票,但是,现在却卖出了400张票.为什么?
//成员变量tickets,每次实例化时,都会跟着对象初始化一次.4个对象就各自拥有一个tickets,总共变成了400张
//--问题1的解决方案:把共享资源tickets加static修饰,变成全局唯一全局共享
//目前来看,程序中的4个线程完美的配合着卖了100张票.
//但是,数据最终有没有安全隐患--让程序休眠一会儿
}
}
//1,创建多线程类-extends Thread
class MyTickets extends Thread{
//--需求:让四个窗口,一起卖出100张票
static int tickets = 100 ;//定义变量,记录票数
//2,开始卖票--把业务放入run()
@Override
public void run() {
//一直卖票,卖完结束
while(true) {//死循环----配置设置好出口!!
//如果共享资源是 一个 静态资源,锁对象必须是 类名.class!!!
synchronized (MyTickets.class) {
if(tickets>0) {
//!!! 5,验证多线程中,数据是否安全,都要接受这次考验
//问题2: 超卖: 卖出了0号票,甚至-1号票
//问题3: 重卖: 同一张票卖给了好几个人
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//3,打印线程名称
System.out.println(getName()+"===="+tickets--);
}else {
break;//合理的出口!!
}
}
}
}
}