线程同步丶java教程网,java线程同步的几种方法

发布时间:2022-11-19

本文目录一览:

  1. Java中线程怎么同步
  2. 简单写出线程同步的方法(java)
  3. Java如何实现多线程同步?
  4. Java线程同步的方法
  5. java中线程同步的几种方法

Java中线程怎么同步

  1. 使用线程类自带的join方法,将子线程加入到主线程,在子线程执行完之后,再执行主线程逻辑。 例如:
    public static void joinDemo() throws InterruptedException {
        System.out.println("=========Test with join=====");
        JoinWorker worker1 = new JoinWorker("worker1");
        JoinWorker worker2 = new JoinWorker("worker2");
        worker1.start();
        worker2.start();
        worker1.join();
        worker2.join();
        doSuperWork();
    }
    
  2. 使用JDK的并发包中的CountDownLatch类,每个线程调用其countDown方法使计数器减1,主线程调用await方法阻塞等待,直到计数器为0时继续执行。 定义子线程:
    static class CountDownLatchWorker extends Thread {
        String workerName;
        CountDownLatch latch;
        public CountDownLatchWorker(String workerName, CountDownLatch latch) {
            this.workerName = workerName;
            this.latch = latch;
        }
        public void run() {
            System.out.println("Sub Worker " + workerName + " do work begin at " + sdf.format(new Date()));
            new ThreadWaitDemo().doSomeWork();
            System.out.println("Sub Worker " + workerName + " do work complete at " + sdf.format(new Date()));
            latch.countDown();
        }
    }
    
    主线程中调用await方法阻塞等待,直到所有线程完成:
    public static void countDownLatchDemo() throws InterruptedException {
        System.out.println("=========Test with CountDownLatch=====");
        CountDownLatch latch = new CountDownLatch(2);
        CountDownLatchWorker worker1 = new CountDownLatchWorker("worker1", latch);
        CountDownLatchWorker worker2 = new CountDownLatchWorker("worker2", latch);
        worker1.start();
        worker2.start();
        // 主线程阻塞等待
        latch.await();
        doSuperWork();
    }
    
  3. 使用JDK并发包CyclicBarrier,类似于CountDownLatch,不同的是await()方法每被调用一次,计数便会减少1,并阻塞当前线程。当计数减至0时,阻塞解除,所有在此CyclicBarrier上阻塞的线程开始运行。 示例:
    public static void cyclicBarrierDemo() throws InterruptedException, BrokenBarrierException {
        System.out.println("=========Test with CyclicBarrier=====");
        CyclicBarrier cb = new CyclicBarrier(2, new Runnable() {
            public void run() {
                new ThreadWaitDemo().doSuperWork();
            }
        });
        ExecutorService executor = Executors.newFixedThreadPool(2);
        CyclicBarrierWorker worker1 = new CyclicBarrierWorker("worker1", cb);
        CyclicBarrierWorker worker2 = new CyclicBarrierWorker("worker2", cb);
        executor.execute(worker1);
        executor.execute(worker2);
        executor.shutdown();
    }
    
  4. 使用JDK并发包中的Executors框架,通过ExecutorServiceinvokeAll方法批量执行多个线程,在invokeAll方法结束之后,再执行主线程其他业务逻辑。 示例:
    public static void callableDemo() throws InterruptedException {
        System.out.println("=========Test with Callable=====");
        List<Callable<Integer>> callList = new ArrayList<>();
        ExecutorService exec = Executors.newFixedThreadPool(2);
        callList.add(new Callable<Integer>() {
            public Integer call() throws Exception {
                System.out.println("Sub Worker worker1 do work begin at " + sdf.format(new Date()));
                new ThreadWaitDemo().doSomeWork();
                System.out.println("Sub Worker worker1 do work complete at " + sdf.format(new Date()));
                return 0;
            }
        });
        callList.add(new Callable<Integer>() {
            public Integer call() throws Exception {
                System.out.println("Sub Worker worker2 do work begin at " + sdf.format(new Date()));
                new ThreadWaitDemo().doSomeWork();
                System.out.println("Sub Worker worker2 do work complete at " + sdf.format(new Date()));
                return 0;
            }
        });
        exec.invokeAll(callList);
        exec.shutdown();
        doSuperWork();
    }
    
  5. 轮询线程状态:主线程创建一个线程列表,将每个子线程保存到列表中,然后定期轮询列表中子线程状态,当所有线程都完成之后,再执行主线程逻辑。

简单写出线程同步的方法(java)

/**
 * Java线程:线程的同步
 *
 * @author leizhimin 2009-11-4 11:23:32
 */
public class Test {
    public static void main(String[] args) {
        User u = new User("张三", 100);
        MyThread t1 = new MyThread("线程A", u, 20);
        MyThread t2 = new MyThread("线程B", u, -60);
        MyThread t3 = new MyThread("线程C", u, -80);
        MyThread t4 = new MyThread("线程D", u, -30);
        MyThread t5 = new MyThread("线程E", u, 32);
        MyThread t6 = new MyThread("线程F", u, 21);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
        t6.start();
    }
}
class MyThread extends Thread {
    private User u;
    private int y = 0;
    MyThread(String name, User u, int y) {
        super(name);
        this.u = u;
        this.y = y;
    }
    public void run() {
        u.oper(y);
    }
}
class User {
    private String code;
    private int cash;
    User(String code, int cash) {
        this.code = code;
        this.cash = cash;
    }
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    /**
     * 业务方法
     * @param x 添加x万元
     */
    public synchronized void oper(int x) {
        try {
            Thread.sleep(10L);
            this.cash += x;
            System.out.println(Thread.currentThread().getName() + "运行结束,增加“" + x + "”,当前用户账户余额为:" + cash);
            Thread.sleep(10L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    @Override
    public String toString() {
        return "User{" +
                "code='" + code + '\'' +
                ", cash=" + cash + '}';
    }
}

输出结果:

线程A运行结束,增加“20”,当前用户账户余额为:120
线程F运行结束,增加"21",当前用户账户余额为:141
线程E运行结束,增加"32",当前用户账户余额为:173
线程C运行结束,增加"-80",当前用户账户余额为:93
线程B运行结束,增加"-60",当前用户账户余额为:33
线程D运行结束,增加"-30",当前用户账户余额为:3
Process finished with exit code 0

反面教材(不同步的情况): 去掉oper(int x)方法的synchronized修饰符,运行结果如下:

线程A运行结束,增加"20",当前用户账户余额为:61
线程D运行结束,增加"-30",当前用户账户余额为:63
线程B运行结束,增加"-60",当前用户账户余额为:3
线程F运行结束,增加"21",当前用户账户余额为:61
线程E运行结束,增加"32",当前用户账户余额为:93
线程C运行结束,增加"-80",当前用户账户余额为:61
Process finished with exit code 0

很显然,上面的结果是错误的,导致错误的原因是多个线程并发访问了竞争资源u,并对u的属性做了改动。可见同步的重要性。 注意: 通过前文可知,线程退出同步方法时将释放掉方法所属对象的锁,但还应该注意的是,同步方法中还可以使用特定的方法对线程进行调度。这些方法来自于java.lang.Object类。

  • void notify():唤醒在此对象监视器上等待的单个线程。
  • void notifyAll():唤醒在此对象监视器上等待的所有线程。
  • void wait():导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。
  • void wait(long timeout):导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量。
  • void wait(long timeout, int nanos):导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。

Java如何实现多线程同步?

  • 解决方案-1:设置3把锁,然后把锁们应用到所有线程中(涉及到synchronizedwaitnotify等,嫌麻烦,略)。
  • 解决方案-2:设置3个全局共享的信号标记(信号灯)+ 3子线程分别占用标记1、2、3 + 主线程轮询/等待(简洁明快,推荐)。
static boolean t1_done = false;
static boolean t2_done = false;
static boolean t3_done = false;
// t1------run() { ............ ; t1_done = true; }
// t2、3: 同理,略
main() {
    // 启动t1;
    // 启动t2;
    // 启动t3;
    // 轮询 or 等待
    while (true)
        if (t1_done && t2_done && t3_done) break;
        else Thread.yield();
    // 或 Thread.sleep(xxxx) ----若子线程运行超过100ms以上,应予考虑
    // 轮询结束,主线程继续工作
}

have fun

Java线程同步的方法

等待唤醒机制

  • wait():让线程等待。将线程存储到一个线程池中。
  • notify():唤醒被等待的线程。通常都唤醒线程池中的第一个。让被唤醒的线程处于临时阻塞状态。
  • notifyAll():唤醒所有的等待线程。将线程池中的所有线程都唤醒,让它们从冻结状态转到临时阻塞状态。 这三个方法用于操作线程,可是定义在了Object类中,为什么呢? 因为,这三个方法在使用时,都需要定义在同步中,要明确这些方法所操作的线程所属于锁。 简单说,在A锁被wait的线程,只能被A锁的notify方法唤醒。 所以必须要表示waitnotify方法所属的锁对象,而锁对象可以是任意的对象。 可以被任意的对象调用的方法肯定定义在Object类中。 注意: 等待唤醒机制,通常都用在同步中,因为需要锁的支持。而且必须要明确waitnotify所作用的锁对象。

JDK1.5后的锁

在JDK1.5版本之后,出现了一些新的特性,将原来的线程进行了改良。 在java.util.concurrent.locks包中提供了一个接口Lock,替代了synchronized

  • synchronized:使用的是锁操作是隐式的。
  • Lock接口:使用的锁操作是显式的。 由两个方法来完成:
  • lock():获取锁。
  • unlock():释放锁。 还有一个对象Condition。 该对象的出现替代了Object中的waitnotifynotifyAll这些操作监视器的方法。 替代后的方式:awaitsignalsignalAll

java中线程同步的几种方法

线程同步主要有以下几种方法(示例中是实现计数的功能):

  1. 同步方法:即使用synchronized关键字修饰方法。
    public synchronized void add(int c) { ... }
    
  2. 同步代码块:即有synchronized关键字修饰的语句块。
    public void addAndGet(int c) {
        synchronized (this) {
            count += c;
        }
    }
    
  3. 使用特殊域变量(volatile)实现线程同步:该方法不能保证绝对的同步。
    private volatile int count = 0;
    
  4. 使用锁实现线程同步
    private Lock lock = new ReentrantLock();
    public void add(int c) {
        lock.lock(); // 上锁
        try {
            count += c;
        } finally {
            lock.unlock(); // 解锁
        }
    }
    
  5. 使用原子变量实现线程同步:在java.util.concurrent.atomic包中提供了创建原子类型变量的工具类。
    private AtomicInteger count = new AtomicInteger(1);
    public void add(int c) {
        count.addAndGet(c);
    }
    
  6. 使用局部变量实现线程同步:如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。 ThreadLocal类的常用方法:
    • new ThreadLocal<T>():创建一个线程本地变量。
    • get():返回此线程局部变量的当前线程副本中的值。
    • initialValue():返回此线程局部变量的当前线程的"初始值"。
    • set(T value):将此线程局部变量的当前线程副本中的值设置为value示例代码:
    private static ThreadLocal<Integer> count = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return 1;
        }
    };
    public void add(int c) {
        count.set(count.get() + c);
    }
    
  7. 使用阻塞队列实现:例如LinkedBlockingQueue,具体使用可百度LinkedBlockingQueue的用法或查看Java文档。