欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

线程,同步

程序员文章站 2022-05-04 17:10:18
...

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(){
//同步代码
}