Java 多线程间通信及线程安全

 

多线程间通信的意义

 

    java中使用多线程操作同一数据时,需要考虑到线程间的通讯,否则会出现数据错误,程序死锁卡死的情况。所以多线程在操作同一数据时必须考虑其先后顺序以保证数据准确,且不会出现死锁的情况。多线程间的通信往往是通过等待机制来实现的,等待某一线程执行完毕或者放弃内存后,再启动另一线程。常使用的两种方法:synchronized和线程锁Lock。

 

synchronized的用法

 

1.通过Runnable对方法进行加锁

    该方法通过多个线程通过同一个Runnable来实现,在执行该Runnable被synchronized修饰的方法时,需要其他线程也需要执行被synchronized修饰的方法时进入等待状态,放弃内存,等到其他线程执行完毕后,再执行。

(1)对代码块进行加锁

public class ThreadTest {

    public static void main(String args[]){
        MRunnable mRunnable = new MRunnable();
        Thread thread1 = new Thread(mRunnable,"Thread1");
        Thread thread2 = new  Thread(mRunnable,"Thread2");
        thread1.start();
        thread2.start();
    }

    public static class MRunnable implements Runnable {
        static int count = 0;//计数
        @Override
        public void run() {
            synchronized(this){
                for(int i=0;i<5;i++){
                    System.out.println(Thread.currentThread().getName()+":"+count);
                    count++;
                }
            }
        }
    }
}

 

2.对指定对象进行加锁

    该方法是通过多个线程持有同一个对象的引用,在执行该对象被synchronized修饰的方法时,需要其他线程也需要执行被synchronized修饰的方法时进入等待状态,放弃内存,等到其他线程执行完毕后,再执行。

 

(1)对代码块进行加锁

public class ThreadTest {
    static Account account;
    public static void main(String args[]){
        account = new Account();
        Thread1 thread1 = new Thread1(account);
        Thread2 thread2 = new Thread2(account);
        thread1.start();
        thread2.start();
    }

    public static class Account{
        int count = 0;//计数
    }

    public static class Thread1 extends Thread{
        Account object;
        public Thread1(Account object){
            this.object = object;
        }
        @Override
        public void run() {
            super.run();
            synchronized(object){
                for(int i=0;i<5;i++){
                    System.out.println("Thread1:"+object.count);
                    object.count++;
                }
            }
        }
    }

    public static class Thread2 extends Thread{
        Account object;
        public Thread2(Account object){
            this.object = object;
        }
        @Override
        public void run() {
            super.run();
            synchronized(object){
                for(int i=0;i<5;i++){
                    System.out.println("Thread2:"+object.count);
                    object.count++;
                }
            }
        }
    }
}

 

(2)对方法进行加锁

 

public class ThreadTest {
    static Account account;
    public static void main(String args[]){
        account = new Account();
        Thread1 thread1 = new Thread1(account);
        Thread2 thread2 = new Thread2(account);
        thread1.start();
        thread2.start();
    }

    public static class Account{
        public synchronized void print1(){
            for(int i=0;i<5;i++){
                System.out.println("方法1");
            }
        }
        public synchronized void print2(){
            for(int i=0;i<5;i++){
                System.out.println("方法2");
            }
        }
    }

    public static class Thread1 extends Thread{
        Account object;
        public Thread1(Account object){
            this.object = object;
        }
        @Override
        public void run() {
            super.run();
            object.print1();
        }
    }

    public static class Thread2 extends Thread{
        Account object;
        public Thread2(Account object){
            this.object = object;
        }
        @Override
        public void run() {
            super.run();
            object.print2();
        }
    }
}

 

输出结果(等待某一线程执行完毕后剩下的线程才开始执行)

 

Java 多线程间通信及线程安全_第1张图片

 

 

Lock的用法

 

(1)Lock配合synchronized使用

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadTest {
    static Lock lock;
    static int count = 0;
    public static void main(String args[]){
        lock = new ReentrantLock();
        Thread1 thread1 = new Thread1(lock);
        Thread2 thread2 = new Thread2(lock);
        thread1.start();
        thread2.start();
    }

    public static class Thread1 extends Thread{
        Lock lock;
        public Thread1(Lock lock){
            this.lock = lock;
        }
        @Override
        public void run() {
            super.run();
            synchronized (lock){
                for(int i=0;i<5;i++){
                    System.out.println("Thread1:"+count);
                    count++;
                }
            }
        }
    }

    public static class Thread2 extends Thread{
        Lock lock;
        public Thread2(Lock lock){
            this.lock = lock;
        }
        @Override
        public void run() {
            super.run();
            synchronized (lock){
                for(int i=0;i<5;i++){
                    System.out.println("Thread2:"+count);
                    count++;
                }
            }
        }
    }
}

 

(2)Lock通过lock和unlock实现多线程通信(该方法较为常用)

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ThreadTest {
    static Lock lock;//当然这里我们还可以使用 ReentrantLock重入锁
    static int count = 0;
    public static void main(String args[]){
        lock = new ReentrantLock();
        Thread1 thread1 = new Thread1(lock);
        Thread2 thread2 = new Thread2(lock);
        thread1.start();
        thread2.start();
    }

    public static class Thread1 extends Thread{
        Lock lock;
        public Thread1(Lock lock){
            this.lock = lock;
        }
        @Override
        public void run() {
            lock.lock();
            for(int i=0;i<5;i++){
                System.out.println("Thread1:"+count);
                count++;
            }
            lock.unlock();
        }
    }

    public static class Thread2 extends Thread{
        Lock lock;
        public Thread2(Lock lock){
            this.lock = lock;
        }
        @Override
        public void run() {
            lock.lock();
            for(int i=0;i<5;i++){
                System.out.println("Thread2:"+count);
                count++;
            }
            lock.unlock();
        }
    }
}

 

Volatile关键字的使用

    volatile能保证指令的可见性,也能禁止重排序,所以在多个线程使用到这个关键字修饰的变量时,能够在它发生改变时,其他线程都能接收到它的改变,这样也保证了线程的安全。

    volatile的使用条件:(1)对该变量的写操作不会依赖于当前值(不能是自增或自减操作)(2)该变量没有包含在具有其他变量的不变式中

 new Thread(new Runnable() {
            volatile boolean isRunning;//通过volatile修饰后可以避免出现因为boolean变量出现死锁
            @Override
            public void run() {
                while (isRunning){
                    System.out.println("is Running");
                }
            }
        }).start();

 

通过Interrupt安全的终止掉线程

Thread mThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!Thread.currentThread().isInterrupted()){//通过是否被中断的标志来循环线程
                    System.out.println("is Running");
                }
            }
        });
        mThread.start();
        mThread.interrupt();

 

写在末尾

    不建议使用轮寻的方式来实现线程间的通讯,因为轮寻的方式线程并没有真正意义上的不执行,所以其所占用内存不会被释放,所以该方式不可取。

你可能感兴趣的:(Java)