本文共 8234 字,大约阅读时间需要 27 分钟。
在Java中,守护线程是一种特殊的线程,它不会自动终止,只要主线程还在运行,守护线程也会一直运行。常见的用途包括资源管理、错误处理以及与其他线程协作。
线程在其执行过程中会经历几个关键状态:
线程的状态可以通过调用getState()
方法来查看。
当多条语句操作同一个线程共享数据时,一个线程中的多条语句可能只执行了一部分,而另一个线程却参与进来,导致共享数据出现错误。这种现象也就是所谓的“线程安全问题”。
为了避免线程安全问题,可以采取以下措施:
Lock
接口来实现同步,Lock
接口支持多级锁和fairness策略。在实现Runnable
接口时,共享变量的选择很重要:
this
关键字:如果线程类是通过Runnable
接口实现的,this
关键字可以用来获取共享数据。Thread
类创建的,那么this
关键字不再是唯一的对象,此时需要使用类对象作为共享数据。操作共享数据的代码需要被同步:
this
对象。Thread
类创建的线程,同步方法需要声明为静态方法,否则会出现线程不安全的问题。单例模式的实现需要确保在多个线程中只有一个实例被创建。传统的单例模式可能会因为线程竞态条件而导致线程不安全。
通过引入双重锁(Double-Checked Locking)可以实现懒汉式单例模式的线程安全:
public class Singleton { private static Singleton instance = null; private Singleton() { // 初始化代码 } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; }}
死锁是指两个或多个线程在等待对方完成的操作中都被阻塞,导致无法继续执行的状态。
public class DeadLock { public static void main(String[] args) { StringBuffer s1 = new StringBuffer(); StringBuffer s2 = new StringBuffer(); new Thread() { @Override public void run() { synchronized (s1) { System.out.println("成功锁住s1"); s1.append("a"); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (s2) { s1.append("b"); s2.append("2"); } System.out.println(s1); System.out.println(s2); } } }.start(); new Thread(new Runnable() { @Override public void run() { synchronized (s2) { System.out.println("成功锁住s2"); s1.append("c"); s2.append("3"); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (s1) { s1.append("d"); s2.append("4"); } System.out.println(s1); System.out.println(s2); } } }).start(); }}
Lock
接口提供了更加灵活的同步机制,与传统的synchronized
关键字相比,具有以下优势:
Lock
接口可以更高效地分配锁。class Account { private double balance; public Account(double balance) { this.balance = balance; } public void deposit(double money) { if (money >= 0) { balance += money; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "存钱,当前余额为:" + balance); } }}class AccountThread extends Thread { private static Lock lock = new ReentrantLock(); private Account account; public AccountThread(Account account) { this.account = account; } @Override public void run() { try { lock.lock(); for (int i = 0; i < 5; i++) { account.deposit(1000); } } finally { lock.unlock(); } }}public class DepositMoneyTest { public static void main(String[] args) { Account account = new Account(0); AccountThread t1 = new AccountThread(account); AccountThread t2 = new AccountThread(account); t1.setName("1"); t2.setName("2"); t1.start(); t2.start(); }}
线程通信是多线程编程中非常重要的一部分,wait()
和notify()
方法提供了线程之间的等待和通知机制。
public class Number implements Runnable { private int number = 1; private Object object = new Object(); @Override public void run() { while (true) { synchronized (object) { object.notify(); if (number <= 100) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ": " + number); number++; try { object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { break; } } } } public static void main(String[] args) { Number number = new Number(); Thread thread1 = new Thread(number); Thread thread2 = new Thread(number); thread1.setName("1->"); thread2.setName("2->"); thread1.start(); thread2.start(); }}
生产者线程的主要任务是向共享数据结构中添加数据。以下是一个简单的生产者线程的实现:
public class Product { private Object lock = new Object(); private int goodsCount = 0; public void produce() { synchronized (lock) { System.out.println("生产者线程开始生产"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } goodsCount++; System.out.println("共有商品:" + goodsCount); System.out.println("生产者线程结束"); } }}
消费者线程的主要任务是从共享数据结构中取走数据。以下是一个简单的消费者线程的实现:
public class Consumer { private Object lock = new Object(); public void consume() { synchronized (lock) { System.out.println("消费者线程开始消费"); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("已消费的商品数量为:" + Goods.getGoodsCount()); System.out.println("消费者线程结束"); } }}
Callable接口允许线程执行一个返回值的任务,并且可以通过FutureTask
来实现。
public class CallableTest implements Callable { @Override public Object call() throws Exception { int sum = 0; for (int i = 1; i <= 100; i++) { if (i % 2 == 0) { System.out.println(i); sum += i; } } return sum; } public static void main(String[] args) throws InterruptedException, ExecutionException { CallableTest callable = new CallableTest(); FutureTask futureTask = new FutureTask(callable); new Thread(futureTask).start(); Integer sum = (Integer) futureTask.get(); System.out.println("100内偶数总和为:" + sum); }}
线程池提供了一个更高效的方式来执行多线程任务,JDK5.0新增了ExecutorService
和Executors
类。
public class ThreadPoolTest { public static void main(String[] args) { ExecutorService service = Executors.newFixedThreadPool(10); service.execute(new NumberThread()); service.execute(new NumberThread1()); service.shutdown(); } static class NumberThread implements Runnable { @Override public void run() { for (int i = 1; i <= 100; i++) { if (i % 2 == 0) { System.out.println(Thread.currentThread().getName() + ":" + i); } } } } static class NumberThread1 implements Runnable { @Override public void run() { for (int i = 1; i <= 100; i++) { if (i % 2 != 0) { System.out.println(Thread.currentThread().getName() + ":" + i); } } } }}
通过以上内容,可以了解Java多线程编程的核心知识点和实际应用场景。
转载地址:http://txgh.baihongyu.com/