JAVA进阶知识点总结 6-线程 同步 线程状态

01.第一章:多线程_回顾实现线程的方式一:

1).定义一个线程类,继承自Thread,重写run()方法;

class MyThread extends Thread{
		public void run(){
			...
		}
	}

2).启动线程
1).创建我们定义线程类的对象;
2).调用对象的start()方法启动线程;

main(){
		MyThread t = new MyThread();
		t.start();
	//	t.run();//只是普通的方法调用
	}

02.第一章:多线程_多线程的运行原理:

03.第一章:多线程_Thread类的常用方法_构造方法:

1).构造方法:

1).Thread():无参构造方法
2).Thread(String name):使用一个线程名称构造一个线程对象
每个线程都有一个默认的线程名:Thread-索引值 通过getName()方法可以获取线程名称
3).Thread(Runnable run):使用一个Runnable的实现类构造一个线程对象; 在“创建线程的方式二”中演示
4).Thread(Runnable run,String name):使用一个Runnable和一个线程名称构造一个线程对象。

03.第一章:多线程_Thread类的常用方法_成员方法:

1).public String getName():获取线程的名称;
2).public static void sleep(long millis):让当前线程休眠指定的毫秒值;

public class MyThread extends Thread {
    @Override
    public void run() {
        for (; ; ) {
            Date date = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String str = sdf.format(date);
            System.out.println(str);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

3).public static Thread currentThread():获取当前的线程对象;

public class Demo {
    public static void main(String[] args) {
        //主线程也是一个线程,是由JVM来启动的
        //主线程也有线程名
	  //Thread.currentThread()可以获取当前的线程对象,然后调用getName()就能获取线程名称
        for (int k = 0; k < 100; k++) {
            System.out.println(Thread.currentThread().getName() + " k = " + k);

        }
    }
}

04.第一章:多线程_制作线程的方式二_Runnable接口:

1).定义一个子类,实现Runnable接口,重写run()方法;
/*
注意:实现Runnable后,我们的类–不是一个线程类
后期必须要委托给一个线程类Thread去执行
*/

public class MyRun implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " i = " + i);

        }
    }
}

2).启动线程:
1).创建一个自定义Runnable子类对象;
2).创建一个Thread对象,并将自定义对象作为参数传给Thread的构造方法;
3).调用Thread的start()方法启动线程。它内部会去调用我们自定义对象内部的run()方法。
示例代码:

public class Demo {
    public static void main(String[] args) {
        MyRun myRun = new MyRun();

        Thread t1 = new Thread(myRun,"章子怡");
        Thread t2 = new Thread(myRun,"汪峰");

        t1.start();
        t2.start();

    }
}

05.第一章:多线程_两种实现方式的区别:

1).第一种:继承自Thread.由于Java单继承的限制,对于子类形成了制约。
2).第二种:实现接口Runnable。子类可以同时实现多个接口【建议使用】

06.第一章:匿名内部类的方式实现线程:

1).匿名内部类的格式:

new 父类/父接口名(){
		//子类的类体
	}

2).实现线程的方式一,使用匿名内部类的方式:
new Thread(){//Thread的子类类体}.start();
示例代码:

public static void main(String[] args) {
    //Thread t = new Thread的匿名子类对象();
    new Thread(){
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println("i = " + i);
            }
        }
    }.start();

//主线程继续
for (int k = 0; k < 100; k++) {
    System.out.println("k = " + k);
    }
}

2).实现线程的方式二,使用匿名内部类的方式:
new Thread(Runnable子类对象).start();
示例代码:

new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("i = " + i);
        }
    }
}).start();

07.第一章:多线程安全性问题:

1).多线程安全性问题产生的原因:多个线程同时访问同一个变量;
2).示意图:

3).代码:
1.定义一个类,实现Runnable接口:
/*
注意:实现Runnable后,我们的类–不是一个线程类
后期必须要委托给一个线程类Thread去执行
*/

public class MyRun implements Runnable {
    int a = 100;
    @Override
    public void run() {//线程1,线程2
        for (int i = 0; i < 100; i++) {//线程1(1,2)//线程2(1,2,3)//线程1(3)
           a++;//1.将a的值100取出到运算器中;2.在运算器中计算 100 + 1 ;3.将101赋给a
        }
        System.out.println("线程执行完毕!");
    }
}

2.测试类:

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        MyRun myRun = new MyRun();

        Thread t1 = new Thread(myRun,"章子怡");
        Thread t2 = new Thread(myRun,"汪峰");

        t1.start();
        t2.start();

        //主线程休息一秒,确保上面的两个线程执行完毕
        Thread.sleep(1000);

        //获取最终的结果
        System.out.println(myRun.a);//正常:300

    }
}

08.第二章:多线程售票模拟线程安全性问题:
1).定义Tickets类:

  public class Tickets implements Runnable {
    private int tickets = 100;
    @Override
  

  public void run() {
        //死循环
        while (true){
            //判断是否有票
            if (tickets > 0) {
                //取一张
                int t = tickets;
                try {
                    Thread.sleep(1);//目的:让效果更明显,让其它线程运行
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                tickets--;

                System.out.println(Thread.currentThread().getName() + 
									" 取走一张票:" + t);
            }else{
                System.out.println("没票了!");
                break;
            }
        }
    }
}

2).定义测试类:

public class Demo {
    public static void main(String[] args) {
        //1.创建一个Tickets对象
        Tickets t = new Tickets();

        //2.创建两个线程
        Thread t1 = new Thread(t,"窗口1");
        Thread t2 = new Thread(t,"窗口2");

        t1.start();
        t2.start();

    }
}

09.第二章:同步代码块_解决多线程售票线程安全性问题:

1).格式:
synchronized(锁对象){
//同步代码
}
锁对象:可以是任何对象,但必须保证多个线程共同使用同一个锁对象。
2).示例代码:
1).修改Tickets类:

public class Tickets implements Runnable {
    private int tickets = 100;
    private Object obj = new Object();
    @Override
    public void run() {
        while (true){
            synchronized (obj){
                if (tickets > 0) {
                    int t = tickets;
                    try {
                        Thread.sleep(1);//目的:让效果更明显
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    tickets--;

                    System.out.println(Thread.currentThread().getName() + 
									" 取走一张票:" + t);
                }else{
                    System.out.println("没票了!");
                    break;
                }
            }//一个线程执行完毕,会自动释放锁

        }
    }
}

2).主线程类:

public class Demo {
    public static void main(String[] args) {
        //1.创建一个Tickets对象
        Tickets t = new Tickets();

        //2.创建两个线程
        Thread t1 = new Thread(t,"窗口1");
        Thread t2 = new Thread(t,"窗口2");

        t1.start();
        t2.start();

    }
}

10.第二章:同步方法_解决多线程售票线程安全性问题【常用】:

1).格式:
//同步方法,使用的锁对象是:this
public synchronized int getTicket(){
//同步代码
}
2).示例代码:
1).修改Tickets类即可:

public class Tickets implements Runnable {
    private int tickets = 100;
    @Override
    public void run() {
        while (true){
            int t = this.getTicket();
            if (t > 0) {
                System.out.println(Thread.currentThread().getName() + 
									" 取走一张票:" + t);

            }else{
                System.out.println("没票了!!");
                break;
            }
        }
    }
    //同步方法,使用的锁对象是:this
public synchronized int getTicket() {
        if (tickets > 0) {
            int t = tickets;
            try {
                Thread.sleep(1);//目的:让效果更明显
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            tickets--;
            return t;
        } else {
            return 0;
        }
    }
}

11.第二章:Lock锁_解决多线程售票线程安全性问题:

1).格式:

Lock l = new 某个实现类对象();
l.lock();//加锁 
try { 
	//同步代码
} finally { 
	l.unlock(); //解锁
} 

2).示例代码:

public class Tickets implements Runnable {
    private int tickets = 100;
    private Lock lock = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            lock.lock();//加锁
            try {
                if (tickets > 0) {
                    int t = tickets;
                    try {
                        Thread.sleep(1);//目的:让效果更明显
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    tickets--;

                    System.out.println(Thread.currentThread().getName() + " 取走一张票:" + t);
                } else {
                    System.out.println("没票了!");
                    break;
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                lock.unlock();//解锁
            }
        }
    }
}

12.第三章:线程状态:

13.第三章:线程状态_无限等待_wait方法:

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();

        new Thread(){
            @Override
            public void run() {
                synchronized (obj) {
                    for (int i = 0; i < 20; i++) {
                        System.out.println("i = " + i);
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        if (i == 10) {
                            //让当前的线程挂起,等待被唤醒
                            try {
                                System.out.println("我开始等待...");
                                obj.wait();//会立即释放obj锁
                                System.out.println("我醒了,开始干活了....");
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }.start();
        //开启一个新线程
        new Thread(){
            @Override
            public void run() {
                //获取obj锁对象
                System.out.println("第二个线程开始执行...");
                synchronized (obj){
                    //唤醒所有在obj锁上等待线程.
                    System.out.println("第二个线程开始唤醒....");
                    obj.notifyAll();//唤醒后,
                }//唤醒后,会释放锁
            }
        }.start();
    }
}

14.第三章:线程状态_无限等待_包子铺案例:

=======================================================================
学习目标总结:
01.能够描述Java中多线程运行原理
每一个执行线程都有一片自己所属的栈内存空间。进行方法的压栈和弹栈

02.能够使用继承类的方式创建多线程

class MyThread extends Thread{
	public void run(){
		//...
	}
}
main(){
	MyThread t = new MyThread();
	t.start();
	t.run();//普通的方法调用
	t.start();//抛异常
}

03.能够使用实现接口的方式创建多线程

class MyRun implements Runnable{
	public void run(){
		//...
	}
}
main(){
	MyRun myRun = new MyRun();
	Thread t = new Thread(myRun);
	t.start();
}

04.能够说出实现接口方式的好处
1).解决单继承的限制。

05.能够解释安全问题的出现的原因
1).多个线程同时访问同一个变量;

06.能够使用同步代码块解决线程安全问题
synchronized(锁对象){
//同步代码
}
07.能够使用同步方法解决线程安全问题

public synchronized void show(){
	//同步代码
}

08.能够说出线程6个状态的名称
1.新建
2.可运行
3.计时等待
4.锁阻塞
5.无限等待
6.被终止
09.能够理解等待唤醒案例

public static void main(String[] args) throws InterruptedException {
Object obj = new Object();

new Thread(){
    @Override
    public void run() {
        synchronized (obj) {
            for (int i = 0; i < 20; i++) {
                System.out.println("i = " + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (i == 10) {
                    //让当前的线程挂起,等待被唤醒
                    try {
                        System.out.println("我开始等待...");
                        obj.wait();//会立即释放obj锁
                        System.out.println("我醒了,开始干活了....");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}.start();
//开启一个新线程
new Thread(){
    @Override
    public void run() {
        //获取obj锁对象
        System.out.println("第二个线程开始执行...");
        synchronized (obj){
            //唤醒所有在obj锁上等待线程.
            System.out.println("第二个线程开始唤醒....");
            obj.notifyAll();//唤醒后,
        }//唤醒后,会释放锁
    }
    }.start();
}

猜你喜欢

转载自blog.csdn.net/AdamCafe/article/details/88900484