线程,同步

01.第一章:线程_多线程原理:

1).定义线程:
    class MyThread extends Thread{
        public void run(){
            for(int i = 0; i < 100 ; i++){
                System.out.println("i = " + i);
            }
        }
    }
2).启动线程:
    main(){
        MyThread t1 = new MyThread();   
        t1.start();
        for(int k = 0; k < 100 ; k++){
            System.out.println("k = " + k);
        }
    }
3).多个线程的运行原理:
    1).当主线程启动一个独立的线程后,这个独立的线程会与主线程“同时”运行;
    2).但对于“单核,单颗”的CPU来讲,在某一个“时间点”上,只能有一个线程去执行,执行一小段时间后,会立即切换到另一个线程。CPU始终会在这两个线程间频繁的、快速的切换。

02.第一章:线程创建线程的方式一继承Thread类及常用方法:

1).自定义线程类,继承自Thread;
2).重写run()方法(将线程中要做的事情写在这里)
3).启动线程:
    1).创建自定义线程类的对象;
    2).调用对象的start()方法:
示例代码:
public class MyThread extends Thread {
@Override
public void run() {
    for (int i = 0; i < 100; i++) {
        System.out.println("i = " + i);

    }
  }
}
public class Demo {
public static void main(String[] args) {
    MyThread t1 = new MyThread();
    t1.start();
    for (int k = 0; k < 100; k++) {
        System.out.println("k = " + k);

    }
  }
}
注意事项:
   1).对于“一个线程类”,可以创建多个“线程对象”,每个线程对象都可以单独启动;
   2).对于“一个线程对象”,只能调用一次start(),不能多次start();
   3).重写的是run(),但启动线程调用的是start();
4).Thread类中的常用方法:
   1).public String getName():获取线程名称;
    注:每个线程对象都有一个默认的线程名称:Thread - 索引值
   2).public void setName(String name):设置线程名称:
    示例代码:
public class MyThread extends Thread {
@Override
public void run() {
    for (int i = 0; i < 100; i++) {
        System.out.println(this.getName() + " i = " + i);

    }
  }
}
public class Demo {
public static void main(String[] args) {
    MyThread t1 = new MyThread();
    MyThread t2 = new MyThread();

    t1.setName("章子怡");
    t2.setName("汪峰");

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


    for (int k = 0; k < 100; k++) {
        System.out.println(Thread.currentThread().getName() + " k = " + k);

    }
  }
}


   3).public static Thread currentThread():获取当前的线程对象;示例代码:
    main(){
        System.out.println("主线程名:" +         Thread.currentThread().getName());
    }
   4).public static void sleep(long m):让当前的线程休眠指定的毫秒数;
    示例代码:
public class Demo {
public static void main(String[] args) {
    for (int i = 0; i < 10; i++) {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String str = sdf.format(date);
        System.out.println(str);
        //休息1秒

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
  }
}

03.第一章:线程创建线程的方式二实现Runnable接口及特点:

步骤:
1).自定义类实现Runnable接口;
2).重写run()方法;
3).启动线程:
    1).创建一个自定义类对象。
    2).创建一个Thread对象:并将自定义对象作为参数传给Thread的构造方法;
        public Thread (Runnable target)
    3).调用Thread对象的start()方法启动线程;
示例代码:
public class MyRunnable implements Runnable {
@Override
public void run() {
    for (int i = 0; i < 100; i++) {
        System.out.println("i = " + i);

    }
  }
}
public class Demo {
public static void main(String[] args) {
    MyRunnable myRun = new MyRunnable();
    Thread t1 = new Thread(myRun);

    t1.start();
    for (int k = 0; k < 100; k++) {
        System.out.println("k = " + k);

    }
  }
}

04.第一章:线程_两种方式的区别:

1).第一种需要继承自Thread--(由于Java是单继承,对子类就有一定的限制)
2).第二种实现Runnable接口--对于子类比较灵活(建议使用)

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

1).格式一:Thread的子类:
public class Demo {
public static void main(String[] args) {
    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).格式二:Runnable的子类:
public class Demo {
public static void main(String[] args) {

    //2.方式二:Runnable的子类
    new Thread(new Runnable() {
        @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);

    }
  }
}

06.第二章:线程安全_多线程的安全性问题:

1).Runnable接口方式的特点:

这里写图片描述

2).当Runable的run()中访问同一个变量的时候:

这里写图片描述

3).多个线程共同访问“同一个共享资源”时,这个共享资源就收到并发访问。会造成共享资源的最终结果不正确,不一致。
例如:上例和多线程售票(demo07)

07.第二章:线程安全线程同步解决多线程的安全性问题同步代码块:

1).同步代码块是用在:被并发访问的那个“方法中”;
2).语法:
    synchronized(锁对象){
        //同步代码
    }
3).锁对象:可以是任何对象;
4).示例代码:
public class Tickets implements Runnable {
private int tickets = 100;
private Object obj = new Object();
@Override
public void run() {
    while (true) {
        try {
            Thread.sleep(1);//目的:让其他线程有机会进入同步代码块取票
          }catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized (obj) {//窗口3(排队),窗口1(排队)
            //窗口2(关门--拥有锁)
            if(tickets > 0){
                System.out.println("给线程:" + Thread.currentThread().getName() + " 取走一张票:" + tickets);
                tickets--;
            }else{
                System.out.println("没票了,都回去吧!");
                break;
            }
        }
        //窗口2执行完毕(开门--释放锁)
    }
  }
}

08.第二章:线程安全线程同步解决多线程的安全性问题同步方法【常用】

public class Tickets implements Runnable {
private int tickets = 100;
private Object obj = new Object();
@Override
public void run() {
    while (true) {
        getTicket();
    }
}

public synchronized void getTicket() {//窗口3(等待),窗口1(等待)

        if(tickets > 0){//窗口2(加锁)
            System.out.println("给线程:" + Thread.currentThread().getName() + " 取走一张票:" + tickets);
            tickets--;
        }else{
            System.out.println("没票了,都回去吧!");
            System.exit(0);
        }
  }
}
1).说明:同步方法,可以是“普通方法”也可以是“静态方法”
 只要这个方法被多个线程同时访问,而程序需要每个线程必须执行完这个方法的所有步骤,才允许第二个线程进入,
 这种情况下就可以将这个方法声明为“同步方法”,可以保证方法内访问的数据的安全性。

09.第二章:线程安全_线程同步解决多线程的安全性问题_Lock锁:

public class Tickets implements Runnable {
private int tickets = 100;
private Lock lock = new ReentrantLock();

@Override
public void run() {
    while (true) {
        try {
            Thread.sleep(1);//目的:让其他线程有机会进入同步代码块取票
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        lock.lock();//(加锁)//窗口3(排队),窗口1(排队)
        try {
            //窗口2(关门--拥有锁)
            if (tickets > 0) {
                System.out.println("给线程:" + Thread.currentThread().getName() + " 取走一张票:" + tickets);
                tickets--;
            } else {
                System.out.println("没票了,都回去吧!");
                break;
            }
        }finally {
            lock.unlock();//窗口2执行完毕(开门--释放锁)
        }
    }
  }
}

10.第三章:线程状态_概述:

这里写图片描述

1).new状态:新建状态;
        MyThread t = new MyThread();
2).调用start():进入到:可运行状态;(交个操作系统,并不马上运行)
3).操作系统允许线程运行,但发现被访问的方法“被锁”:进入到:锁阻塞;
4).允许运行,被无限等待(Object --> wait()):进入到:无限等待(等待被唤醒)
5).允许运行,执行sleep(1000),进入到:计时等待状态
6).run运行完毕,进入到:被终止状态(变为垃圾,等待被回收)

11.第三章:线程状态_等待与唤醒:

public class Demo {
private static Object obj = new Object();
public static void main(String[] args) throws InterruptedException {
    new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (obj){
                for (int i = 0; i < 100; i++) {
                    System.out.println("i = " + i);
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (i == 20) {
                        try {
                            System.out.println("第一个线程开始等待,等待唤醒....");
                            obj.wait();//让当前持有这把锁的线程:等待(挂起--暂停),会释放锁
                                  System.out.println("第一个线程醒来啦,开始继续运行...");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }).start();


   Thread.sleep(1000);//休息1秒:目的:保证让第一个线程先执行,先获取锁
    new Thread(new Runnable() {
        @Override
        public void run() {
            synchronized (obj) {
                try {
                    System.out.println("第二个线程,等待5秒,唤醒第一个线程....");
                    Thread.sleep(1000 * 5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                obj.notify();
    //  obj.notifyAll();

            }
        }
    }).start();
  }
}

12.第三章:线程状态_状态图:

这里写图片描述

总结:
01.能够描述Java中多线程运行原理
当多个线程同时执行时:
1).每个线程都会有一个独立的“栈”空间;
2).这些“栈空间”都是在“同时运行”;
3).同时访问CPU;

02.能够使用继承类的方式创建多线程
class MyThread extends Thread{
public void run(){
..
}
}
main(){
MyThread t = new MyThread();
t.start();
}
03.能够使用实现接口的方式创建多线程
class MyRunnable implements Runnable{
public void run(){
}
}
main(){
MyRunnable run = new MyRunnable();
Thread t1 = new Thread(run);
t1.start();
}
04.能够说出实现接口方式的好处
子类可以更灵活,还可以继承其它类;
05.能够解释安全问题的出现的原因
多个线程共同访问同一个共享资源;
06.能够使用同步代码块解决线程安全问题
Object obj = new Object();
public void getTicket(){
synchronized(obj){
…//同步代码块
}
}
07.能够使用同步方法解决线程安全问题
public synchronized void getTicket(){
//同步代码
}

猜你喜欢

转载自blog.csdn.net/zzz323666/article/details/81809422
今日推荐