黑马程序员 Java多线程

本文详细介绍了Java多线程编程的核心概念,包括线程的创建、线程的生命周期、线程间通信、同步机制(如synchronized关键字、wait和notify方法)、线程的优先级、线程池的使用等。同时,通过一个具体的多线程循环练习题展示了如何在Java中实现复杂的线程交互。此外,文章还讨论了捕获线程死亡事件的方法,提供了实际的代码示例。

------- android培训java培训、期待与您交流! ----------

1可以使用静态方法Thread.currentTread().getName()来获取当前线程的线程名
2如果我们对某个线程对象在启动之前调用了setDaemon(true)方法,这个线程就变成了后台线程,对JAVA程序来说,只要还有一个前台线程在运行,这个进程就不会结束,如果一个进程中只有后台线程运行,这个进程就会结束,setDaemon方法首先调用该线程的checkAccess方法,且不带任何参数。这可能抛出 SecurityException(在当前线程中)如果指示线程没有处于请求操作所要求的适当状态还会抛出IllegalThreadStateException异常
注:
checkAccess public final void checkAccess()
判定当前运行的线程是否有权修改该线程。如果有安全管理器,则调用其 checkAccess方法,并将该线程作为其参数。这可能导致抛出 SecurityException -如果不允许当前线程访问该线程
3线程A调用线程Bjoin()方法后,线程A就会等线程B结束后才继续运行,join()等待该线程终止,join(long millis)等待该线程终止的时间最长为 millis毫秒。join(long millis,int nanos)等待该线程终止的时间最长为 millis毫秒 + nanos 纳秒。
注:
join
public final void join(long millis,  int nanos) throwsInterruptedException
等待该线程终止的时间最长为 millis毫秒 + nanos 纳秒。

参数:
millis -
以毫秒为单位的等待时间。
nanos -
要等待的 0-999999附加纳秒。 
抛出: 
IllegalArgumentException -
如果 millis值为负,则 nanos的值不在 0-999999范围内。 
InterruptedException -
如果任何线程中断了当前线程。当抛出该异常时,当前线程的中断状态被清除。

public final void join()  throwsInterruptedException等待该线程终止。

抛出: 
InterruptedException -
如果任何线程中断了当前线程。当抛出该异常时,当前线程的中断状态被清除。
public final void join(long millis) throws InterruptedException
等待该线程终止的时间最长为 millis毫秒。超时为 0 意味着要一直等下去。

参数:
millis -
以毫秒为单位的等待时间。 
抛出: 
InterruptedException -
如果任何线程中断了当前线程。当抛出该异常时,当前线程的中断状态被清除。
4创建多线程有两种方式,一种是继承Thread类,另一种是实现Runnable接口,我觉得如果能使用接口就选择接口,因为这确实能很好地体现出面向对象的设计思想
5sleep
public static void sleep(long millis) throws InterruptedException
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。该线程不丢失任何监视器的所属权。

参数:
millis -
以毫秒为单位的休眠时间。 
抛出: 
InterruptedException -
如果任何线程中断了当前线程。当抛出该异常时,当前线程的中断状态被清除。
6当在线程A中启动了线程B后,CPU不一定立刻跳转到线程B,它很可能要再在线程A中执行一会儿了再跳转到线程B,为了让CPU立刻跳转到线程B,我们可以在启到了线程B后在线程A中调用Tread类中的sleep方法让线程A等待一会儿
7synchronized的四种用法:
1)方法声明时使用,放在范围操作符(public等)之后,返回类型声明(void等)之前,即一次只能有一个线程进入该方法,其他线程想在此时调用该方法,只能排队等候,当前线程(就是在synchronized方法内部的线程)执行完该方法后,别的线程才能进入。
2)对某一代码块使用,synchronized后跟括号,括号里是变量,这样一次只有一个线程进入该代码块。
3synchronized后面括号里是一对象,如果线程进入,则得到对象锁,那么别的线程在该类所有对象上的任何操作都不能进行。在对象级使用锁通常是一种比较粗糙的方法。为什么要将整个对象都上锁,而不允许其他线程短暂地使用对象中其他同步方法来访问共享资源?如果一个对象拥有多个资源,就不需要只为了让一个线程使用其中一部分资源,就将所有线程都锁在外面。由于每个对象都有锁,所以可以使用虚拟对象来上锁.
  (4)synchronized
后面括号里是类,如果线程进入,则线程在该类中所有操作不能进行,包括静态变量和静态方法,实际上,对于含有静态方法和静态变量的代码块的同步,我们通常用4来加锁
以上4种之间的关系:
锁是和对象相关联的,每个对象有一把锁,为了执行synchronized语句,线程必须能够获得synchronized语句中表达式指定的对象锁,一个对象只有一把锁,被一个线程获得之后它就不再拥有这把锁,线程在执行完synchronized语句后,将获得锁交还给对象。在方法前面加上synchronized修饰符即可以将一个方法声明为同步化方法。同步化方法在执行之前获得一个锁。如果这是一个类方法,那么获得的锁是和声明方法的类相关的Class类对象的锁,如果这个一个实例方法,那么此锁是this对象的锁。
8public final void wait(long timeout) throwsInterruptedException
参数:
timeout -
要等待的最长时间(以毫秒为单位)。 
抛出: 
IllegalArgumentException -
如果超时值为负。 
IllegalMonitorStateException -
如果当前线程不是此对象监视器的所有者。 
InterruptedException -
如果在当前线程等待通知之前或者正在等待通知时,任何线程中断了当前线程。在抛出此异常时,当前线程的中断状态被清除。
public final void wait(long timeout, int nanos) throwsInterruptedException
参数:
timeout -
要等待的最长时间(以毫秒为单位)。
nanos -
额外时间(以毫微秒为单位,范围是 0-999999)。 
抛出: 
IllegalArgumentException -
如果超时值是负数,或者毫微秒值不在 0-999999范围内。 
IllegalMonitorStateException -
如果当前线程不是此对象监视器的所有者。 
InterruptedException -
如果在当前线程等待通知之前或者正在等待通知时,任何线程中断了当前线程。在抛出此异常时,当前线程的中断状态被清除。
public final void wait()
(等价于wait(0)throws InterruptedException
抛出: 
IllegalMonitorStateException -
如果当前线程不是此对象监视器的所有者。 
InterruptedException -
如果在当前线程等待通知之前或者正在等待通知时,任何线程中断了当前线程。在抛出此异常时,当前线程的中断状态被清除。
9public final void notify()唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。
直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。

此方法只应由作为此对象监视器的所有者的线程来调用。通过以下三种方法之一,线程可以成为此对象监视器的所有者:

通过执行此对象的同步实例方法。 
通过执行在此对象上进行同步的 synchronized语句的正文。 
对于 Class 类型的对象,可以通过执行该类的同步静态方法。 
一次只能有一个线程拥有对象的监视器。 
抛出: 
IllegalMonitorStateException -
如果当前线程不是此对象监视器的所有者。
10public final void notifyAll()唤醒在此对象监视器上等待的所有线程。线程通过调用其中一个 wait方法,在对象的监视器上等待。
直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。

此方法只应由作为此对象监视器的所有者的线程来调用。有关线程能够成为监视器所有者的方法的描述,请参阅 notify方法。 
抛出: 
IllegalMonitorStateException -
如果当前线程不是此对象监视器的所有者。

11练习题:子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次,接着再回到主线程又循环100次,如此循环50次,请写出程序

这道题我和张老师做得不一样,他是把主线程循环的100次和和子线程循环10次写成两个方法的,而我是写成一个方法通过传递所需循环的次数的参数来实现的

1.    public class ThreadTest {  

2.        public static void main(String[] args) {  

3.            final ThreadCommunication threadCommunication = new ThreadCommunication();  

4.            new Thread(new Runnable() {  

5.                @Override  

6.                public void run() {  

7.                    for (int i = 1; i <= 50; i++) {  

8.                        threadCommunication.print(10);  

9.                    }  

10.              }  

11.          }).start();  

12.          for (int i = 1; i <= 50; i++) {  

13.              threadCommunication.print(100);  

14.          }  

15.      }  

16.  }  

17.    

18.  class ThreadCommunication {  

19.      private int temp;  

20.      private boolean flag = true;  

21.    

22.      public synchronized void print(int num) {  

23.          while (!flag & 10 == num || flag & 100 == num) {  

24.              try {  

25.                  this.wait();  

26.              } catch (InterruptedException e) {  

27.                  e.printStackTrace();  

28.              }  

29.          }  

30.          for (temp = num; temp > 0; temp--) {  

31.              System.out.println(Thread.currentThread().getName() + temp);  

32.          }  

33.    

34.          flag = false == flag ? true : false;  

35.          this.notify();  

36.      }  

37.  }  

12张老师在视频中提到了捕获线程死亡事件,这个在课堂上没有找到方法,我就好奇的也试着看了看API,这是我个人的捕获线程死亡事件做法,也不知道是不是这么一回事

1.    public class CatchThreadTerminate {  

2.        public static void main(String[] args) {  

3.            final Thread thread = new Thread(new Runnable() {  

4.                @Override  

5.                public void run() {  

6.                    for (int i = 0; i <= 100; i++) {  

7.                        System.out.println(i);  

8.                    }  

9.                }  

10.          });  

11.          thread.start();// 启动thread线程  

12.          new Thread(new Runnable() {  

13.    

14.              @Override  

15.              public void run() {  

16.                  while (true) {  

17.                      if (Thread.State.TERMINATED == thread.getState()) {  

18.                          /* 如果thread线程死亡则打印下面这条语句 */  

19.                          System.out.println("线程死亡");  

20.                          break;// 已经捕获到thread的死亡,结束监听  

21.                      }  

22.                  }  

23.              }  

24.    

25.          }).start();// 启动一个线程来监听thread线程是否是亡  

26.      }  

27.  }  

 

### 关于Java多线程编程的拓展学习资料 #### 一、线程安全与同步机制 在Java中,为了确保多个线程能够安全地访问共享资源而不发生数据竞争,通常会采用多种方式来实现线程间的同步控制。上述提到的`VolatileAtomicThread`类展示了通过`synchronized`关键字锁定对象实例的方式来进行同步处理[^1]。 ```java public class VolatileAtomicThread implements Runnable { private int count = 0; private final Object lockObj = new Object(); @Override public void run() { for (int i = 0; i < 100; ++i) { synchronized (lockObj){ count++; System.out.println("count =======>> " + count); } } } } ``` 除了使用`synchronized`外,还可以利用更高效的原子操作(如`AtomicInteger`),以及显式的锁机制(如`ReentrantLock`)。这些方法可以减少不必要的阻塞开销并提高程序性能。 #### 二、并发工具包中的高级特性 Java提供了丰富的并发工具库——`java.util.concurrent`,其中包含了诸如`CountDownLatch`, `CyclicBarrier`, `Semaphore`等实用组件。它们可以帮助开发者更加灵活地管理复杂的多线程场景下的协作逻辑。 例如,在某些情况下可能需要等待一组线程完成特定的任务之后再继续执行后续的操作;此时就可以考虑应用`CountDownLatch`: ```java import java.util.concurrent.CountDownLatch; class Worker extends Thread{ private CountDownLatch latch; public Worker(CountDownLatch latch){ this.latch=latch; } @Override public void run(){ try { // 执行任务... Thread.sleep((long)(Math.random()*10)); // 表示当前工作已完成 latch.countDown(); } catch (InterruptedException e){} } } // 主线程创建计数器,并启动子线程们 final int N=5; CountDownLatch cdl=new CountDownLatch(N); for(int i=0;i<N;++i) new Worker(cdl).start(); try { // 等待所有子线程结束 cdl.await(); } catch (InterruptedException ignored){} System.out.println("All threads have finished."); ``` #### 三、异步通信模式的应用实践 当涉及到不同模块之间的解耦合设计时,“发布/订阅”模型是一种非常有效的解决方案之一。这里提到了一个简单的观察者接口定义[^2],它允许主体通知其注册过的监听者有关状态变化的信息。 ```java public interface Observer { void update(String message); } ``` 基于此基础之上构建完整的事件驱动架构,则可以使系统的各个部分保持松散联系的同时又能高效互动。 #### 四、数据库连接池配置优化建议 对于频繁读写的Web应用程序而言,合理设置JDBC连接参数至关重要。下面给出了一组典型的MySQL数据库URL及其用户名密码组合[^3]: ```properties jdbc.url=jdbc:mysql://localhost:3306/day06?useSSL=false&serverTimezone=UTC jdbc.user=root jdbc.password=root ``` 需要注意的是实际项目部署过程中应当遵循最小权限原则分配账户角色,并且尽可能启用加密传输选项以保障安全性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值