- 多线程基础
1.1 认识进程和线程
1.1.1 什么是进程
1.1.1.1 进程是正在进项的程序,是资源分配的一个基本单位,有内存分配;
1.1.2 什么是线程
1.1.2.1 线程是进程的一个执行单位,也是进程的执行顺序;
1.1.2.2 一个进程至少有一个线程,可以由两个或以上的线程;
1.1.3 JVM至少有几个线程
1.1.3.1 至少有一个或两个线程,main方法和垃圾回收线程;
1.1.4 什么是多线程
1.1.4.1 2个或以上的线程去执行
1.1.5 多线程的作用
1.1.5.1 提高效率,线程之间切换的小号是可以接受的;
1.1.5.2 单线程容易阻塞;
1.1.5.3 多个线程同时执行代码;
1.1.5.4 一个线程挂了,还有其他的线程在,程序还在执行
1.1.6 进程和线程是谁创建的
都是操作系统创建的
1.1.7 如何自定义一个线程
1.1.7.1 继承Thread类,重写run方法
1.1.7.2 生成对象
1.1.7.3 开启start
1.1.8 所有实现的接口
1.1.8.1 Runnable接口
1.1.8.2 new线程对象的时候,创建了线程
1.1.8.3 start方法,沟通了操作系统,告诉操作系统有一个线程要开启,也调用了run
1.2 代码示例
1.2.1 在main主线程里,再度开启其他线程
1.2.1.1 测试类:
public class Test {
public static void main(String[] args) throws ParseException {
Test001 t = new Test001();//这里代表着产生了一个线程
Test001 t1 = new Test001();//这里代表着产生了一个线程
System.out.println("开启线程之前");
//t1.run();//代表着调用了对象的方法
t.start();//这里代表开启了一个线程,是沟通了操作系统
t1.start();//这里代表开启了一个线程,是沟通了操作系统
try {
Thread.sleep(15);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("开启线程之后");
System.out.println("main over");
}
}
1.2.1.2 重写run方法类,继承Thread类:
class Test001 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("run .. "+i);
}
}
}
1.2.2 创建一个线程,和主程序交替执行
1.2.2.1 测试类
public class ThreadTest {
public static void main(String[] args) {
Test t = new Test();
t.start();
for (int i = 0; i < 10; i++) {
System.out.println("main>>>"+i);
}
System.out.println("main - gg");
}
}
1.2.2.2 方法类
class Test extends Thread{
int i = 0;
@Override
public void run() {
while(true){
if(i<10){
System.out.println("run方法..." + i++);
}else{
System.out.println("run - OVER");
break;
}
}
}
}
1.2.3 模拟买票功能
1.2.3.1 测试类
public class TestRunnable01 {
public static void main(String[] args) {
Runnable x1 = new XinXiaoMi();
Thread t1 = new Thread(x1);
Thread t2 = new Thread(x1);
Thread t3 = new Thread(x1);
t1.setName("小红");
t2.setName("小蓝");
t3.setName("小绿");
t1.start();
t2.start();
t3.start();
}
}
1.2.3.2 方法类
class XinXiaoMi implements Runnable{
static int i =100;//共享的
Object o = new Object();
@Override
public void run() {
boolean b = true;
while(b){
synchronized (o) {
if(i>0){//i = 1
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"...卖出了手机,编号是..." + i--);
}
else{
break;
}
}
}
}
}
1.2.4 小明和老婆同时存钱,每次存取500元,每人存5次
1.2.4.1 测试类:
public class TestRunnable02 {
public static void main(String[] args) {
test t = new test();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.setName("小明");
t2.setName("陈学冬");
t1.start();
t2.start();
}
}
1.2.4.2 方法类:
class test implements Runnable{
int con = 0;//初始定义了账户余额为0元
Object o = new Object();
@Override
public void run() {
synchronized (o) {
for (int i = 0; i < 5; i++) {
con += 500;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 存了500大洋---此时账户余额为 。。"+con);
}
}
}
}
1.2.4 小明和老婆同时存钱,每次存取500元,每人存5次; 同时实现方法的同步
1.2.4.1 测试类
public class TestM {
public static void main(String[] args) {
test00 t = new test00();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.setName("小明");
t2.setName("baby");
t2.start();
t1.start();
}
}
1.2.4.2 方法类
class test00 implements Runnable{
int con = 0;//初始定义了账户余额为0元
Object o = new Object();
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" 到达房间门口");
this.show();//一次就好,许你地老天荒
}
public synchronized void show(){
System.out.println(Thread.currentThread().getName()+" 锁住了房间");
for (int i = 0; i < 5; i++) {
con += 500;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 存入了500,此时余额是 "+con);
}
System.out.println(Thread.currentThread().getName()+" 离开了房间,打开了锁");
}
}
1.2.5 小明和老婆同时存钱,每次存取500元,每人存5次; 同时实现方法的同步;同时实现交替存储
1.2.5.1 测试类
public class TestM2 {
public static void main(String[] args) {
test000 t = new test000();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.setName("小明");
t2.setName("baby");
t2.start();
t1.start();
}
}
1.2.5.2 方法类
class test000 implements Runnable{
int con = 0;//初始定义了账户余额为0元
Object o = new Object();
@Override
public void run() {
for (int i = 0; i < 5; i++) {
//等待区
show();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void show(){
System.out.println(Thread.currentThread().getName()+" 上锁了");
con += 500;
System.out.println(Thread.currentThread().getName()+" 存入了 500 , 此时余额为 "+con);
System.out.println(Thread.currentThread().getName()+" 锁开了");
}
}
1.2.6 死锁的示例
1.2.6.1 测试类:
/* Runnablere =new Runnable() {
public void run() {
}
};
*/
public class Test01 {
public static void main(String[] args) {
TestSY ts1 = new TestSY();
Thread th1 = new Thread(ts1);
Thread th2= new Thread(ts1);
th1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
TestSY.b = false;
th2.start();
}
}
1.2.6.2 方法类:
class TestSY implements Runnable{
static boolean b = true;
Object o = new Object();
Object o1 = new Object();
@Override
public void run() {
if( b ){
synchronized (o) {
System.out.println("if 拿到了 o 的锁");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1) {
System.out.println("if-----");
}
}
}else{
synchronized (o1) {
System.out.println("else 拿到了 o1 的锁");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o) {
System.out.println("else-----");
}
}
}
}
}
1.3 cpu调度
cpu的时间片,cpu控制着执行;
cpu会自行分配资源给线程,得到资源的线程可以执行,没有得到资源的线程没有办法执行;
多个cpu才是真正意义上的同时执行多线程,因为运行速度特别多,会给我们产生错觉,认为是同时执行的;
1.4 线程的状态
1.4.1 新生
1.4.1.1 new对象的时候代表着新生;
1.4.2 就绪
1.4.2.1 start方法代表着就绪,这个时候在等待着CPU的资源;如果有资源就进入执行状态,没有资源就继续等待;
1.4.3 执行
1.4.3.1 拿到时间片,正在执行;时间片被拿走了就离开了执行状态;
1.4.4 阻塞
1.4.4.1 睡觉sleep,让资源;时间结束了就回到就绪状态,等待资源;
1.4.5 死亡
1.4.5.1 线程执行结束;非正常死亡方式;
1.5 两种创建线程方式的比较
1.5.1 继承方式创建简单,使用的方法也多;
1.5.2 java是单继承的,使用集成方式过于死板;
1.5.3 继承是不共享资源的,实现方式是共享的;
1.5.4 主要由于单继承的机制,推荐使用Runnable接口实现这种方式
1.5.4.1 实现接口,重写方法
1.5.4.2 new一个对象
1.5.4.3 把new的对象放进线程(Thread)对象里
1.5.4.4 用线程对象开启线程
1.5.4.5 注意:run方法不能抛异常
1.6 同步代码块原理解析
1.6.1 java提供了锁的机制synchronized ,可以锁住一段代码,在任何时期这段代码里面最多只有一个线程在执行。每个要进来的线程都会先判断此处有没有上锁。
1.6.1.1 如果没有,就进去并且上锁,再执行完之后才会离开,同时打开锁,其他的线程才可以进来
1.6.1.2 如果被锁,再门口等待,进不去; 这里锁,锁的是对象。具体锁那个对象呢。具体情况具体分析
1.6.2 同步的优缺点
1.6.2.1 同步的前提
1.6.2.1.1 两个或两个以上的线程去操作共享数据时
1.6.2.2 同步的好处
1.6.2.2.1 保证了线程的安全性
1.6.2.3 同步的缺陷
1.6.2.3.1 消耗增加,但是影响不大
1.6.2.3.2 使用不当容易造成死锁