17.2.Java语言线程同步和线程安全

版权声明:原创,转载请说明,谢谢! https://blog.csdn.net/ajia5216/article/details/82701970

线程

1.多线程原理

       主线程在main()方法被调用时创建,当Thread类的对象创建并调用start()方法线程启动,此时主线程和自定义线程同时(交错)运行,整个应用在多线程下运行

       栈内存中,其实每一个执行线程都有一片自己所属的栈内存空间

2.Thread类

       a).构造方法;

              Thread():无参构造方法

              Thread(String name):线程名称

              Thread(Runnable target):

              Thread(Runnablr target,String name):

       b)常用方法:

    public String getName() :获取当前线程名称。

    public void start() :启动线程

    public void run() :此线程要执行的任务在此处定义代码。

    public static void sleep(long millis) :(暂时停止执行)。

public static Thread currentThread() :返回对当前正在执行的线程对象的引用。

3.实现Runnable接口创建线程

定义是实现接口的类,创建对象,调用Thread(Runnable target)构造方法创建线程。然后调用start()启动线程

示例:

public class MyRunnable implements Runnable {

    @Override

    public void run() {

        System.out.println("Runnable");

    }

}

调用

public class Runnable接口创建线程 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t = new Thread(myRunnable);
        t.start();
    }
}

tips:Runnable对象仅仅作为Thread对象的target,Runnable实现类里包含的run()方法仅作为线程执行体。而实际的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run()方法。

4.Thread和Runnable的区别

1.第一种方式需要子类继承自Thread。由于Java是“单继承”,所以对子类造成了不方便。

2.第二种方式需要子类实现Runnable接口。Java允许一个类同时实现多个接口,所以对于子类就比较方便。(建议使用)

5.匿名内部类的方式实现线程的创建

              Thread的匿名内部子类

new Thread(){

    @Override

    public void run() {

        System.out.println("as");

    }

}.start();

 

              Runnable的匿名内部子类

new Thread(new Runnable() {

    @Override

    public void run() {

        System.out.println("创建了一个指定目标有名称的新线程");

    }

},"线程").start();

线程安全

当多个线程同时操作一个全局变量,如果对这个变量只有读,那么线程是安全的,如果多个线程对一个全局变量有写的操作,那么线程不安全,因为线程是先取出变量的值,然后读写之后再给变量赋最终值,这样在实际应用中往往会发生错误,得不到预期值。

1.线程同步

       当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题。Java中提供了同步机制(synchronized)来解决。

       同步机制就是当一个线程在读写共享资源时,别的线程只能等待这个线程访问结束

有三种方式

1.同步代码块。2.同步方法。3.锁机制。

2.同步代码块

       格式:

synchronized (同步锁){ 需要同步操作的代码 }

       1.这个锁是一个对象,可以使任何类型(对象的同步锁就相当于给对象做了一个标记)

       2.多个线程对象必须使用同一把锁

       在任何时候,最多有一个线程拥有同步锁,谁有同步锁就可以进入代码块,其他只能等

       示例:

public class 同步锁 extends Thread{

    Object obj = new Object();

    @Override

    public void run() {

        synchronized (obj){

            System.out.println("拥有同步锁,进入代码块");

            //...可以对共享资源读写

        }

    }

}

3.同步方法

       使用synchronized修饰的方法,一个线程在执行此方法时,其他方法在外等待

对于非static方法,同步锁就是this。

对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。

public class 同步锁 extends Thread{

    Object obj = new Object();

    @Override

    public synchronized void run() {

            System.out.println("拥有同步锁,进入代码块");

            //...可以对共享资源读写

    }

}

4.锁机制、

java.util.concurrent.locks.Lock(接口),同步锁同步代码块有的功能lock锁都有,而且更强大,更能体现面向对象的特性

用法:

public void lock() :加同步锁。

public void unlock() :释放同步锁

       示例:

public class 同步锁 extends Thread{
    Lock lock = new ReentrantLock();
    @Override
    public  void run() {
        lock.lock();//枷锁
        System.out.println("拥有同步锁,进入代码块");
        //...可以对共享资源读写
        lock.unlock();//解锁
    }
}

 

线程状态

1.六种状态线程:

1).新建:MyThread t = new MyThread();

       2).可运行(待运行):t.start();

       3).锁阻塞:运行后调用某个方法,但此方法已被其他线程“锁上了”。

       4).无限等待:运行后,调用了“锁的wait()”方法。

       5).计时等待:运行后,被调用了sleep(...);

       6).被终止:run()方法正常运行完毕。

2.等待与唤醒

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

        new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    synchronized (obj) {
                        System.out.println("i = " + i);
                        if (i == 20) {
                            try {
                                //当前持有这把锁的线程,开始无限等待....
                                System.out.println("线程开始进入无限等待....");
                                obj.wait();
                                System.out.println("线程被唤醒.....");
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }.start();

        System.out.println("主线程休息2秒...目的让前面的线程先运行...");
        Thread.sleep(2000);

        new Thread(){
            @Override
            public void run() {
                synchronized (obj) {
                    //唤醒所有obj锁上等待的线程
                    System.out.println("我要唤醒所有等待的线程....");
                    obj.notifyAll();
                }
            }
        }.start();
    }
}

 

3.状态图:

猜你喜欢

转载自blog.csdn.net/ajia5216/article/details/82701970
今日推荐